Oracle DatabaseでエンジンBOMをProperty Graph化(基礎編)
この記事を試す前に、Autonomous Database をまだ作成していない場合は、先に次の Oracle Japan OCI チュートリアルを参照してください。
ADBインスタンスを作成してみよう | OCIチュートリアルこの記事では、ADB上でSQLを実行し、Property Graph を作成し、最後に APEX で可視化する 流れを前提にしています。
engine_bom_oracle.sql を題材に、テーブル設計 → データ投入 → Graph化 → Graph Query → APEX可視化 の順で、解説します。
SQLファイルそのものを読み解く記事 として整理しました。
Graph Query と可視化 まで扱います。
はじめに
BOM(部品表)は、表で管理していても本質は 「親品目と子品目のつながり」 です。
つまり、RDBで正しく設計したうえで、必要な場面だけ Graph として扱えるようにしておくと、次のようなことが一気に分かりやすくなります。
- 多階層の部品展開
- 改訂付きBOMの追跡
- 影響範囲の把握
- 可視化ツールでの直感的な理解
今回の engine_bom_oracle.sql は、
- 品目マスタ
- BOMヘッダ
- BOM明細
- 業務意味を埋め込んだ
COMMENT -
MERGEベースの投入処理 CREATE PROPERTY GRAPH
までをひとまとめにした、SQLです。
engine_bom_oracle.sql
-- ============================================================================
-- 1) DDL: 製造業向けアセンブリBOM
-- ============================================================================
CREATE TABLE mfg_item (
item_id NUMBER GENERATED ALWAYS AS IDENTITY,
item_code VARCHAR2(50 CHAR) NOT NULL,
item_name VARCHAR2(200 CHAR) NOT NULL,
item_type VARCHAR2(30 CHAR) NOT NULL,
procurement_type VARCHAR2(20 CHAR) NOT NULL,
uom_code VARCHAR2(10 CHAR) NOT NULL,
revision_code VARCHAR2(20 CHAR) DEFAULT 'A' NOT NULL,
lifecycle_status VARCHAR2(20 CHAR) DEFAULT 'ACTIVE' NOT NULL,
drawing_no VARCHAR2(100 CHAR),
description_text VARCHAR2(1000 CHAR),
CONSTRAINT pk_mfg_item PRIMARY KEY (item_id),
CONSTRAINT uk_mfg_item_code UNIQUE (item_code),
CONSTRAINT ck_mfg_item_type CHECK (item_type IN ('FG','ASM','PART','RM','CONSUMABLE')),
CONSTRAINT ck_mfg_item_proc CHECK (procurement_type IN ('MAKE','BUY','BOTH')),
CONSTRAINT ck_mfg_item_lifecycle CHECK (lifecycle_status IN ('ACTIVE','INACTIVE','OBSOLETE'))
);
CREATE TABLE asm_bom_header (
bom_id NUMBER GENERATED ALWAYS AS IDENTITY,
bom_code VARCHAR2(50 CHAR) NOT NULL,
assembly_item_id NUMBER NOT NULL,
bom_name VARCHAR2(200 CHAR) NOT NULL,
bom_revision VARCHAR2(20 CHAR) NOT NULL,
alternate_bom_code VARCHAR2(20 CHAR) DEFAULT 'PRIMARY' NOT NULL,
effectivity_start_date DATE NOT NULL,
effectivity_end_date DATE,
status_code VARCHAR2(20 CHAR) DEFAULT 'RELEASED' NOT NULL,
description_text VARCHAR2(1000 CHAR),
CONSTRAINT pk_asm_bom_header PRIMARY KEY (bom_id),
CONSTRAINT uk_asm_bom_header_code UNIQUE (bom_code),
CONSTRAINT uk_asm_bom_header_item_rev_alt UNIQUE (assembly_item_id, bom_revision, alternate_bom_code),
CONSTRAINT fk_asm_bom_header_item FOREIGN KEY (assembly_item_id)
REFERENCES mfg_item (item_id),
CONSTRAINT ck_asm_bom_header_status CHECK (status_code IN ('DRAFT','RELEASED','OBSOLETE')),
CONSTRAINT ck_asm_bom_header_effective CHECK (
effectivity_end_date IS NULL OR effectivity_end_date >= effectivity_start_date
)
);
CREATE TABLE asm_bom_component (
bom_line_id NUMBER GENERATED ALWAYS AS IDENTITY,
bom_id NUMBER NOT NULL,
parent_item_id NUMBER NOT NULL,
component_item_id NUMBER NOT NULL,
line_no NUMBER(6) NOT NULL,
find_no VARCHAR2(30 CHAR),
component_qty NUMBER(18,6) NOT NULL,
component_uom VARCHAR2(10 CHAR) NOT NULL,
scrap_factor NUMBER(9,6) DEFAULT 0 NOT NULL,
yield_factor NUMBER(9,6) DEFAULT 1 NOT NULL,
operation_seq NUMBER(6),
supply_type VARCHAR2(20 CHAR) DEFAULT 'PULL' NOT NULL,
optional_yn CHAR(1) DEFAULT 'N' NOT NULL,
remarks VARCHAR2(1000 CHAR),
CONSTRAINT pk_asm_bom_component PRIMARY KEY (bom_line_id),
CONSTRAINT uk_asm_bom_component_bom_line UNIQUE (bom_id, line_no),
CONSTRAINT fk_asm_bom_component_bom FOREIGN KEY (bom_id)
REFERENCES asm_bom_header (bom_id) ON DELETE CASCADE,
CONSTRAINT fk_asm_bom_component_parent FOREIGN KEY (parent_item_id)
REFERENCES mfg_item (item_id),
CONSTRAINT fk_asm_bom_component_comp FOREIGN KEY (component_item_id)
REFERENCES mfg_item (item_id),
CONSTRAINT ck_asm_bom_component_qty CHECK (component_qty > 0),
CONSTRAINT ck_asm_bom_component_scrap CHECK (scrap_factor >= 0 AND scrap_factor < 1),
CONSTRAINT ck_asm_bom_component_yield CHECK (yield_factor > 0 AND yield_factor <= 1),
CONSTRAINT ck_asm_bom_component_supply CHECK (supply_type IN ('PUSH','PULL','KIT')),
CONSTRAINT ck_asm_bom_component_optional CHECK (optional_yn IN ('Y','N')),
CONSTRAINT ck_asm_bom_component_no_self CHECK (parent_item_id <> component_item_id)
);
CREATE INDEX ix_asm_bom_header_item ON asm_bom_header (assembly_item_id);
CREATE INDEX ix_asm_bom_component_bom ON asm_bom_component (bom_id);
CREATE INDEX ix_asm_bom_component_parent ON asm_bom_component (parent_item_id);
CREATE INDEX ix_asm_bom_component_comp ON asm_bom_component (component_item_id);
COMMENT ON TABLE mfg_item IS q'~{"表示名":"製造品目マスタ","説明":"BOMで参照される完成品、中間アセンブリ、購入部品、原材料、消耗品を一意に管理する中核マスタ。1行が1品目であり、BOMヘッダの assembly_item_id と BOM明細の parent_item_id / component_item_id の参照先になる。AIは item_type で構成上の役割、procurement_type で調達方式、lifecycle_status で使用可否を解釈する。"}~';
COMMENT ON TABLE asm_bom_header IS q'~{"表示名":"アセンブリBOMヘッダ","説明":"1つの親品目に対する1改訂・1代替BOMの定義を表すテーブル。どの品目に対するBOMか、どの改訂か、いつ有効か、利用状態が何かを管理する。多階層BOMではサブアセンブリごとにもヘッダを持たせ、再帰展開や改訂管理を安定して行う。"}~';
COMMENT ON TABLE asm_bom_component IS q'~{"表示名":"アセンブリBOM明細","説明":"BOMヘッダ配下の1明細行を表すテーブル。親品目1単位あたりに必要な子品目、所要数量、工程、歩留、支給方式などを保持する。parent_item_id は asm_bom_header.assembly_item_id を冗長保持した列で、再帰SQLとグラフ変換の起点キーとして使う。"}~';
COMMENT ON COLUMN mfg_item.item_id IS q'~{"表示名":"品目ID","説明":"システム内部で使用するサロゲート主キー。人間可読な品目コードとは分離し、名称変更やコード体系変更が起きても参照整合性を維持する。"}~';
COMMENT ON COLUMN mfg_item.item_code IS q'~{"表示名":"品目コード","説明":"ERP/MES/PLMなど複数システムで同一品目を突合するための業務キー。接頭辞や語尾から製品群や用途を推測できるが、厳密な一意性は item_id で担保する。"}~';
COMMENT ON COLUMN mfg_item.item_name IS q'~{"表示名":"品目名称","説明":"画面表示、帳票、検索に使う日本語の代表名称。AIが自然言語で品目意味を理解する際の主要手掛かりになる。"}~';
COMMENT ON COLUMN mfg_item.item_type IS q'~{"表示名":"品目区分","説明":"完成品(FG)、中間アセンブリ(ASM)、部品(PART)、原材料(RM)、消耗品(CONSUMABLE)などの役割区分。BOM展開時にノードの意味を判定する基礎属性。"}~';
COMMENT ON COLUMN mfg_item.procurement_type IS q'~{"表示名":"調達区分","説明":"自社製造(MAKE)、購入(BUY)、両方可(BOTH)を示す属性。生産計画、原価計算、代替調達判断で重要な分類。"}~';
COMMENT ON COLUMN mfg_item.uom_code IS q'~{"表示名":"在庫単位","説明":"品目を在庫・購買・生産で数える基本単位。通常は EA、SET、KG などを保持し、BOM明細の component_uom の基準にもなる。"}~';
COMMENT ON COLUMN mfg_item.revision_code IS q'~{"表示名":"品目改訂","説明":"品目自体の設計改訂や仕様版数。BOM改訂とは別概念であり、部品単体の変更履歴を識別する。"}~';
COMMENT ON COLUMN mfg_item.lifecycle_status IS q'~{"表示名":"ライフサイクル状態","説明":"ACTIVE、INACTIVE、OBSOLETE などの状態。BOMで新規採用できるか、既存保守のみか、使用停止かを判断する。"}~';
COMMENT ON COLUMN mfg_item.drawing_no IS q'~{"表示名":"図面番号","説明":"図面・CAD・PLM上の正式な参照番号。存在する場合は設計文書と品目を結び付ける主キー候補として扱う。"}~';
COMMENT ON COLUMN mfg_item.description_text IS q'~{"表示名":"品目説明","説明":"自由記述の仕様補足、用途、材質、注意事項を保持する説明文。AIによる検索・分類・要約の入力情報として活用できる。"}~';
COMMENT ON COLUMN asm_bom_header.bom_id IS q'~{"表示名":"BOM ID","説明":"システム内部で使用するBOMヘッダのサロゲート主キー。BOM明細が参照する親キーであり、改訂単位の構成を一意に識別する。"}~';
COMMENT ON COLUMN asm_bom_header.bom_code IS q'~{"表示名":"BOMコード","説明":"人間可読なBOM識別子。外部システムや帳票で特定のBOM改訂を識別する業務キーとして利用する。"}~';
COMMENT ON COLUMN asm_bom_header.assembly_item_id IS q'~{"表示名":"親品目ID","説明":"このBOMが属する親アセンブリ品目の ID。1つのBOMヘッダは必ず1つの親品目に対応し、サブアセンブリも同じ仕組みで表現する。"}~';
COMMENT ON COLUMN asm_bom_header.bom_name IS q'~{"表示名":"BOM名称","説明":"画面・帳票向けのBOM表示名。通常は親品目名に「BOM」を付けた業務名称を保持する。"}~';
COMMENT ON COLUMN asm_bom_header.bom_revision IS q'~{"表示名":"BOM改訂","説明":"構成表そのものの改訂番号。品目改訂と独立して管理でき、構成変更のみを追跡したい場合に重要となる。"}~';
COMMENT ON COLUMN asm_bom_header.alternate_bom_code IS q'~{"表示名":"代替BOM区分","説明":"PRIMARY、ALT1 などの代替構成識別子。仕向地差、工場差、代替工程差など複数BOMを並行管理するときに使う。"}~';
COMMENT ON COLUMN asm_bom_header.effectivity_start_date IS q'~{"表示名":"有効開始日","説明":"このBOM改訂を使用開始できる日付。製造日や計画日を基準に適用BOMを判定する際の下限となる。"}~';
COMMENT ON COLUMN asm_bom_header.effectivity_end_date IS q'~{"表示名":"有効終了日","説明":"このBOM改訂の使用終了日。NULL の場合は終了未定で継続有効と解釈する。"}~';
COMMENT ON COLUMN asm_bom_header.status_code IS q'~{"表示名":"BOM状態","説明":"DRAFT、RELEASED、OBSOLETE などの状態。生産で使用可能か、設計中か、廃止済みかを示す。"}~';
COMMENT ON COLUMN asm_bom_header.description_text IS q'~{"表示名":"BOM説明","説明":"当該BOM改訂の適用条件、変更理由、注意事項などを記述する自由文。AIが構成変更理由を理解する補足情報になる。"}~';
COMMENT ON COLUMN asm_bom_component.bom_line_id IS q'~{"表示名":"BOM明細ID","説明":"BOM明細行のサロゲート主キー。1行1構成関係を一意に識別し、グラフ変換時にはエッジキーとして利用できる。"}~';
COMMENT ON COLUMN asm_bom_component.bom_id IS q'~{"表示名":"BOM ID","説明":"どのBOMヘッダに属する明細かを示す外部キー。改訂、有効期間、代替BOM区分などの文脈を明細に結び付ける。"}~';
COMMENT ON COLUMN asm_bom_component.parent_item_id IS q'~{"表示名":"親品目ID","説明":"親アセンブリ品目のID。asm_bom_header.assembly_item_id を冗長保持した列で、再帰SQLとプロパティグラフの SOURCE KEY を単純化するために持つ。"}~';
COMMENT ON COLUMN asm_bom_component.component_item_id IS q'~{"表示名":"子品目ID","説明":"親品目にぶら下がる構成品目のID。購入部品、中間アセンブリ、原材料のいずれも取り得る。"}~';
COMMENT ON COLUMN asm_bom_component.line_no IS q'~{"表示名":"行番号","説明":"BOM内での表示順・論理順を表す番号。通常は 10,20,30 のように採番し、途中挿入をしやすくする。"}~';
COMMENT ON COLUMN asm_bom_component.find_no IS q'~{"表示名":"図示番号","説明":"組図のバルーン番号や部品表参照番号。図面上の部品位置とBOM行を対応付ける。"}~';
COMMENT ON COLUMN asm_bom_component.component_qty IS q'~{"表示名":"所要数量","説明":"親品目1単位あたりに必要な子品目数量。歩留やスクラップを加味する前の基準数量として扱う。"}~';
COMMENT ON COLUMN asm_bom_component.component_uom IS q'~{"表示名":"明細単位","説明":"当該BOM行で数量を解釈する単位。品目の在庫単位と一致することが多いが、必要に応じて異なる表現も許容する。"}~';
COMMENT ON COLUMN asm_bom_component.scrap_factor IS q'~{"表示名":"スクラップ率","説明":"組付け時の損耗・廃棄・切り捨てを見込む追加率。0.02 なら基準数量に対して 2% のロスを見込む。"}~';
COMMENT ON COLUMN asm_bom_component.yield_factor IS q'~{"表示名":"歩留係数","説明":"工程を通過して良品として残る比率。1 に近いほど損失が少なく、必要投入量計算の補正に使う。"}~';
COMMENT ON COLUMN asm_bom_component.operation_seq IS q'~{"表示名":"工程順序","説明":"ルーティングや組立工程のどの工程で投入・組付けするかを示す番号。工程別ピッキングや投入タイミング制御に使う。"}~';
COMMENT ON COLUMN asm_bom_component.supply_type IS q'~{"表示名":"支給方式","説明":"PUSH、PULL、KIT などの供給方式。工程への払い出し方法やバックフラッシュ処理を判定する。"}~';
COMMENT ON COLUMN asm_bom_component.optional_yn IS q'~{"表示名":"任意部品フラグ","説明":"Y の場合は選択品・オプション品・条件付き採用品を表す。N は必須構成品を表す。"}~';
COMMENT ON COLUMN asm_bom_component.remarks IS q'~{"表示名":"明細備考","説明":"組付け注意、代替条件、締付トルク参照など、明細単位の補足指示を記録する自由文。"}~';
DECLARE
FUNCTION get_item_id(p_item_code IN VARCHAR2) RETURN NUMBER IS
v_item_id mfg_item.item_id%TYPE;
BEGIN
SELECT item_id
INTO v_item_id
FROM mfg_item
WHERE item_code = p_item_code;
RETURN v_item_id;
END;
FUNCTION get_bom_id(p_bom_code IN VARCHAR2) RETURN NUMBER IS
v_bom_id asm_bom_header.bom_id%TYPE;
BEGIN
SELECT bom_id
INTO v_bom_id
FROM asm_bom_header
WHERE bom_code = p_bom_code;
RETURN v_bom_id;
END;
PROCEDURE upsert_item(
p_item_code IN VARCHAR2,
p_item_name IN VARCHAR2,
p_item_type IN VARCHAR2,
p_procurement_type IN VARCHAR2,
p_uom_code IN VARCHAR2 DEFAULT 'EA',
p_revision_code IN VARCHAR2 DEFAULT 'A',
p_lifecycle_status IN VARCHAR2 DEFAULT 'ACTIVE',
p_drawing_no IN VARCHAR2 DEFAULT NULL,
p_description_text IN VARCHAR2 DEFAULT NULL
) IS
BEGIN
MERGE INTO mfg_item t
USING (
SELECT p_item_code AS item_code,
p_item_name AS item_name,
p_item_type AS item_type,
p_procurement_type AS procurement_type,
p_uom_code AS uom_code,
p_revision_code AS revision_code,
p_lifecycle_status AS lifecycle_status,
p_drawing_no AS drawing_no,
COALESCE(
p_description_text,
p_item_name || '。2.0L直列4気筒ガソリンエンジンASSYのサンプルBOMで使用する品目。'
) AS description_text
FROM dual
) s
ON (t.item_code = s.item_code)
WHEN MATCHED THEN UPDATE
SET t.item_name = s.item_name,
t.item_type = s.item_type,
t.procurement_type = s.procurement_type,
t.uom_code = s.uom_code,
t.revision_code = s.revision_code,
t.lifecycle_status = s.lifecycle_status,
t.drawing_no = COALESCE(s.drawing_no, t.drawing_no),
t.description_text = COALESCE(s.description_text, t.description_text)
WHEN NOT MATCHED THEN INSERT (
item_code,
item_name,
item_type,
procurement_type,
uom_code,
revision_code,
lifecycle_status,
drawing_no,
description_text
) VALUES (
s.item_code,
s.item_name,
s.item_type,
s.procurement_type,
s.uom_code,
s.revision_code,
s.lifecycle_status,
s.drawing_no,
s.description_text
);
END;
PROCEDURE upsert_bom_header(
p_bom_code IN VARCHAR2,
p_assembly_item_code IN VARCHAR2,
p_bom_name IN VARCHAR2,
p_bom_revision IN VARCHAR2,
p_alternate_bom_code IN VARCHAR2,
p_effectivity_start_date IN DATE,
p_effectivity_end_date IN DATE DEFAULT NULL,
p_status_code IN VARCHAR2 DEFAULT 'RELEASED',
p_description_text IN VARCHAR2 DEFAULT NULL
) IS
v_assembly_item_id mfg_item.item_id%TYPE;
BEGIN
v_assembly_item_id := get_item_id(p_assembly_item_code);
MERGE INTO asm_bom_header t
USING (
SELECT p_bom_code AS bom_code,
v_assembly_item_id AS assembly_item_id,
p_bom_name AS bom_name,
p_bom_revision AS bom_revision,
p_alternate_bom_code AS alternate_bom_code,
p_effectivity_start_date AS effectivity_start_date,
p_effectivity_end_date AS effectivity_end_date,
p_status_code AS status_code,
COALESCE(
p_description_text,
p_bom_name || '。有効開始日=' || TO_CHAR(p_effectivity_start_date, 'YYYY-MM-DD')
) AS description_text
FROM dual
) s
ON (t.bom_code = s.bom_code)
WHEN MATCHED THEN UPDATE
SET t.assembly_item_id = s.assembly_item_id,
t.bom_name = s.bom_name,
t.bom_revision = s.bom_revision,
t.alternate_bom_code = s.alternate_bom_code,
t.effectivity_start_date = s.effectivity_start_date,
t.effectivity_end_date = s.effectivity_end_date,
t.status_code = s.status_code,
t.description_text = COALESCE(s.description_text, t.description_text)
WHEN NOT MATCHED THEN INSERT (
bom_code,
assembly_item_id,
bom_name,
bom_revision,
alternate_bom_code,
effectivity_start_date,
effectivity_end_date,
status_code,
description_text
) VALUES (
s.bom_code,
s.assembly_item_id,
s.bom_name,
s.bom_revision,
s.alternate_bom_code,
s.effectivity_start_date,
s.effectivity_end_date,
s.status_code,
s.description_text
);
END;
PROCEDURE upsert_bom_line(
p_bom_code IN VARCHAR2,
p_component_code IN VARCHAR2,
p_line_no IN NUMBER,
p_component_qty IN NUMBER,
p_component_uom IN VARCHAR2 DEFAULT 'EA',
p_operation_seq IN NUMBER DEFAULT NULL,
p_scrap_factor IN NUMBER DEFAULT 0,
p_yield_factor IN NUMBER DEFAULT 1,
p_supply_type IN VARCHAR2 DEFAULT 'PULL',
p_optional_yn IN CHAR DEFAULT 'N',
p_remarks IN VARCHAR2 DEFAULT NULL
) IS
v_bom_id asm_bom_header.bom_id%TYPE;
v_parent_item_id asm_bom_header.assembly_item_id%TYPE;
v_component_item_id mfg_item.item_id%TYPE;
BEGIN
v_bom_id := get_bom_id(p_bom_code);
v_component_item_id := get_item_id(p_component_code);
SELECT assembly_item_id
INTO v_parent_item_id
FROM asm_bom_header
WHERE bom_id = v_bom_id;
MERGE INTO asm_bom_component t
USING (
SELECT v_bom_id AS bom_id,
v_parent_item_id AS parent_item_id,
v_component_item_id AS component_item_id,
p_line_no AS line_no,
LPAD(p_line_no, 4, '0') AS find_no,
p_component_qty AS component_qty,
p_component_uom AS component_uom,
p_scrap_factor AS scrap_factor,
p_yield_factor AS yield_factor,
p_operation_seq AS operation_seq,
p_supply_type AS supply_type,
p_optional_yn AS optional_yn,
p_remarks AS remarks
FROM dual
) s
ON (t.bom_id = s.bom_id AND t.line_no = s.line_no)
WHEN MATCHED THEN UPDATE
SET t.parent_item_id = s.parent_item_id,
t.component_item_id = s.component_item_id,
t.find_no = s.find_no,
t.component_qty = s.component_qty,
t.component_uom = s.component_uom,
t.scrap_factor = s.scrap_factor,
t.yield_factor = s.yield_factor,
t.operation_seq = s.operation_seq,
t.supply_type = s.supply_type,
t.optional_yn = s.optional_yn,
t.remarks = COALESCE(s.remarks, t.remarks)
WHEN NOT MATCHED THEN INSERT (
bom_id,
parent_item_id,
component_item_id,
line_no,
find_no,
component_qty,
component_uom,
scrap_factor,
yield_factor,
operation_seq,
supply_type,
optional_yn,
remarks
) VALUES (
s.bom_id,
s.parent_item_id,
s.component_item_id,
s.line_no,
s.find_no,
s.component_qty,
s.component_uom,
s.scrap_factor,
s.yield_factor,
s.operation_seq,
s.supply_type,
s.optional_yn,
s.remarks
);
END;
BEGIN
-- 品目マスタ: 100件
upsert_item('ENG-ASM-I4-2000-NA', '2.0L直列4気筒ガソリンエンジンASSY', 'FG', 'MAKE');
upsert_item('SB-ASM-I4-2000', 'ショートブロックASSY', 'ASM', 'MAKE');
upsert_item('HEAD-ASM-I4-2000', 'シリンダヘッドASSY', 'ASM', 'MAKE');
upsert_item('TIMING-ASM-I4-2000', 'タイミングドライブASSY', 'ASM', 'MAKE');
upsert_item('LUBE-ASM-I4-2000', '潤滑システムASSY', 'ASM', 'MAKE');
upsert_item('COOL-ASM-I4-2000', '冷却システムASSY', 'ASM', 'MAKE');
upsert_item('INTAKE-ASM-I4-2000', '吸気システムASSY', 'ASM', 'MAKE');
upsert_item('EXH-ASM-I4-2000', '排気システムASSY', 'ASM', 'MAKE');
upsert_item('FUEL-ASM-I4-2000', '燃料システムASSY', 'ASM', 'MAKE');
upsert_item('IGNCTRL-ASM-I4-2000', '点火制御システムASSY', 'ASM', 'MAKE');
upsert_item('ACCDRV-ASM-I4-2000', '補機駆動ASSY', 'ASM', 'MAKE');
upsert_item('INSTALL-KIT-I4-2000', '搭載取付キットASSY', 'ASM', 'MAKE');
upsert_item('CYL-BLOCK-AL-I4', 'アルミシリンダブロック', 'PART', 'MAKE');
upsert_item('CRANKSHAFT-FO-STD', '鍛造クランクシャフト', 'PART', 'MAKE');
upsert_item('THRUST-BEARING-SET', 'スラストベアリングセット', 'PART', 'BUY');
upsert_item('MAIN-BEARING-SET', 'メインベアリングセット', 'PART', 'BUY');
upsert_item('CONROD-SET-I4', 'コネクティングロッドセット', 'PART', 'MAKE');
upsert_item('PISTON-PIN-SET-86MM', 'ピストン・ピンセット 86mm', 'PART', 'BUY');
upsert_item('PISTON-RING-SET-86MM', 'ピストンリングセット 86mm', 'PART', 'BUY');
upsert_item('BED-PLATE-I4', 'ベッドプレート', 'PART', 'MAKE');
upsert_item('BED-PLATE-BOLT-SET', 'ベッドプレートボルトセット', 'PART', 'BUY');
upsert_item('CONROD-BOLT-SET', 'コンロッドボルトセット', 'PART', 'BUY');
upsert_item('CRANK-SEAL-FRONT', 'フロントクランクオイルシール', 'PART', 'BUY');
upsert_item('CRANK-SEAL-REAR', 'リアクランクオイルシール', 'PART', 'BUY');
upsert_item('CORE-PLUG-SET', 'コアプラグセット', 'PART', 'BUY');
upsert_item('OIL-SQUIRTER-SET', 'オイルジェットノズルセット', 'PART', 'BUY');
upsert_item('BALANCE-SHAFT-MODULE', 'バランスシャフトモジュール', 'PART', 'BUY');
upsert_item('CYL-HEAD-AL-DOHC', 'アルミシリンダヘッド DOHC', 'PART', 'MAKE');
upsert_item('INTAKE-VALVE-SET-4', '吸気バルブセット 4気筒分', 'PART', 'BUY');
upsert_item('EXHAUST-VALVE-SET-4', '排気バルブセット 4気筒分', 'PART', 'BUY');
upsert_item('VALVE-SPRING-SET-16', 'バルブスプリングセット 16本', 'PART', 'BUY');
upsert_item('VALVE-RETAINER-SET-16', 'バルブリテーナ・コッタセット 16本', 'PART', 'BUY');
upsert_item('VALVE-STEM-SEAL-SET-16', 'バルブステムシールセット 16本', 'PART', 'BUY');
upsert_item('CAMSHAFT-INTAKE-VVT', '吸気カムシャフト VVT', 'PART', 'MAKE');
upsert_item('CAMSHAFT-EXH-VVT', '排気カムシャフト VVT', 'PART', 'MAKE');
upsert_item('ROCKER-ARM-SET-16', 'ロッカーアームセット 16本', 'PART', 'BUY');
upsert_item('LASH-ADJUSTER-SET-16', 'ラッシュアジャスタセット 16個', 'PART', 'BUY');
upsert_item('HEAD-GASKET-MLS', 'メタルヘッドガスケット', 'PART', 'BUY');
upsert_item('HEAD-BOLT-SET', 'シリンダヘッドボルトセット', 'PART', 'BUY');
upsert_item('TIMING-CHAIN-PRIMARY', 'タイミングチェーン', 'PART', 'BUY');
upsert_item('CRANK-SPROCKET', 'クランクスプロケット', 'PART', 'BUY');
upsert_item('CAM-SPROCKET-INTAKE', '吸気カムスプロケット', 'PART', 'BUY');
upsert_item('CAM-SPROCKET-EXH', '排気カムスプロケット', 'PART', 'BUY');
upsert_item('CHAIN-GUIDE-FIXED', 'タイミングチェーン固定ガイド', 'PART', 'BUY');
upsert_item('CHAIN-GUIDE-TENSION', 'タイミングチェーンテンションガイド', 'PART', 'BUY');
upsert_item('CHAIN-TENSIONER-HYD', '油圧チェーンテンショナ', 'PART', 'BUY');
upsert_item('OIL-PUMP-ASM', 'オイルポンプASSY', 'PART', 'BUY');
upsert_item('OIL-PICKUP-STRNR', 'オイルストレーナ', 'PART', 'BUY');
upsert_item('OIL-PAN-UPPER', '上部オイルパン', 'PART', 'MAKE');
upsert_item('OIL-PAN-LOWER', '下部オイルパン', 'PART', 'MAKE');
upsert_item('OIL-PAN-SEALANT', 'オイルパン液状ガスケット', 'PART', 'BUY');
upsert_item('OIL-FILTER-MODULE', 'オイルフィルタモジュール', 'PART', 'BUY');
upsert_item('OIL-PRESS-SENSOR', '油圧センサ', 'PART', 'BUY');
upsert_item('WATER-PUMP', 'ウォータポンプ', 'PART', 'BUY');
upsert_item('THERMOSTAT-ASM', 'サーモスタットASSY', 'PART', 'BUY');
upsert_item('COOLANT-OUTLET-HOUSING', 'クーラントアウトレットハウジング', 'PART', 'BUY');
upsert_item('COOLANT-INLET-PIPE', 'クーラントインレットパイプ', 'PART', 'BUY');
upsert_item('WATER-JACKET-GASKET-SET', '冷却水路ガスケットセット', 'PART', 'BUY');
upsert_item('ECT-SENSOR', '水温センサ', 'PART', 'BUY');
upsert_item('BYPASS-HOSE-SET', 'バイパスホースセット', 'PART', 'BUY');
upsert_item('INTAKE-MANIFOLD', '吸気マニホールド', 'PART', 'MAKE');
upsert_item('THROTTLE-BODY-ETC', '電子スロットルボディ', 'PART', 'BUY');
upsert_item('MAP-SENSOR', '吸気圧センサ', 'PART', 'BUY');
upsert_item('IAT-SENSOR', '吸気温センサ', 'PART', 'BUY');
upsert_item('PCV-VALVE', 'PCVバルブ', 'PART', 'BUY');
upsert_item('INTAKE-MANI-GASKET-SET', '吸気マニホールドガスケットセット', 'PART', 'BUY');
upsert_item('THROTTLE-GASKET', 'スロットルガスケット', 'PART', 'BUY');
upsert_item('EXH-MANIFOLD', '排気マニホールド', 'PART', 'MAKE');
upsert_item('EXH-HEAT-SHIELD', '排気マニホールドヒートシールド', 'PART', 'BUY');
upsert_item('EXH-MANI-GASKET', '排気マニホールドガスケット', 'PART', 'BUY');
upsert_item('O2-SENSOR-UP', '上流O2センサ', 'PART', 'BUY');
upsert_item('FUEL-RAIL', 'フューエルレール', 'PART', 'BUY');
upsert_item('FUEL-INJECTOR-SET-4', 'フューエルインジェクタセット 4本', 'PART', 'BUY');
upsert_item('FUEL-PRESS-REG', '燃圧レギュレータ', 'PART', 'BUY');
upsert_item('FUEL-FEED-PIPE', '燃料供給パイプ', 'PART', 'BUY');
upsert_item('INJECTOR-SEAL-KIT', 'インジェクタシールキット', 'PART', 'BUY');
upsert_item('ENGINE-WIRE-HARNESS', 'エンジンワイヤハーネス', 'PART', 'BUY');
upsert_item('ENGINE-CONTROL-MODULE', 'エンジン制御ECU', 'PART', 'BUY');
upsert_item('CRANK-POS-SENSOR', 'クランク角センサ', 'PART', 'BUY');
upsert_item('CAM-POS-SENSOR', 'カム角センサ', 'PART', 'BUY');
upsert_item('KNOCK-SENSOR', 'ノックセンサ', 'PART', 'BUY');
upsert_item('IGNITION-COIL-SET-4', 'イグニッションコイルセット 4本', 'PART', 'BUY');
upsert_item('SPARK-PLUG-SET-4', 'スパークプラグセット 4本', 'PART', 'BUY');
upsert_item('GROUND-CABLE-SET', 'エンジンアースケーブルセット', 'PART', 'BUY');
upsert_item('HARMONIC-BALANCER', 'ハーモニックバランサ', 'PART', 'BUY');
upsert_item('SERPENTINE-BELT-6PK', 'サーペンタインベルト 6PK', 'PART', 'BUY');
upsert_item('BELT-TENSIONER-ASM', 'ベルトテンショナASSY', 'PART', 'BUY');
upsert_item('IDLER-PULLEY', 'アイドラプーリ', 'PART', 'BUY');
upsert_item('ALTERNATOR-150A', 'オルタネータ 150A', 'PART', 'BUY');
upsert_item('STARTER-MOTOR', 'スタータモータ', 'PART', 'BUY');
upsert_item('ACCESSORY-BRACKET', '補機ブラケット', 'PART', 'MAKE');
upsert_item('ALTERNATOR-BRACKET', 'オルタネータブラケット', 'PART', 'MAKE');
upsert_item('FLYWHEEL', 'フライホイール', 'PART', 'MAKE');
upsert_item('FLYWHEEL-BOLT-SET', 'フライホイールボルトセット', 'PART', 'BUY');
upsert_item('PILOT-BEARING', 'パイロットベアリング', 'PART', 'BUY');
upsert_item('ENG-MOUNT-BRKT-RH', '右エンジンマウントブラケット', 'PART', 'MAKE');
upsert_item('ENG-MOUNT-BRKT-LH', '左エンジンマウントブラケット', 'PART', 'MAKE');
upsert_item('LIFTING-HOOK-FR', '前側吊りフック', 'PART', 'MAKE');
upsert_item('LIFTING-HOOK-RR', '後側吊りフック', 'PART', 'MAKE');
upsert_item('ENGINE-NAMEPLATE', 'エンジン銘板', 'PART', 'BUY');
-- BOMヘッダ: 12件
upsert_bom_header('BOM-ENG-ASM-I4-2000-NA', 'ENG-ASM-I4-2000-NA', '2.0L直列4気筒ガソリンエンジンBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-SB-ASM-I4-2000', 'SB-ASM-I4-2000', 'ショートブロックBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-HEAD-ASM-I4-2000', 'HEAD-ASM-I4-2000', 'シリンダヘッドBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-TIMING-ASM-I4-2000', 'TIMING-ASM-I4-2000', 'タイミングドライブBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-LUBE-ASM-I4-2000', 'LUBE-ASM-I4-2000', '潤滑システムBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-COOL-ASM-I4-2000', 'COOL-ASM-I4-2000', '冷却システムBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-INTAKE-ASM-I4-2000', 'INTAKE-ASM-I4-2000', '吸気システムBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-EXH-ASM-I4-2000', 'EXH-ASM-I4-2000', '排気システムBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-FUEL-ASM-I4-2000', 'FUEL-ASM-I4-2000', '燃料システムBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-IGNCTRL-ASM-I4-2000', 'IGNCTRL-ASM-I4-2000', '点火制御システムBOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-ACCDRV-ASM-I4-2000', 'ACCDRV-ASM-I4-2000', '補機駆動BOM', 'A', 'PRIMARY', DATE '2026-01-01');
upsert_bom_header('BOM-INSTALL-KIT-I4-2000', 'INSTALL-KIT-I4-2000', '搭載取付キットBOM', 'A', 'PRIMARY', DATE '2026-01-01');
-- BOM明細: 99件
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'SB-ASM-I4-2000', 10, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'HEAD-ASM-I4-2000', 20, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'TIMING-ASM-I4-2000', 30, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'LUBE-ASM-I4-2000', 40, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'COOL-ASM-I4-2000', 50, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'INTAKE-ASM-I4-2000', 60, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'EXH-ASM-I4-2000', 70, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'FUEL-ASM-I4-2000', 80, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'IGNCTRL-ASM-I4-2000', 90, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'ACCDRV-ASM-I4-2000', 100, 1, 'EA', 1000);
upsert_bom_line('BOM-ENG-ASM-I4-2000-NA', 'INSTALL-KIT-I4-2000', 110, 1, 'EA', 1000);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'CYL-BLOCK-AL-I4', 10, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'CRANKSHAFT-FO-STD', 20, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'THRUST-BEARING-SET', 30, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'MAIN-BEARING-SET', 40, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'CONROD-SET-I4', 50, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'PISTON-PIN-SET-86MM', 60, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'PISTON-RING-SET-86MM', 70, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'BED-PLATE-I4', 80, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'BED-PLATE-BOLT-SET', 90, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'CONROD-BOLT-SET', 100, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'CRANK-SEAL-FRONT', 110, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'CRANK-SEAL-REAR', 120, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'CORE-PLUG-SET', 130, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'OIL-SQUIRTER-SET', 140, 1, 'EA', 1100);
upsert_bom_line('BOM-SB-ASM-I4-2000', 'BALANCE-SHAFT-MODULE', 150, 1, 'EA', 1100);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'CYL-HEAD-AL-DOHC', 10, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'INTAKE-VALVE-SET-4', 20, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'EXHAUST-VALVE-SET-4', 30, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'VALVE-SPRING-SET-16', 40, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'VALVE-RETAINER-SET-16', 50, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'VALVE-STEM-SEAL-SET-16', 60, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'CAMSHAFT-INTAKE-VVT', 70, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'CAMSHAFT-EXH-VVT', 80, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'ROCKER-ARM-SET-16', 90, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'LASH-ADJUSTER-SET-16', 100, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'HEAD-GASKET-MLS', 110, 1, 'EA', 1200);
upsert_bom_line('BOM-HEAD-ASM-I4-2000', 'HEAD-BOLT-SET', 120, 1, 'EA', 1200);
upsert_bom_line('BOM-TIMING-ASM-I4-2000', 'TIMING-CHAIN-PRIMARY', 10, 1, 'EA', 1300);
upsert_bom_line('BOM-TIMING-ASM-I4-2000', 'CRANK-SPROCKET', 20, 1, 'EA', 1300);
upsert_bom_line('BOM-TIMING-ASM-I4-2000', 'CAM-SPROCKET-INTAKE', 30, 1, 'EA', 1300);
upsert_bom_line('BOM-TIMING-ASM-I4-2000', 'CAM-SPROCKET-EXH', 40, 1, 'EA', 1300);
upsert_bom_line('BOM-TIMING-ASM-I4-2000', 'CHAIN-GUIDE-FIXED', 50, 1, 'EA', 1300);
upsert_bom_line('BOM-TIMING-ASM-I4-2000', 'CHAIN-GUIDE-TENSION', 60, 1, 'EA', 1300);
upsert_bom_line('BOM-TIMING-ASM-I4-2000', 'CHAIN-TENSIONER-HYD', 70, 1, 'EA', 1300);
upsert_bom_line('BOM-LUBE-ASM-I4-2000', 'OIL-PUMP-ASM', 10, 1, 'EA', 1400);
upsert_bom_line('BOM-LUBE-ASM-I4-2000', 'OIL-PICKUP-STRNR', 20, 1, 'EA', 1400);
upsert_bom_line('BOM-LUBE-ASM-I4-2000', 'OIL-PAN-UPPER', 30, 1, 'EA', 1400);
upsert_bom_line('BOM-LUBE-ASM-I4-2000', 'OIL-PAN-LOWER', 40, 1, 'EA', 1400);
upsert_bom_line('BOM-LUBE-ASM-I4-2000', 'OIL-PAN-SEALANT', 50, 1, 'EA', 1400);
upsert_bom_line('BOM-LUBE-ASM-I4-2000', 'OIL-FILTER-MODULE', 60, 1, 'EA', 1400);
upsert_bom_line('BOM-LUBE-ASM-I4-2000', 'OIL-PRESS-SENSOR', 70, 1, 'EA', 1400);
upsert_bom_line('BOM-COOL-ASM-I4-2000', 'WATER-PUMP', 10, 1, 'EA', 1500);
upsert_bom_line('BOM-COOL-ASM-I4-2000', 'THERMOSTAT-ASM', 20, 1, 'EA', 1500);
upsert_bom_line('BOM-COOL-ASM-I4-2000', 'COOLANT-OUTLET-HOUSING', 30, 1, 'EA', 1500);
upsert_bom_line('BOM-COOL-ASM-I4-2000', 'COOLANT-INLET-PIPE', 40, 1, 'EA', 1500);
upsert_bom_line('BOM-COOL-ASM-I4-2000', 'WATER-JACKET-GASKET-SET', 50, 1, 'EA', 1500);
upsert_bom_line('BOM-COOL-ASM-I4-2000', 'ECT-SENSOR', 60, 1, 'EA', 1500);
upsert_bom_line('BOM-COOL-ASM-I4-2000', 'BYPASS-HOSE-SET', 70, 1, 'EA', 1500);
upsert_bom_line('BOM-INTAKE-ASM-I4-2000', 'INTAKE-MANIFOLD', 10, 1, 'EA', 1600);
upsert_bom_line('BOM-INTAKE-ASM-I4-2000', 'THROTTLE-BODY-ETC', 20, 1, 'EA', 1600);
upsert_bom_line('BOM-INTAKE-ASM-I4-2000', 'MAP-SENSOR', 30, 1, 'EA', 1600);
upsert_bom_line('BOM-INTAKE-ASM-I4-2000', 'IAT-SENSOR', 40, 1, 'EA', 1600);
upsert_bom_line('BOM-INTAKE-ASM-I4-2000', 'PCV-VALVE', 50, 1, 'EA', 1600);
upsert_bom_line('BOM-INTAKE-ASM-I4-2000', 'INTAKE-MANI-GASKET-SET', 60, 1, 'EA', 1600);
upsert_bom_line('BOM-INTAKE-ASM-I4-2000', 'THROTTLE-GASKET', 70, 1, 'EA', 1600);
upsert_bom_line('BOM-EXH-ASM-I4-2000', 'EXH-MANIFOLD', 10, 1, 'EA', 1700);
upsert_bom_line('BOM-EXH-ASM-I4-2000', 'EXH-HEAT-SHIELD', 20, 1, 'EA', 1700);
upsert_bom_line('BOM-EXH-ASM-I4-2000', 'EXH-MANI-GASKET', 30, 1, 'EA', 1700);
upsert_bom_line('BOM-EXH-ASM-I4-2000', 'O2-SENSOR-UP', 40, 1, 'EA', 1700);
upsert_bom_line('BOM-FUEL-ASM-I4-2000', 'FUEL-RAIL', 10, 1, 'EA', 1800);
upsert_bom_line('BOM-FUEL-ASM-I4-2000', 'FUEL-INJECTOR-SET-4', 20, 1, 'EA', 1800);
upsert_bom_line('BOM-FUEL-ASM-I4-2000', 'FUEL-PRESS-REG', 30, 1, 'EA', 1800);
upsert_bom_line('BOM-FUEL-ASM-I4-2000', 'FUEL-FEED-PIPE', 40, 1, 'EA', 1800);
upsert_bom_line('BOM-FUEL-ASM-I4-2000', 'INJECTOR-SEAL-KIT', 50, 1, 'EA', 1800);
upsert_bom_line('BOM-IGNCTRL-ASM-I4-2000', 'ENGINE-WIRE-HARNESS', 10, 1, 'EA', 1900);
upsert_bom_line('BOM-IGNCTRL-ASM-I4-2000', 'ENGINE-CONTROL-MODULE', 20, 1, 'EA', 1900);
upsert_bom_line('BOM-IGNCTRL-ASM-I4-2000', 'CRANK-POS-SENSOR', 30, 1, 'EA', 1900);
upsert_bom_line('BOM-IGNCTRL-ASM-I4-2000', 'CAM-POS-SENSOR', 40, 1, 'EA', 1900);
upsert_bom_line('BOM-IGNCTRL-ASM-I4-2000', 'KNOCK-SENSOR', 50, 1, 'EA', 1900);
upsert_bom_line('BOM-IGNCTRL-ASM-I4-2000', 'IGNITION-COIL-SET-4', 60, 1, 'EA', 1900);
upsert_bom_line('BOM-IGNCTRL-ASM-I4-2000', 'SPARK-PLUG-SET-4', 70, 1, 'EA', 1900);
upsert_bom_line('BOM-IGNCTRL-ASM-I4-2000', 'GROUND-CABLE-SET', 80, 1, 'EA', 1900);
upsert_bom_line('BOM-ACCDRV-ASM-I4-2000', 'HARMONIC-BALANCER', 10, 1, 'EA', 2000);
upsert_bom_line('BOM-ACCDRV-ASM-I4-2000', 'SERPENTINE-BELT-6PK', 20, 1, 'EA', 2000);
upsert_bom_line('BOM-ACCDRV-ASM-I4-2000', 'BELT-TENSIONER-ASM', 30, 1, 'EA', 2000);
upsert_bom_line('BOM-ACCDRV-ASM-I4-2000', 'IDLER-PULLEY', 40, 1, 'EA', 2000);
upsert_bom_line('BOM-ACCDRV-ASM-I4-2000', 'ALTERNATOR-150A', 50, 1, 'EA', 2000);
upsert_bom_line('BOM-ACCDRV-ASM-I4-2000', 'STARTER-MOTOR', 60, 1, 'EA', 2000);
upsert_bom_line('BOM-ACCDRV-ASM-I4-2000', 'ACCESSORY-BRACKET', 70, 1, 'EA', 2000);
upsert_bom_line('BOM-ACCDRV-ASM-I4-2000', 'ALTERNATOR-BRACKET', 80, 1, 'EA', 2000);
upsert_bom_line('BOM-INSTALL-KIT-I4-2000', 'FLYWHEEL', 10, 1, 'EA', 2100);
upsert_bom_line('BOM-INSTALL-KIT-I4-2000', 'FLYWHEEL-BOLT-SET', 20, 1, 'EA', 2100);
upsert_bom_line('BOM-INSTALL-KIT-I4-2000', 'PILOT-BEARING', 30, 1, 'EA', 2100);
upsert_bom_line('BOM-INSTALL-KIT-I4-2000', 'ENG-MOUNT-BRKT-RH', 40, 1, 'EA', 2100);
upsert_bom_line('BOM-INSTALL-KIT-I4-2000', 'ENG-MOUNT-BRKT-LH', 50, 1, 'EA', 2100);
upsert_bom_line('BOM-INSTALL-KIT-I4-2000', 'LIFTING-HOOK-FR', 60, 1, 'EA', 2100);
upsert_bom_line('BOM-INSTALL-KIT-I4-2000', 'LIFTING-HOOK-RR', 70, 1, 'EA', 2100);
upsert_bom_line('BOM-INSTALL-KIT-I4-2000', 'ENGINE-NAMEPLATE', 80, 1, 'EA', 2100);
COMMIT;
END;
/
-- ============================================================================
-- 3) PGQL / Oracle Graph
-- A. 品目直接展開グラフ
-- B. BOM改訂認識グラフ
-- ============================================================================
CREATE PROPERTY GRAPH mfg_engine_bom_item_pg
VERTEX TABLES (
mfg_item AS items
KEY (item_id)
LABEL item
PROPERTIES ARE ALL COLUMNS
)
EDGE TABLES (
asm_bom_component AS uses_edges
KEY (bom_line_id)
SOURCE KEY (parent_item_id) REFERENCES items (item_id)
DESTINATION KEY (component_item_id) REFERENCES items (item_id)
LABEL uses
PROPERTIES ARE ALL COLUMNS
)
CREATE PROPERTY GRAPH mfg_engine_bom_rev_pg
VERTEX TABLES (
mfg_item AS items
KEY (item_id)
LABEL item
PROPERTIES ARE ALL COLUMNS,
asm_bom_header AS bom_versions
KEY (bom_id)
LABEL bom_version
PROPERTIES ARE ALL COLUMNS
)
EDGE TABLES (
asm_bom_header AS has_bom_edges
KEY (bom_id)
SOURCE KEY (assembly_item_id) REFERENCES items (item_id)
DESTINATION KEY (bom_id) REFERENCES bom_versions (bom_id)
LABEL has_bom
PROPERTIES ARE ALL COLUMNS,
asm_bom_component AS bom_line_edges
KEY (bom_line_id)
SOURCE KEY (bom_id) REFERENCES bom_versions (bom_id)
DESTINATION KEY (component_item_id) REFERENCES items (item_id)
LABEL bom_line
PROPERTIES ARE ALL COLUMNS
)
この記事でわかること
- BOM向けのRDB設計
- 主キー・外部キー・CHECK制約
CREATE PROPERTY GRAPH- Oracleの
GRAPH_TABLE - APEX Graph Visualization Plugin
このSQLの全体像
このSQLは、大きく分けると次の5層です。
| レイヤー | 役割 |
|---|---|
mfg_item |
品目そのものを管理するマスタ |
asm_bom_header |
どの親品目に対する、どの改訂のBOMかを管理 |
asm_bom_component |
親子関係・数量・工程・歩留などの明細 |
mfg_engine_bom_item_pg |
品目 → 品目 の直接展開Graph |
mfg_engine_bom_rev_pg |
品目 → BOM改訂 → 品目 の改訂認識Graph |
サンプルデータも含まれており、最上位の
ENG-ASM-I4-2000-NA(2.0L直列4気筒ガソリンエンジンASSY)を起点に、ショートブロック、シリンダヘッド、タイミング系、潤滑系などへ展開できる構成になっています。
SQLファイル上では、投入件数のコメントとして次の値が示されています。
- 品目マスタ: 100件
- BOMヘッダ: 12件
- BOM明細: 99件
全体の流れ
1. テーブル作成を読む
このSQLの前半は、BOMを安定して管理するためのRDB設計です。
主キー・一意制約・外部キー・CHECK制約・索引の組み合わせで、BOM を表現しています。
1-1. mfg_item:品目マスタ
まずは登場人物一覧です。
完成品、中間アセンブリ、購入部品まで、全部ここに載せます。
主な列
| 列名 | 意味 |
|---|---|
item_id |
システム内部の主キー |
item_code |
業務で使う品目コード |
item_name |
品目名称 |
item_type |
FG / ASM / PART / RM / CONSUMABLE |
procurement_type |
MAKE / BUY / BOTH |
uom_code |
単位 |
revision_code |
品目自体の改訂 |
lifecycle_status |
ACTIVE / INACTIVE / OBSOLETE |
ポイント
-
item_idはサロゲートキー- 人が見るコードは
item_code - 参照整合性は
item_idで守る
- 人が見るコードは
-
item_codeは業務キー- 外部システム連携や検索にはこちらを使う
-
item_typeとprocurement_typeが重要- どのノードが完成品か
- どれが購入品か
- 自社製造か購入か
を区別できる
代表的な制約
-
pk_mfg_item:主キー -
uk_mfg_item_code:品目コードの一意制約 -
ck_mfg_item_type:品目区分の値域制約 -
ck_mfg_item_proc:調達区分の値域制約 -
ck_mfg_item_lifecycle:ライフサイクル状態の値域制約
1-2. asm_bom_header:BOMヘッダ
これは 「どの親品目の、どの版のBOMか」 を表すテーブルです。
主な列
| 列名 | 意味 |
|---|---|
bom_id |
BOMヘッダの主キー |
bom_code |
BOM識別コード |
assembly_item_id |
親品目 |
bom_name |
BOM名称 |
bom_revision |
BOM改訂 |
alternate_bom_code |
PRIMARY / ALT1 など |
effectivity_start_date |
有効開始日 |
effectivity_end_date |
有効終了日 |
status_code |
DRAFT / RELEASED / OBSOLETE |
なぜヘッダを分けるのか?
BOM明細だけで親子を持つこともできますが、現実の製造業では次の情報が必要になります。
- 改訂管理
- 代替BOM管理
- 有効期間管理
- ステータス管理
つまり、「何が入るか」だけでなく、「いつの、どの版か」も管理したい わけです。
そのため、ヘッダと明細を分けています。
ここが効いている制約
uk_asm_bom_header_codeuk_asm_bom_header_item_rev_altfk_asm_bom_header_itemck_asm_bom_header_statusck_asm_bom_header_effective
特に uk_asm_bom_header_item_rev_alt によって、
同じ親品目に対して、同じ改訂・同じ代替BOMを重複登録させない ようになっています。
1-3. asm_bom_component:BOM明細
ここが、実際の親子関係の本体です。
主な列
| 列名 | 意味 |
|---|---|
bom_line_id |
明細行の主キー |
bom_id |
どのBOMヘッダに属するか |
parent_item_id |
親品目 |
component_item_id |
子品目 |
line_no |
行番号 |
find_no |
図示番号 |
component_qty |
所要数量 |
component_uom |
明細単位 |
scrap_factor |
スクラップ率 |
yield_factor |
歩留係数 |
operation_seq |
工程順序 |
supply_type |
PUSH / PULL / KIT |
optional_yn |
任意部品かどうか |
ポイント
-
component_qty- 親1つあたり子が何個必要か
-
scrap_factor- 作業ロスをどれだけ見込むか
-
yield_factor- 良品として残る割合
-
optional_yn='Y'- 選択部品・オプション部品
-
parent_item_id- ヘッダの
assembly_item_idと重なるように見えるが、 - 再帰SQLやGraph変換をシンプルにするための冗長保持
- ヘッダの
ここが効いている制約
fk_asm_bom_component_bomfk_asm_bom_component_parentfk_asm_bom_component_compck_asm_bom_component_qtyck_asm_bom_component_scrapck_asm_bom_component_yieldck_asm_bom_component_supplyck_asm_bom_component_optionalck_asm_bom_component_no_self
削除時のポイント
fk_asm_bom_component_bom には ON DELETE CASCADE が付いています。
つまり、BOMヘッダを消したら、その配下の明細もまとめて消えます。
1-4. ER図で見る
ADBのデータモデラーで見たER図
engine_bom_oracle.sql のDDLを ADB のデータモデラーで可視化すると、テーブル間の参照関係をさらに直感的に把握できます。
特に、mfg_item を基点に asm_bom_header と asm_bom_component がどうつながるか、主キー・外部キー・一意制約がどこにあるかを、DDLと見比べながら確認しやすくなります。
まとめ
-
mfg_itemは「品目辞書」 -
asm_bom_headerは「BOMの表紙」 -
asm_bom_componentは「BOMの中身」
この3つで、製造BOMの基本構造を表現しています。
2. SQL制約(業務ルール)
SQLの制約は、制約 = 業務ルールの宣言 です。
| 制約の種類 | 例 | 守っていること |
|---|---|---|
| 主キー | pk_mfg_item |
行を一意に識別する |
| 一意制約 | uk_mfg_item_code |
同じ品目コードを重複登録しない |
| 外部キー | fk_asm_bom_header_item |
存在しない品目を親にできない |
| CHECK | ck_asm_bom_component_no_self |
自己循環を防ぐ |
| CHECK | ck_asm_bom_header_effective |
有効期間の矛盾を防ぐ |
| CHECK | ck_asm_bom_component_qty |
0やマイナス数量を防ぐ |
つまりこのSQLは、
業務として成立するBOMデータを担保する設計 になっています。
3. COMMENTで業務意味をSQLに埋め込む
このSQLでは、COMMENT ON TABLE / COMMENT ON COLUMN を丁寧に書いています。
これらのコメントは将来的に、テーブルやカラムを生成AI(LLM)などから参照させる時にコンテキスト理解にも役立ちます。
COMMENT ON TABLE mfg_item IS q'~{
"表示名":"製造品目マスタ",
"説明":"BOMで参照される完成品、中間アセンブリ、購入部品、原材料、消耗品を一意に管理する中核マスタ。"
}~';
これが何に効くのか?
- DB設計書として読める
- 画面表示名に流用しやすい
- AIや検索基盤に「この列は何者か」を伝えやすい
- 後から見た人が、列の業務意味を誤解しにくい
4. MERGEベースのサンプルデータ投入
このSQLは、データ投入も単純な INSERT ではなく、PL/SQLの補助関数と MERGE を使った upsert になっています。
get_item_idget_bom_idupsert_itemupsert_bom_headerupsert_bom_line
という形で、既存データは更新、未登録データは挿入、という振る舞いになります。
なぜ MERGE を使うのか?
- すでに存在するデータは更新
- まだ無いデータは挿入
- スクリプトを再実行しやすい
- デモや検証環境で扱いやすい
ルートの構成イメージ
この時点ですでに、
「最上位ASSYの下に、さらにサブASSYがぶら下がる」
という多階層BOMの形ができています。
5. CREATE PROPERTY GRAPH でRDBをGraphに変える
このSQLは、最後に 2種類の Property Graph を作ります。
- 品目から品目へ直接たどるGraph
- 品目 → BOM改訂 → 品目 という、改訂ノードを残すGraph
どちらも PROPERTIES ARE ALL COLUMNS を使っているため、元テーブル列をそのままGraph属性として扱えます。
5-1. まず、ノードとエッジを言葉で整理する
Graphでは、まず次の2つを意識します。
-
ノード(Vertex)
- モノや概念そのもの
- 例: 品目、BOM改訂
-
エッジ(Edge)
- ノード同士の関係
- 例: 使っている、BOMを持つ、BOM明細でつながる
5-2. mfg_engine_bom_item_pg:直接展開Graph
これは一番シンプルなGraphです。
- ノード:
mfg_item - エッジ:
asm_bom_component - 関係ラベル:
uses
イメージ
何が嬉しい?
このGraphでは、
「親品目から子品目へそのまま辿る」
というBOM展開が非常に分かりやすくなります。
短縮版の定義イメージ
CREATE PROPERTY GRAPH mfg_engine_bom_item_pg
VERTEX TABLES (
mfg_item AS items
KEY (item_id)
LABEL item
PROPERTIES ARE ALL COLUMNS
)
EDGE TABLES (
asm_bom_component AS uses_edges
KEY (bom_line_id)
SOURCE KEY (parent_item_id) REFERENCES items (item_id)
DESTINATION KEY (component_item_id) REFERENCES items (item_id)
LABEL uses
PROPERTIES ARE ALL COLUMNS
);
5-3. mfg_engine_bom_rev_pg:改訂認識Graph
こちらは、BOMヘッダをノードとして残す版です。
- ノード1:
mfg_item→item - ノード2:
asm_bom_header→bom_version - エッジ1:
has_bom - エッジ2:
bom_line
イメージ
何が嬉しい?
こちらは、単なる親子関係だけでなく
- どのBOM改訂か
- どの代替BOMか
- いつ有効か
-
RELEASEDかDRAFTか
まで含めて追跡できます。
5-4. どちらのGraphを使うべきか?
| Graph | 向いている問い |
|---|---|
mfg_engine_bom_item_pg |
このASSYの部品をサッと展開したい |
mfg_engine_bom_rev_pg |
改訂や有効期間込みで正しく見たい |
6. SQLでGraph Queryを書く
Oracleの SQL Property Graph Query は、GRAPH_TABLE を起点に
入力グラフ名 → MATCH → 外側の WHERE → COLUMNS で書きます。
公式ドキュメントでは、基本要素として次が説明されています。
FROM GRAPH_TABLE(...)-
MATCH句 - 外側の
WHERE句 -
COLUMNS句 - 必要に応じて
ONE ROW PER句
また、VERTEX_ID / EDGE_ID による識別子取得もサポートされています。
6-1. 基本の形
SELECT *
FROM GRAPH_TABLE (
<graph_name>
MATCH (<vertex>) -[<edge>]-> (<vertex>)
WHERE <condition>
COLUMNS (...)
);
最低限おさえる4要素
GRAPH_TABLE(<graph_name>)MATCHWHERECOLUMNS
6-2. まずは「直下の子部品」を取る
最初の一歩として、
エンジンASSYの直下にぶら下がる子部品 を取ってみます。
SELECT *
FROM GRAPH_TABLE (
mfg_engine_bom_item_pg
MATCH (root IS item) -[e IS uses]-> (comp IS item)
WHERE root.item_code = 'ENG-ASM-I4-2000-NA'
COLUMNS (
root.item_code AS root_code,
root.item_name AS root_name,
comp.item_code AS component_code,
comp.item_name AS component_name,
comp.item_type AS component_type,
e.line_no AS line_no,
e.component_qty AS component_qty,
e.supply_type AS supply_type
)
)
ORDER BY line_no;
読み方
-
root IS item- 親側のノード
-
e IS uses- 親子関係を表すエッジ
-
comp IS item- 子側のノード
-
root.item_code = 'ENG-ASM-I4-2000-NA'- 起点をエンジンASSYに絞る
この書き方に慣れると、
「どのノードから、どの関係で、どこへ行くか」
をそのままSQLにできます。
6-3. 多階層BOMを 1〜3 レベル辿る
Graphの気持ちよさが出るのはここです。
多階層BOMを、可変長パス で辿れます。
公式ドキュメントで説明されている数量詞は次の3種類です。
{n}{n,m}{,m}
たとえば 1〜3 ホップを辿るなら、次のように書けます。
SELECT *
FROM GRAPH_TABLE (
mfg_engine_bom_item_pg
MATCH (root IS item) -[IS uses]->{1,3} (comp IS item)
WHERE root.item_code = 'ENG-ASM-I4-2000-NA'
COLUMNS (
root.item_code AS root_code,
comp.item_code AS component_code,
comp.item_name AS component_name,
comp.item_type AS component_type
)
)
ORDER BY component_code;
->{1,3} の意味
- 1ホップ
- 2ホップ
- 3ホップ
のいずれか、という意味です。
RDBの自己結合だけでこれをやると、
階層が増えるたびにSQLが長くなりがちです。
Graph Queryでは、「何段辿るか」 を量指定子で書けるのが大きな差です。
6-4. 改訂付きBOMを取りたいなら rev_pg
改訂や代替BOM、有効期間も見たいときは、
mfg_engine_bom_rev_pg を使います。
SELECT *
FROM GRAPH_TABLE (
mfg_engine_bom_rev_pg
MATCH (a IS item) -[hb IS has_bom]-> (b IS bom_version) -[bl IS bom_line]-> (c IS item)
WHERE a.item_code = 'ENG-ASM-I4-2000-NA'
AND b.alternate_bom_code = 'PRIMARY'
AND b.status_code = 'RELEASED'
COLUMNS (
a.item_code AS assembly_code,
b.bom_code AS bom_code,
b.bom_revision AS bom_revision,
b.alternate_bom_code AS alt_bom_code,
b.effectivity_start_date AS effectivity_start_date,
c.item_code AS component_code,
c.item_name AS component_name,
bl.line_no AS line_no,
bl.component_qty AS component_qty
)
)
ORDER BY line_no;
このQueryで分かること
- どの親品目の
- どのBOM改訂を通って
- どの子品目へつながるか
つまり、
「親 → 改訂BOM → 子」
という業務の意味を保ったまま引けます。
6-5. 可視化向けに VERTEX_ID / EDGE_ID を返す
グラフ可視化に載せるときは、要素の識別子も欲しくなります。
SELECT *
FROM GRAPH_TABLE (
mfg_engine_bom_item_pg
MATCH (p IS item) -[e IS uses]-> (c IS item)
WHERE p.item_code = 'SB-ASM-I4-2000'
COLUMNS (
VERTEX_ID(p) AS parent_vertex_id,
EDGE_ID(e) AS edge_id,
VERTEX_ID(c) AS component_vertex_id,
p.item_code AS parent_code,
c.item_code AS component_code
)
);
VERTEX_ID / EDGE_ID は、
「どのノード/どのエッジか」 を一意に扱いたいときに便利です。
6-6. Graph Queryを書くときのコツ
コツ1. 最初は 1ホップから書く
いきなり多階層にしないで、
- 起点ノードを置く
- 1本のエッジでつなぐ
- 返す列を確認する
この順で作ると壊れにくいです。
コツ2. まず「どのGraphを使うか」を決める
- 単純な親子展開 →
mfg_engine_bom_item_pg - 改訂文脈込み →
mfg_engine_bom_rev_pg
コツ3. COLUMNS は最小限から増やす
いきなり全部返すと見づらいので、
- 起点コード
- 子コード
- 数量
- 行番号
あたりから始めると読みやすいです。
コツ4. 基礎表に索引を作る
Oracleの公式ドキュメントでも、SQLグラフ問合せのパフォーマンス改善のため、
基礎表に索引を作成すること が推奨されています。
コツ5. 権限も確認する
SQLプロパティ・グラフを問い合せるには、
グラフまたは基礎オブジェクトへの READ または SELECT 権限 が必要です。
7. APEX Graph Visualization Plugin のインストールと使い方
最後に、Oracle公式ドキュメントの
APEXグラフ・ビジュアライゼーション・プラグイン の内容をもとに、
Graphの可視化まで整理します。
7-1. 事前準備
公式ドキュメントでは、最初の前提として次が挙げられています。
- ターゲットとなる APEX アプリケーションが存在すること
- そのアプリが Oracle Database 26ai に接続していること
- 可視化対象の SQL Property Graph がデフォルトスキーマに存在すること
今回の engine_bom_oracle.sql は、
item_idbom_idbom_line_id
7-2. インストール手順
Oracle Docsベースで整理すると、流れは次の通りです。
- Oracle APEX GitHub から
region_type_plugin_graphviz.sqlをダウンロードする - APEXワークスペースにサインインする
- APEXワークスペースに
DBMS_GVTパッケージとAPEX_SQLGRAPH_JSON関数を作成するgvt_sqlgraph_to_json.sqlapex_sqlgraph_to_json.sql
- ダウンロードしたプラグインをターゲットAPEXアプリにインポートする
- アプリのページにリージョンとして配置する
補足
Oracle Docs では、GitHub上のこのプラグイン版は APEX 24.2 でのみサポート と説明されています。
導入前に、対象のAPEXバージョンを確認しておくと安心です。
7-3. ページ・デザイナでの基本設定
ページ・デザイナでは、新しいリージョンを作成し、次のように設定します。
- タイプ: Graph Visualization (Preview)
- 場所: Local Database
- タイプ値:
- SQL Query
- または Property Graph
ここで指定するのが、Graphをどう取得するかの方法です。
設定画面のイメージ
以下は、APEX のページ・デザイナで Graph リージョンを作成し、でグラフ名や MATCH / COLUMNS を指定している画面イメージです。
記事中の説明と、実際の画面上の入力欄を対応付けながら読むと理解しやすくなります。
7-4. SQL Query モード
これは、GRAPH_TABLE を丸ごと書く方式です。
SELECT *
FROM GRAPH_TABLE (
mfg_engine_bom_item_pg
MATCH (root IS item) -[e IS uses]-> (comp IS item)
WHERE root.item_code = 'ENG-ASM-I4-2000-NA'
COLUMNS (
VERTEX_ID(root) AS root_id,
EDGE_ID(e) AS edge_id,
VERTEX_ID(comp) AS comp_id
)
);
向いているケース
- SQLをそのまま管理したい
- 複雑な条件を1本のSQLで表現したい
- SQLベースでレビューしやすくしたい
親子リージョンを分けた設定例
親ノード用のリージョンと子ノード用のリージョンを分けると、選択中の頂点を起点に段階的に展開するUIも作りやすくなります。
次の画面では、GRAPH_PARENT / GRAPH_CHILD のようにリージョンを分けつつ、ページアイテムを WHERE 句へ渡して絞り込む構成が確認できます。
7-5. 表示ラベル・Expand・表示サイズの調整
APEX の Graph Visualization では、どのラベルをノード名として見せるか、何ホップまで展開するか、一度に何要素を表示するか を組み合わせて調整できます。
たとえば次のような設定が使えます。
- Vertex Caption:
ITEM_NAME - Show full label text on hover: ON
- Enable Expand: ON
- Expand Hops:
1 - Max Number Of Hop:
5
特に Vertex Caption を ITEM_NAME にすると、ITEM_CODE だけの表示より意味が分かりやすくなります。
また Expand Hops を小さく始めると、巨大グラフでも画面が崩れにくく、段階的に探索できます。
ドキュメントでは、表示サイズコントロールを有効にすることで、
表示する頂点数・エッジ数・要素数を動的に変更できる と説明されています。
多階層BOMは一気に可視化すると密になりやすいので、
- まず 1ホップ
- 次に 1〜2ホップ
- 特定サブASSYだけ
- その後に 1〜3ホップ以上
というように、段階的に広げるのがおすすめです。
7-6. 実際の表示結果
設定ができたら、APEX 上では次のようにグラフが表示されます。
全体を表示した例
全体を表示すると、エンジンASSYを中心に多階層の部品関係がネットワークとして描かれます。
表形式では見えにくい「どの品目がどの品目にぶら下がっているか」が、ノードとエッジのつながりとして直感的に把握できます。
ノードを起点に段階展開した例
特定のノードを選び、ホップ数を指定して展開すると、対象ノード周辺だけをフォーカスして追えます。
全体俯瞰と詳細確認を行き来できるのが、Graph可視化の大きな利点です。
Sample Graph Visualizations アプリ
公式ドキュメントでは、Sample Graph Visualizations アプリのインポート手順も案内されています。
もし完成例を先に見たいなら、このサンプルを使うのも有効です。
まとめ
今回の engine_bom_oracle.sql は、
「RDBで正しく設計したBOMを、必要に応じてGraphとして扱う」
という流れを示しています。
ポイントを整理すると、次の通りです。
-
mfg_item/asm_bom_header/asm_bom_componentの3表でBOMの基本を表す - 主キー・外部キー・CHECK制約で業務ルールを守る
-
COMMENTに業務意味を埋め込むことで、設計書・UI・AI利用にも効く -
mfg_engine_bom_item_pgとmfg_engine_bom_rev_pgを使い分ける -
GRAPH_TABLEを使えば、SQLのままGraph Queryが書ける - APEX Graph Visualization Plugin を使えば、SQL Query モードまたは Property Graph モードで可視化できる





