2017-10-10 97 views
0

我想開發一個高效的XML解析例程,它使用從父/子關係的數據源的多表插入。我想要同時插入1個父級和多個子級記錄。不幸的是,我得到的父母記錄數量與我的孩子記錄數量相同。Oracle多表插入與一個父/子關係

XML看起來像這樣,但有數百個父元素和數千個子元素。

<batch id="123" process_date="20171010"> 
    <parent id="101" name="P101" status="active"> 
     <child id="201" name="C201" value="xxx1" /> 
     <child id="202" name="C202" value="xxx2" /> 
     <child id="203" name="C203" value="xxx3" /> 
     <child id="204" name="C204" value="xxx4" /> 
     <child id="205" name="C205" value="xxx5" /> 
    </parent> 
    <parent id="102" name="P102" status="active"> 
     <child id="211" name="C211" value="yyy1" /> 
     <child id="212" name="C212 value="yyy2" /> 
     <child id="213" name="C213" value="yyy3" /> 
    </parent> 
    <parent id="103" name="P103" status="suspended"> 
     <child id="221" name="C221" value="zzz1" /> 
    </parent> 
</batch> 

這些XML文檔存儲在XML列於下表:

tblBatchUpload table (batch_upload_id int, xml xmltype) 

我要分析此XML並將其存儲到3個表:

tblBatch (batch_id int, process_date varchar2(8)) 
tblParent (parent_id int, batch_id int, parent_name varchar2(10), parent_status varchar2(10)) 
tblChild (child_id int, parent_id int, child_name varchar2(10), child_value varchar2(10)) 

我知道我可以通過多遍從這個扁平表中提取批處理,父項和子項,但是我真正想要做的是在一次傳遞中針對這些數據插入一個多表。問題是當我想要的是一個批記錄和唯一的父記錄時,我的多表插入插入與子記錄相同數量的批記錄和父記錄。

這是我嘗試在查詢:

INSERT ALL 
    -- always insert the child record 
    WHEN 1=1 THEN 
     INTO tblChild (child_id, parent_id, child_name, child_value) 
      VALUES (child_id, parent_id, child_name, child_value) 
    -- only insert parents that don't already exist 
    WHEN NOT EXISTS (SELECT * FROM tblParent A WHERE A.parent_id = parent_id) THEN 
     INTO tblParent (parent_id, batch_id, parent_name, parent_status) 
      VALUES (parent_id, batch_id, parent_name, parent_status) 
    -- only insert batches that don't already exist 
    WHEN NOT EXISTS (SELECT * FROM tblBatch A WHERE A.batch_id = batch_id) THEN 
     INTO tblBatch (batch_id, process_date) 
      VALUES (batch_id int, process_date) 
SELECT 
    t.batch_upload_id 
    b.batch_id, 
    b.process_date, 
    p.parent_id, 
    p.parent_name, 
    p.parent_status, 
    c.child_id, 
    c.child_name, 
    c.child_value 
FROM 
    tbl_batch_upload t, 
    XMLTABLE ('/batch' passing t.xml 
     columns batch_id int path '@id', 
      process_date varchar2(8) path '@process_date', 
      parents XMLTYPE PATH 'Parent') b, 
    XMLTABLE ('/parent' passing b.parents 
     columns parent_id int path '@id', 
      parent_name varchar2(10) path '@name', 
      parent_status varchar2(10) path '@status', 
      children XMLTYPE PATH 'child') p 
    XMLTABLE ('/child' passing p.children 
     columns child_id int path '@id', 
      child_name varchar2(10) path '@name', 
      child_value varchar2(10) path '@value') c 
WHERE 
    t.batch_upload_id = :p_batch_upload_id; 

我怎麼只能插入獨特的批生產記錄和獨特的父記錄,而只有解析XML一次?

回答

0

我找到了解決方案。我不得不將FOR ORDINALITY列添加到XMLTABLE,並且將批處理和父項的條件插入基於子行的順序:

INSERT ALL 
    -- always insert the child record 
    WHEN 1=1 THEN 
     INTO tblChild (child_id, parent_id, child_name, child_value) 
      VALUES (child_id, parent_id, child_name, child_value) 
    -- first child implies a new parent 
    WHEN child_ordinal = 1 /* AND NOT EXISTS (SELECT * FROM tblParent A WHERE A.parent_id = parent_id) */ THEN 
     INTO tblParent (parent_id, batch_id, parent_name, parent_status) 
      VALUES (parent_id, batch_id, parent_name, parent_status) 
    -- first parent first child implies a new batch 
    WHEN parent_ordinal = 1 AND child_ordinal = 1 /* AND NOT EXISTS (SELECT * FROM tblBatch A WHERE A.batch_id = batch_id) */ THEN 
     INTO tblBatch (batch_id, process_date) 
      VALUES (batch_id int, process_date) 
SELECT 
    t.batch_upload_id 
    b.batch_id, 
    b.process_date, 
    p.parent_ordinal, 
    p.parent_id, 
    p.parent_name, 
    p.parent_status, 
    c.child_ordinal, 
    c.child_id, 
    c.child_name, 
    c.child_value 
FROM 
    tbl_batch_upload t, 
    XMLTABLE ('/batch' passing t.xml 
     columns batch_id int path '@id', 
      process_date varchar2(8) path '@process_date', 
      parents XMLTYPE PATH 'Parent') b, 
    XMLTABLE ('/parent' passing b.parents 
     columns 
      parent_ordinal FOR ORDINALITY, 
      parent_id int path '@id', 
      parent_name varchar2(10) path '@name', 
      parent_status varchar2(10) path '@status', 
      children XMLTYPE PATH 'child') p 
    XMLTABLE ('/child' passing p.children 
     columns 
      child_ordinal FOR ORDINALITY, 
      child_id int path '@id', 
      child_name varchar2(10) path '@name', 
      child_value varchar2(10) path '@value') c 
WHERE 
    t.batch_upload_id = :p_batch_upload_id;