16. データモデル(ER図)

ここまでのページは「仕事の流れ」を追ってきました。このページはその裏側にあるデータのつながりを図にしたものです。まず業務のことばで描いた概念ERで全体像をつかみ、次に実際のテーブル名・項目名で描いた実ERでプログラムを読むための足場を作る、という二段構えになっています。

このページの要点
  • 概念ERは業務のことばで描いた簡略版、実ERは実テーブル名で描いた正確版。用途が違うので両方載せています。
  • AS/400には外部キー制約が1つもありません。図の線は「プログラムがそう使っている」という意味であって、データベースが正しさを保証しているわけではありません。移行後のPostgreSQLでも、この性質は意図的にそのまま残してあります。
  • 品番には「これが正」という単一のテーブルがありません。ある25桁の品番が完成品か部品かは、jhmpを引いて無ければbhmpを引く、という読むたびの探索で決まります。
  • 単価はjudp(伝票別)>prmp(単価マスタ)>jhmp(受注品番マスタ)の優先順位。新人が必ず一度つまずくところです。
  • 構成(BOM)は自己参照です。ある行の「子」が別の行では「親」になります。ただし実際のプログラムは1段しか展開しません

1. 概念ER① お金をもらう流れ(受注→出荷→請求)

板倉製作所の業務は大きく2つの流れに分かれます(0. 全体像参照)。まず、お客様からお金をもらう側の流れです。

