TECHS の ER 解剖
— 製番から逆算する

TECHS は「受注(製番)を背骨にした台帳」であって、製品ツリーを持たない。この頁は fab-forward-pms の鏡像(1,265製番・11,073製造品目・239,262部品表明細)から逆算した、TECHS の実際のデータ構造と、そこに透けて見えるクライアントのメンタルモデルの読み取りである。開発者(Ryo)本人向けメモ。

読者:PMSを作っている開発者本人 実測日 2026-09-05 鏡像 create_pms_local 16ソース → 17テーブル

0導入

一文で: TECHS は「受注(製番)を背骨にした台帳」であって、製品ツリーを持たない。カタログも号機も製品マスタも無い。あるのは「いつ・誰の・何番目の仕事か」を刻んだ台帳と、その仕事にぶら下がる部品表だけである。

この頁の数字はすべて、fab-forward-pms の鏡像DB(TECHSの読み取り専用同期先)を2026-09-05に直接クエリした実測値と、客先の実スキーマレポート(60,543列・1,388オブジェクト、2026-09-03付)から来ている。確認済み は再クエリで確認した事実、推定 は構造的な状況証拠から読んだ解釈であることを明示する。両者を混同しないこと。

1製番の背骨(図1)

受注から品番まで、実際に辿れる経路は一本しかない:MstDeal(得意先/仕入先)→ TrnOrder(受注)→ TrnSeiban(製番)→ TrnProd(製造品目=ユニット)→ TrnBom(受注部品表)→ MstItemNo(品番)。この一本道の外側に、MstPartsCompose(マスタ部品表、品番の自己参照エッジ)がぶら下がり、TrnSOrderD/TrnAcceptD(発注・検収、いずれも品番キーで製番キーではない)と TrnInOutRec(入出庫)が側枝として品番から生えている。

製品/機種マスタ — 無い 号機/製造番号 — 無い 売価 — 無いに近い 受注 11/1,265・品目 11/11,073 MstPartsCompose (マスタ部品表) key: PartsComposeSeq 45,521エッジ (自己参照) 親ItemNo→子ItemNo MstDeal (得意先/仕入先) key: DealCd 305件 (仕入先75) TrnOrder (受注) key: OrderNo 1,265件 TrnSeiban (製番) key: Seiban 1,265件 TrnProd (製造品目=ユニット) key: ProdSeq(global) 11,073件 TrnBom (受注部品表) key: PartsSeq 239,262明細/10,750H MstItemNo (品番) key: ItemNo 6,619件 DealCd OrderNo 100% 1:1 Seiban 1:N ProdNo=UnitCode 100% ItemNo 製番⇄品番は TrnProd 経由でしか繋がらない/SeibanItemNo は全行 NULL TrnSOrderD (発注明細) key: SOrderDNo 22,131件 TrnAcceptD (検収明細) key: AcceptDSeq 22,005件 TrnInOutRec (入出庫) key: InOutRecSeq 2,264件(2026-07のみ) ItemNo経由(製番を経由しない) ✕ 発注→製番 0% (1/22,131) 実測ジョイン率が高い経路 存在しないマスタ(ゴースト) 実測0%に近い=繋がらない経路
図1:製番の背骨。実線=実測ジョイン、緑太字=ほぼ100%一致、破線グレー=存在しないマスタ、破線赤=実測でほぼ繋がらない経路。

読み取れること: 製番と品番のあいだに直接のリレーションは無い。TrnSeiban.SeibanItemNo は全行 NULL で、製番から品番へ辿る唯一の経路は TrnProd(この製番で作った各ユニットの品番)を経由するものだけである。発注・検収は品番キーで、製番付きの発注は 22,131行中 1行しかない — 原価をジョブ(製番)単位で見たいなら、発注データからは辿れず、受注部品表 (bom_lines) や検収明細を経由するしかない。

1-1. 全体像 — 16ソース/17テーブルの完全なER図(図2)

下図はネットワーク経由で Mermaid(CDN読み込み)を使って描画する。オフラインや読み込み失敗時は図1が要約として機能する。