erDiagram
  TOKUISAKI["得意先"] { }
  JUCHU["受注"] { }
  HINBAN["受注品番"] { }
  TANKA["単価履歴"] { }
  KOSEI["構成(BOM)"] { }
  BUHIN["部品品番"] { }
  SHIJI["製造指示・部品展開"] { }
  SHUKKA["出荷"] { }
  SEIKYU["請求"] { }
  SHISAKU["試作"] { }

  TOKUISAKI ||--o{ JUCHU   : "注文を出す"
  JUCHU     }o--|| HINBAN  : "何を作るか"
  HINBAN    ||--o{ TANKA   : "品番ごとの価格"
  TOKUISAKI ||--o{ TANKA   : "客先ごとの価格"
  HINBAN    ||--o{ KOSEI   : "部品に分解する"
  KOSEI     }o--|| BUHIN   : "使う部品"
  BUHIN     ||--o{ KOSEI   : "部品自身も分解されうる"
  HINBAN    ||--o{ SHISAKU : "量産前の試し作り"
  JUCHU     ||--o{ SHIJI   : "作れと指示する"
  SHIJI     ||--o{ SHUKKA  : "できたら出す"
  SHUKKA    }o--|| SEIKYU  : "締日で束ねる"
  TOKUISAKI ||--o{ SEIKYU  : "月次でまとめて請求"

図が画面に収まらないときは、枠の中を横にスクロールしてください(縮小すると項目名が読めなくなるため、原寸で描いています)。

読み方はシンプルで、得意先が注文を出す(受注)→ 何を作るかが決まる(受注品番)→ それを部品に分解する(構成)→ 作れと指示する(製造指示)→ できたら出す(出荷)→ 締日でまとめて請求する、という一本道です。この一本道が業務の背骨で、他はすべてその周りに付く枝です。

この図で新人がつまずく3点
  • 「単価履歴」に線が2本入っている — 価格は「品番ごと」ではなく「得意先×品番×適用日」で決まります。同じ品番でも客先が違えば値段が違い、さらに時期によって改定されます。
  • 「部品品番」から「構成」へ線が戻っている — 部品自身がさらに部品に分解されることがあります(サブアッシー)。実データでも子品番の46.5%が別の行では親になっています。
  • 「試作」が受注品番からぶら下がっている — 試作は量産(号口)とは別ラインで、試作№で追跡します。同じ品番でも試作と号口はデータ上ほぼ別世界です。

2. 概念ER② お金をはらう流れ(発注→支払)と金型

もう一方は、外注先・仕入先へお金を払う側の流れです。①と対称に見えますが、実際には対称ではありません——そこが要注意ポイントです。

erDiagram
  BUHIN["部品品番(再掲)"] { }
  SHIIRESAKI["仕入先"] { }
  HACHU["発注(外注)"] { }
  UKEIRE["受入"] { }
  SHIHARAI["支払"] { }
  GINKO["銀行"] { }
  TOKUISAKI["得意先(再掲)"] { }
  HINBAN["受注品番(再掲)"] { }
  KANAGATA["金型"] { }
  KOKI["工機作業指示"] { }

  SHIIRESAKI ||--o{ HACHU    : "外に注文する"
  BUHIN      ||--o{ HACHU    : "外注する部品"
  HACHU      ||--|| UKEIRE   : "同じ1行に受入を追記"
  SHIIRESAKI ||--o{ SHIHARAI : "月次で支払う(発注とは別に手入力)"
  SHIIRESAKI }o--|| GINKO    : "振込先"
  TOKUISAKI  ||--o{ KANAGATA : "客先の資産"
  KANAGATA   }o--o{ HINBAN   : "1つの型を複数品番で共用"
  KANAGATA   ||--o{ KOKI     : "型・治具を作る・直す"

図が画面に収まらないときは、枠の中を横にスクロールしてください(縮小すると項目名が読めなくなるため、原寸で描いています)。

「発注→受入→支払」は一本の流れではありません
  • 発注と受入は同じ1行です。別テーブルではなく、発注時に書いたsfdpの行へ、受入時に受入項目を追記します。だから「発注はあるが受入がまだ」の行は、受入項目が空のまま残ります。
  • 支払は発注から自動計算されません。shdpは経理が手で入力する月次の買掛台帳で、sfdpとはプログラム上つながっていません。つまり仕入先の下に「発注」と「支払」という独立した2人の子がいる形で、パイプラインではありません。突合は人がやっています。
  • 金型は得意先の資産です。板倉の持ち物ではないため減価償却の概念がありません。しかも1つの型を複数の受注品番で共用することがあり、ここは数少ない明確な多対多です。

3. 実ER 背骨になる15テーブル

ここからは実際のテーブル名・項目名です。システム全体には166テーブルありますが、この図は業務の背骨にあたる15本に絞っています。残りをどう扱ったかは6章を見てください。

erDiagram
  tkmp["tkmp 得意先マスタ"] {
    char tkcdtk PK "得意先コード"
    char tknmrj    "得意先名"
  }
  tk1p["tk1p 得意先マスター従属1"] {
    char t1cdtk PK "得意先コード"
    num  t1dysm    "締日 SIMEBI"
  }
  judp["judp 受注データ"] {
    num  junoju PK "受注№"
    char jucdtk FK "得意先コード"
    char juhnbj FK "受注品番"
    num  junohi    "納入日"
    num  jutank    "伝票別単価(最優先)"
  }
  jhmp["jhmp 受注品番マスタ"] {
    char jhbnhn PK "受注品番"
    char jhcdtk FK "得意先コード"
    num  jhktan    "決定単価(旧・形骸化)"
  }
  prmp["prmp 製品単価マスタ"] {
    char prcdtk PK "得意先コード"
    char prbnhn PK "品番"
    num  prdyty PK "適用日"
    num  prtank    "単価(価格のSSOT)"
  }
  ksmp["ksmp 構成マスタ(BOM)"] {
    char kshnbj PK "親=受注品番"
    char kscdng PK "内外コード"
    char kshnbb PK "子=部品品番"
    num  kskssu    "構成数"
  }
  bhmp["bhmp 部品品番マスタ"] {
    char bhhnbb PK "部品品番"
    num  bhrtsz    "標準ロットサイズ"
  }
  bjdp["bjdp 部品状況ファイル"] {
    num  bjnojt PK "受注№×100+枝番"
    char bjhnbb FK "展開された部品品番"
  }
  sjdp["sjdp 製造指示データ"] {
    num  sjnojt PK "製造指示№"
    char sjhnbb FK "部品品番"
  }
  simp["simp 仕入先マスタ"] {
    char sicdsi PK "仕入先コード"
    char sikbwk    "E=使用停止(論理削除)"
  }
  sfdp["sfdp 仕入(発注+受入が1行)"] {
    char sfcdsi FK "仕入先コード"
    char sfhnbo FK "注文品番"
    num  sfnosu    "受入数(受入時に追記)"
  }
  shdp["shdp 支払費用データ"] {
    char shcdss PK "仕入先コード"
    num  shdyhs PK "発生年月"
  }
  mdmp["mdmp 金型管理マスタ"] {
    num  mdnorb PK "年度連番"
    char mdcdtk FK "得意先コード"
    char mdnohn FK "品番(緩い参照)"
  }

  tkmp ||--|| tk1p : "締日ほか請求条件"
  tkmp ||--o{ judp : "jucdtk"
  tkmp ||--o{ jhmp : "jhcdtk"
  tkmp ||--o{ prmp : "prcdtk"
  tkmp ||--o{ mdmp : "mdcdtk"
  judp }o--|| jhmp : "juhnbj = jhbnhn"
  jhmp ||--o{ prmp : "jhbnhn = prbnhn"
  jhmp ||--o{ ksmp : "jhbnhn = kshnbj(親側)"
  ksmp }o--|| bhmp : "kshnbb = bhhnbb(子側)"
  bhmp ||--o{ ksmp : "子が別の行では親になる=自己参照"
  judp ||--o{ bjdp : "単段展開(bjnojt=受注№×100)"
  bjdp }o--|| ksmp : "展開元の構成行"
  judp ||--o{ sjdp : "製造指示を起こす"
  simp ||--o{ sfdp : "sfcdsi"
  simp ||--o{ shdp : "shcdss(手入力・sfdpとは独立)"
  sfdp }o--|| bhmp : "sfhnbo(緩い参照)"
  mdmp }o--o{ jhmp : "mjmp経由で多対多"

図が画面に収まらないときは、枠の中を横にスクロールしてください(縮小すると項目名が読めなくなるため、原寸で描いています)。

3.1 この図を見るときの注意

4. 図の読み方(記号の意味)

ER図の線の両端についている記号(鳥の足=crow's foot)は「1件か、複数件か」を表します。

記号読み方この資料での例
||ちょうど1件受注1行に対して受注品番は必ず1つ
o{0件以上(複数ありうる)1得意先に受注は何件でも
||--o{1対多tkmp1件 ← judp複数
}o--o{多対多金型と受注品番(1つの型を複数品番で共用)
--(実線)その親が無いと子が成り立たない受注が無ければ部品展開も無い
..(破線)参照するだけ(独立して存在できる)コード表の引き当てなど

ただし前述のとおり、この「必ず1件」はDBが保証しているものではありません。あくまで「業務上そうなっているはず」「プログラムがそう作っているはず」であって、実データには親のいない子が混じりえます。実際、smmp.smhnjuのように名前はFKに見えるのに実データでは0.7%(4,442行中30行)しか結合しない項目も見つかっています(6章参照)。

5. 主要な関係とその根拠

図の線は推測ではなく、論理ファイルのアクセスパス(AS/400が実際に持っているインデックス)、RPGソースのCHAIN/SETLL、または移行版TypeScriptの実際のJOINで裏付けています。主要なものを挙げます。

関係業務上の意味根拠
tkmp.tkcdtkjudp.jucdtk得意先が受注を出す論理ファイル JUDL02/03/08 ほかが jucdtk をキー先頭に持つ (analysis/out/lf_indexes.sql:63,67,79)
tkmp.tkcdtktk1p.t1cdtk締日など請求条件の従属レコード(1対1)SELECT t1d101,t1d301 FROM tk1p WHERE t1cdtk=$1 (pilot/app/src/menus1/invoices.ts:262)
judp.juhnbjjhmp.jhbnhn受注が指す受注品番JOIN jhmp j ON j.jhbnhn = k.kshnbj (pilot/app/src/menuk1/bomInquiryChildParent.ts:39)
jhmp.jhbnhnksmp.kshnbjbhmp.bhhnbb受注品番を部品へ分解する(BOM本体)LEFT JOIN bhmp b ON b.bhhnbb = k.kshnbb (pilot/app/src/menuk1/bomInquiryParentChild.ts:44)
ksmp の自己参照子部品がさらに分解される(サブアッシー)実データで子品番9,870種のうち4,585種(46.5%)が別行の親でもある (engineering.html 実データスナップショット)
逆引き KSML1(子→親)「この部品はどの製品で使われているか」DDS実物で PFILE(KSMP) / キー KSHNBBKSHNBJ を確認 (source/as400-admin/transport-tooling/ITLIBS/KSML1.txt、hearing.html B8)
judpbjdp受注を部品へ展開して製造状況を追うSELECT ... FROM ksmp WHERE kshnbj = $1 を1回だけ実行(再帰なし) (pilot/app/src/menuj1/bomExplosion.ts:277-280)
単価 judp > prmp > jhmp伝票別単価が最優先、次に単価マスタ、最後に受注品番マスタ翻訳仕様 PBTANKR「受注データの単価が受注品番マスターの単価よりも優先するため」(estimate.html §単価の決まり方)
simp.sicdsisfdp.sfcdsi仕入先へ外注を発注する論理ファイル SFDL1/2/4 (lf_indexes.sql:202,205,208)pilot/app/src/domain/receive.ts:65
simp.sicdsishdp.shcdss月次の買掛。sfdpとは独立(手入力)FROM shdp WHERE shcdss=... (pilot/app/src/menur1/paymentInquiry.ts:60)、自動連携するコードは存在しない
mdmpjhmp(多対多)1つの金型を複数の受注品番で共用するITMD10R が金型×親品番の組を mjmp へ実体化 (pilot/app/migrations/018_menuz1.sql:22-28)

6. この図に載せなかったもの

166テーブルのうち図に描いたのは15本です。省いたものと、その理由を正直に挙げておきます。「図に無い=存在しない」ではありません。

省いたもの本数理由
judp*bjdp* のExcel連携ステージング381つの下流Excelマクロにつき1本ある使い捨ての置き場。実行のたびに全消去して書き直すだけで、業務概念としての独立した意味を持ちません。行数や日付から実機の稼働状況を推測してはいけない点は1. マスタとコード体系の「鉄則」を参照。
nmmp 名称マスタ(汎用コードブック)1内外コード・号口区分・担当班など、ほぼ全テーブルが参照する横断的なコード表。線を引くと図中のすべての箱につながってしまい可読性を落とすため省略しました。
squpsqdp 権限2業務データではなく、全プログラムの入口に立つ実行許可の仕組み。11. 権限と部署責任マップを参照。
ktmp 構成ツリーマスタ1多段BOMを明示的に持とうとした一度きりの試みで、データは2014年10月で止まっています。現行のSSOTではないため、載せると誤解を招きます。
smmp 支給品マスタ(アイシン)1smhnjuが受注№への外部キーに見えますが、実データでは4,442行中30行(0.7%)しか結合しません。名前がFKに見えるだけの項目の実例として、あえて図から外し、ここに記録しておきます。
試作・工機・かんばん・出来高などの周辺テーブル約30いずれも品番/受注品番/得意先にぶら下がる枝葉。各ドメインのページ(6. 試作・工機・材料・金型5. 号口生産・組付)で個別に説明しています。
分かっていないこと
  • KSML4KSML1と同じキー順(子→親)で、2本ある理由が資料からは特定できていません。選択/除外条件が違う可能性が高いものの、lf_indexes.sqlには差分が現れていません。
  • kfdp(工機作業指示)は主キー宣言が無く、品番への参照も文字列比較のみで、論理ファイルにもJOINにも裏付けが見つかりません。図に描くだけの根拠がない状態です。
  • 多段BOMはデータとしては実在するのに、展開するコードが存在しません。現場が頭の中で補っているのか、Excel側で処理しているのかは未確認です。
板倉製作所 業務フロー資料 — 出典: AS/400 ソース・翻訳仕様・Excel/VBA 資産(itakura-sys リポジトリ) ← 15. 用語集 / 0. 全体像 →