erDiagram
  MstDeal["MstDeal (customers)"] {
    string DealCd PK
    string DealNm
    string role "得意先/仕入先どちらにもなる"
  }
  MstSupplier["MstSupplier (suppliers)"] {
    string SupplierCd PK "== DealCd, 75/75一致"
  }
  MstSupplierBuyUp["MstSupplierBuyUp (supplier_price_tiers)"] {
    int SupplierBuyUpSeq PK
    string SupplierCd FK
    string ItemNo FK
  }
  MstItemNo["MstItemNo (parts)"] {
    string ItemNo PK "bare, ItemNoRevは99.97%NULL"
    string LargeCategoryCd "Eのみ実値あり"
  }
  MstPartsCompose["MstPartsCompose (part_compositions)"] {
    int PartsComposeSeq PK
    string ParentItemNo FK
    string ChildItemNo FK
    int DataSrt
  }
  MstPerson["MstPerson (employees)"] {
    string PersonCd PK "4桁 or Exxxの2系統"
  }
  TrnSeiban["TrnSeiban (seiban)"] {
    string Seiban PK
    string OrderNo FK
    string SeibanItemNo "全行NULL"
  }
  TrnOrder["TrnOrder (orders)"] {
    string OrderNo PK
    string CustomerCd FK
    string ProjectName "自由記述,34機種に正規化可(81.6%)"
  }
  TrnProd["TrnProd (production_items)"] {
    int ProdSeq PK "グローバル surrogate"
    string Seiban FK
    string ProdNo "Seiban+-NN, 96.6%"
    string ProdItemNo FK "28%がparts外"
  }
  TrnSOrderH["TrnSOrderH (purchase_order_headers)"] {
    string SOrderSlipNo PK "10桁ゼロ埋め連番"
  }
  TrnSOrderD["TrnSOrderD (purchase_order_lines)"] {
    int SOrderDNo PK
    string SOrderSlipNo FK
    string ItemNo FK
    string Seiban "1/22,131のみ充填"
  }
  TrnAcceptH["TrnAcceptH (acceptance_headers)"] {
    string AcceptNo PK
  }
  TrnAcceptD["TrnAcceptD (acceptance_lines)"] {
    int AcceptDSeq PK
    string AcceptNo FK
    string ItemNo FK
  }
  TrnBom["TrnBom (bom_headers/bom_lines)"] {
    int PartsSeq PK "line"
    string OrderNo FK "headerキーの一部"
    string UnitCode FK "= ProdNo"
    string ItemNo FK
    string ParentPartsSeq "全行NULL"
  }
  TrnInOutRec["TrnInOutRec (inventory_movements)"] {
    int InOutRecSeq PK
    string ItemNo FK
    string MovementTypeCd "S0/S1,極性未確認"
  }
  ViwStock["ViwRptStockConditionListInfo (stock_balances)"] {
    string ItemNo FK
    string StockBaseCd
    string HouseCd
  }

  MstDeal ||--o{ TrnOrder : "DealCd 305to1265"
  MstDeal ||--o{ MstSupplier : "role projection 75/75"
  TrnOrder ||--|| TrnSeiban : "OrderNo 100% 1:1"
  TrnSeiban ||--o{ TrnProd : "Seiban 1265to11073"
  TrnProd ||--o{ TrnBom : "ProdNo=UnitCode headers100%"
  MstItemNo ||--o{ TrnBom : "ItemNo 明細参照"
  MstItemNo ||--o{ MstPartsCompose : "ParentItemNo 自己参照"
  MstItemNo ||--o{ MstPartsCompose : "ChildItemNo 自己参照"
  MstItemNo ||--o{ TrnSOrderD : "ItemNo Seibanは0%"
  TrnSOrderH ||--o{ TrnSOrderD : "SOrderSlipNo"
  MstItemNo ||--o{ TrnAcceptD : "ItemNo"
  TrnAcceptH ||--o{ TrnAcceptD : "AcceptNo"
  MstItemNo ||--o{ TrnInOutRec : "ItemNo 2026-07のみ"
  MstItemNo ||--o{ ViwStock : "ItemNo|StockBaseCd|HouseCd"
  MstSupplier ||--o{ MstSupplierBuyUp : "SupplierCd"
  MstItemNo ||--o{ MstSupplierBuyUp : "ItemNo"

2ID の文法(図3)

各識別子を実例で分解する。番号は分類コードではなく時系列の台帳番号(月×連番)であること、リビジョンや加工段階までも番号の中に埋め込む文化であることが読み取れる。

識別子実例分解
製番
推定 接頭辞の意味
BD2212-07 BD 接頭辞(内部分類、DM/SS=自社案件) + 2212 YYMM(採番月。due_date一致61.4%) + -07 月内で全顧客共有の連番(95.1%が密。歯抜けはTrnSeibanDelでの物理削除)
製造No
確認済み
BD2212-07-03 BD2212-07 製番 + -03 連番(1..k、96.6%が密)。 残り3.4%は手打ち接尾辞 -SOJI-KIRIKAE-CR01
品番
推定 文字の意味
5131F719-A 5131 4桁ファミリー(5111/5131/5141/5151のみ存在) + F 文字(K/M≈組立、親84-93%/E/F≈個別加工部品、E67%が葉/Bは小規模) + 719 3桁 + -A リビジョン(A→N、頻度が単調減少 確認済み
同一品番の加工段階違い: IT5141E171-A(素材)/TS5141E172-A(加工中)
発注伝票
推定 グローバル連番
0000023477 10桁ゼロ埋め の全期間通し連番(00000000010000023477
得意先/仕入先 00000000 / 30000123 8桁00000000=自社(内部・実証案件用)。仕入先は同じMstDealのロール投影(75/75が得意先側にも実在)
社員 1001 / E004 4桁数字(28/33=85%)と Exxx英数字(5/33=15%)が併存 確認済み。別々の登録経路の名残 推定
彼らのメンタルモデルの読み取り 番号は分類コードではなく時系列の台帳番号(月×連番)である。品番のリビジョンは専用列(ItemNoRev、99.97%がNULL)ではなく番号の中に埋め込む。加工段階すら品番の接頭で表す(IT/TS)。手打ちで接尾を付ける文化がある(_ADVICS_KIRIKAE-SUETUKE)。そして「製品」の名前は独立したマスタではなく orders.project_name の自由記述に住んでいる — それでも34機種に正規化でき(1,032/1,265件=81.6%)、そのうち上位10機種で全体の79.4%をカバーする(詳細は fab-forward-pms の lineup-inference-2026-09-05.md)。

3自然キーとサロゲート

見出し/マスタ系のテーブルは業務コードをそのままキーにし、明細/エッジ系のテーブルは全て独自の *Seq 整数サロゲートを持つ。この線引きは16ソース全てで一貫している。

PMSテーブル(TECHS源)実際のキー種別
parts(MstItemNoItemNo自然キー
customers/suppliers(MstDealDealCd/SupplierCd自然キー
supplier_price_tiers(MstSupplierBuyUpSupplierBuyUpSeq独自サロゲート
part_compositions(MstPartsComposePartsComposeSeq独自サロゲート((Parent,Child)は非一意、207重複)
employees(MstPersonPersonCd自然キー
seiban(TrnSeibanSeiban自然キー(別途SeibanNoは無い)
orders(TrnOrderOrderNo自然キー
production_items(TrnProdProdSeq独自サロゲート(グローバル、製番単位ではない)
purchase_order_headers(TrnSOrderHSOrderSlipNo自然キー(10桁ゼロ埋め連番)
purchase_order_lines(TrnSOrderDSOrderDNo独自サロゲート
acceptance_headers(TrnAcceptHAcceptNo自然キー
acceptance_lines(TrnAcceptDAcceptDSeq独自サロゲート
bom_lines(TrnBomPartsSeq独自サロゲート
inventory_movements(TrnInOutRecInOutRecSeq独自サロゲート
stock_balances(ViwRptStockConditionListInfoなし複合導出キー ItemNo|StockBaseCd|HouseCd

唯一の複合キー実体(stock_balances)は、同時に唯一「確定スキーマ調査の16オブジェクトから外れている」実体でもある — ソースビュー(ViwRptStockConditionListInfo)が別のwave3設計ドキュメントからしか裏付けられていない。

TmStamp ウォッチマークと物理削除の罠 確定スキーマの16基底テーブル全てが TmStamp timestamp(8) NOT NULL(SQL Serverのrowversion)を持ち、stock_balancesを除く全実体はこれで増分同期する(何も変わらなければ0行更新)。だが TmStampは物理削除を検知できない — このTECHS環境はDelFlgを立てるだけでなく行を物理削除するため、TrnOrderDel(52行)・TrnSeibanDel(52行)・TrnProdDel(1,175行)という影テーブルが存在する。ウォッチマーク同期に加えて、この*Delテーブル(またはキー全件突合)による削除検知が必須。

4部品表は2つある

受注部品表(TrnBom)とマスタ部品表(MstPartsCompose)は、似て非なる別物である。

受注部品表 TrnBom(bom_lines)マスタ部品表 MstPartsCompose(part_compositions)
フラット。ParentPartsSeq は297,158行全件でNULL — 列は存在するがこの環境では使われていない木構造。ルートからの最大深さちょうど4(確認済み、再帰CTE)
件数239,262明細 / 10,750ヘッダ45,521エッジ(45,314種のユニークな親子対、207組が重複)
階層の代替表現ProdLevel≥2(13,139行)は同じ部品の加工段階違い(素材→加工中)であって独立部品ではないルート1,573種のうち852種(54%)は一度も発注されていない(=製造実績なし)
手打ちの分類AssyUnitNo=KONYU(購入,79.3%)/GAITYU(外注,18.2%)+タイプミス群(ZZKONYU, GAICYU, GATIYU…)parts_noは118種の値しかなく親ごとに非一意(位置バルーン番号の使い回しと推定)

両者の一致度: 実際に発注実績のある877品番について、受注BOM(実績)とマスタBOM(設計)を比較すると、Jaccard中央値 0.961(200組サンプル、fab-forward-pmsのlineup-inference-2026-09-05.md実測)— 「設計通りに作られている」という点では両者はよく一致する。一方でマスタBOM側の54%は一度も実発注されておらず、「設計されたが売れたことのない構成」がマスタには混じっている。

5無いもの

無いもの実測PMS側の対応
製品/機種マスタorders.project_nameの自由記述のみ(949種、874種が一度きり)/lineup 推定表示。正規化ルールで34機種に集約(81.6%、上位10機種で79.4%カバー)
号機(アセット単位のシリアル)production_itemsはジョブ内ユニット番号(ProdNo)を持つが、機体としての通し番号は無い/serials を新設し、推定管理として別枠で持つ方針
売価受注の売価 orders.amount が非ゼロなのは 1,265件中 11件、製造品目の production_items.unit_price/amount は 11,073行中 11行(0.1%)。原価に対して桁が合わないため利益率の分母に使えない売価未登録として画面に明示表示(黙って0円扱いにしない)
実績完了日CompleteFinishYmd(complete_finish_date)は0%(全行空欄)。出荷実績日(ship_finish_date)は90.4%充填完成実績日欄は空欄のまま表示し、出荷実績日を実績の代理指標として扱う
製番付きの発注purchase_order_lines.seiban充填は 1/22,131行のみジョブ単位の原価突合は発注ではなくbom_lines/acceptance_lines経由で行う
継続的な入出庫inventory_movementsは2,264件、期間は2026-07-07〜07-31のみで以降ゼロ鏡像側のバグではないと確認済み。客先への未解決質問として保留(§7)

6ビューが本当の読み取り面

客先の実スキーマは1,388オブジェクト中1,019がビューViw*接頭)。base tableだけを見ていると、この環境の実際の姿を半分以上見落とす。

「ERDの名前はERDについての証拠であって、客先についての証拠ではない」 TrnOrderH/TrnOrderD/MstItemNoInfo/TrnInvStock/TrnBuyAcceptが見つからなかったことから、一時「受注管理/検収モジュールが未導入」と誤結論し、製造・購買・原価集計の大規模な再設計に踏み出しかけた。実際にはこの環境は別名(単一126列のTrnOrderTrnProdTrnAcceptH/TrnAcceptD)でデータを持っており、全て稼働していた。

7未確定事項

鏡像データだけでは決着しなかった論点。推測で埋めていない。