1

我创建了一个将 xml 数据解析为多个表的过程。我正在捕获主键约束的异常,如果在结果中发现重复项,它将被插入到名为 DUPLICATE 的表中。
现在,当我使用光标时,它的迭代次数往往超过所需的次数,即 1 次

DECLARE
PER_ID varchar2(20);
    NAME varchar2(20);
SECTIONS_ID varchar2(20);
SECTIONS_NAME varchar2(20);
    var1 number;
    exception_var number;
CURSOR C1 IS
    select d.department_id
       , d.department_name
       , s.sections_id
      , s.sections_name
   from xml_unit_download t
     , xmltable(
         '/ROWSET/DATA'
         passing t.xml_file
         columns
           DEPARTMENT_ID   varchar2(20) path 'DEPARTMENT/DEPARTMENT_ID'
         , DEPARTMENT_NAME varchar2(30) path 'DEPARTMENT/DEPARTMENT_NAME'
         , SECTIONS        xmltype      path 'SECTIONS'
       ) d
     , xmltable(
         '/SECTIONS'
         passing d.sections
         columns
           SECTIONS_ID     varchar2(20) path 'SECTIONS_ID'
        , SECTIONS_NAME   varchar2(30) path 'SECTIONS_NAME'
      ) s
 where
  t.Status = 4;
  BEGIN

  FOR R_C1 IN C1 LOOP
      BEGIN
      insert into DEPARTMENT(id, name) values(R_C1.PER_ID, R_C1.name);
      insert into SECTIONS(id, name) values(R_C1.SECTIONS_ID, R_C1.SECTIONS_NAME);
      var1:= var1+1;
       dbms_output.put_line('Insert=' || var1);
      commit;
           --dbms_output.put_line('Duplicate='||var);
      EXCEPTION
         WHEN DUP_VAL_ON_INDEX THEN
         dbms_output.put_line('Duplicate=');
         insert into duplicate(id, name)values(R_C1.id, R_C1_name);
      END;
      END LOOP;
  END;

我将如何处理这种情况?我也尝试过使用 INSERT ALL,但它似乎不起作用。这是我对 INSERT ALL 程序的尝试

DECLARE
PER_ID varchar2(20);
    NAME varchar2(200);
    var1 number;
    exception_var number;

  BEGIN

      insert all
      into SECTIONS (id) values(department_id)

      --into sect (id, name) values(s.SECTIONS_ID, s.SECTIONS_NAME )
   select d.department_id
       , d.department_name
       , s.sections_id
      , s.sections_name
   from xml_unit_download t
     , xmltable(
         '/ROWSET/DATA'
         passing t.xml_file
         columns
           "DEPARTMENT_ID"   varchar2(20) path 'DEPARTMENT/DEPARTMENT_ID'
         , "DEPARTMENT_NAME" varchar2(30) path 'DEPARTMENT/DEPARTMENT_NAME'
         , "SECTIONS"        xmltype      path 'SECTIONS'
       ) d
     , xmltable(
         '/SECTIONS'
         passing d.sections
         columns
           "SECTIONS_ID"     varchar2(20) path 'SECTIONS_ID'
        , "SECTIONS_NAME"   varchar2(30) path 'SECTIONS_NAME'
      ) s
 where
  t.Status = 4;
  dbms_output.put_line('Insert=' || var1);
      var1:= var1+1;
       dbms_output.put_line('Insert=' || var1);
      commit;
           --dbms_output.put_line('Duplicate='||var);
      EXCEPTION
         WHEN DUP_VAL_ON_INDEX THEN
         --insert into
         dbms_output.put_line('Duplicate=');
  END;

正在查询的 XML 包含 DEPARTMENT 及其 SECTIONS 的数据。DEPARTMENT 与 SECTIONS 有一对多的关系,即一个 DEPARTMENT 可以有一个或多个 SECTIONS,并且可能存在 DEPARTMENT 没有任何 SECTIONS 的情况。

XML 的结构是这样的,标签标识一个部门及其相应的部分。
XML

<ROWSET> 
<DATA>
 <DEPARTMENT>
  <DEPARTMENT_ID>DEP1</DEPARTMENT_ID>
  <DEPARTMENT_NAME>myDEPARTMENT1</DEPARTMENT_NAME>
 </DEPARTMENT>
 <SECTIONS>
  <SECTIONS_ID>6390135666643567</SECTIONS_ID>
  <SECTIONS_NAME>mySection1</SECTIONS_NAME>
  </SECTIONS>
   <SECTIONS>
  <SECTIONS_ID>6390135666643567</SECTIONS_ID>
  <SECTIONS_NAME>mySection2</SECTIONS_NAME>
  </SECTIONS>
 </DATA>
 <DATA>
 <DEPARTMENT>
  <DEPARTMENT_ID>DEP2</DEPARTMENT_ID>
  <DEPARTMENT_NAME>myDEPARTMENT2</DEPARTMENT_NAME>
 </DEPARTMENT>
 <SECTIONS>
  <SECTIONS_ID>63902</SECTIONS_ID>
  <SECTIONS_NAME>mySection1</SECTIONS_NAME>
  </SECTIONS>
 </DATA>
<DATA>
 <DEPARTMENT>
  <DEPARTMENT_ID>DEP3</DEPARTMENT_ID>
  <DEPARTMENT_NAME>myDEPARTMENT3</DEPARTMENT_NAME>
 </DEPARTMENT>
</DATA>
</ROWSET>
4

2 回答 2

1

这似乎是一个基本的基数问题 - 如果您在一个部门中有多个部分,那么您将在结果中为每个部门获得不止一行,因此由于程序代码,您将看到重复的部门信息而不是输入数据。

与其尝试在一个查询中执行此操作,不如将其分成两个游标/for 循环?

像这样的东西:

BEGIN

  <<department_loop>>
  FOR r_department IN (
    SELECT 
      d.department_id
    , d.department_name
    , d.sections
    FROM xml_unit_download t
   , XMLTABLE(
     '/ROWSET/DATA'
     PASSING t.xml_file
     COLUMNS
       department_id   VARCHAR2(20) PATH 'DEPARTMENT/DEPARTMENT_ID'
     , department_name VARCHAR2(30) PATH 'DEPARTMENT/DEPARTMENT_NAME'
     , sections        XMLTYPE      path 'SECTIONS'
    ) d
    WHERE t.status = 4
  )
  LOOP

    BEGIN
      INSERT INTO departments (id, name)
      VALUES (r_department.department_id, r_department.department_name);
    EXCEPTION
      WHEN DUP_VAL_ON_INDEX THEN
        INSERT INTO department_duplicates (id, name)
        VALUES (r_department.department_id, r_department.department_name);
    END;

    <<section_loop>>
    FOR r_section IN (
      SELECT 
        s.sections_id
      , s.sections_name
      FROM XMLTABLE (
      '/SECTIONS'
      PASSING r_department.sections
      COLUMNS
        sections_id   VARCHAR2(20) PATH 'SECTIONS_ID'
      , sections_name VARCHAR2(30) PATH 'SECTIONS_NAME'
      ) s
    )
    LOOP

      BEGIN
        INSERT INTO sections (id, name, department_id)
        VALUES (r_section.sections_id, r_section.sections_name, r_department.department_id);
      EXCEPTION
        WHEN DUP_VAL_ON_INDEX THEN
          INSERT INTO section_duplicates (id, name, department_id)
          VALUES (r_section.sections_id, r_section.sections_name, r_department.department_id);
      END;

    END LOOP section_loop;

  END LOOP department_loop;

END;
/

这有以下好处:

  • 就部门和部门而言,您可以相当容易地捕获单个重复项(如果找到)。
  • 由于程序代码,您不会引入基数 - 任何重复都是真正的输入数据重复。
  • 您不必担心跟踪您正在使用/已经使用的行,它隐含在嵌套循环结构中。
于 2013-05-04T12:51:41.900 回答
1

由于每个部门可以有多个部分,因此您会期望重复。您可能只需移动捕获异常的位置即可获得所需的内容,因此它仍会插入sections

  FOR R_C1 IN C1 LOOP
      BEGIN
         insert into DEPARTMENT(id, name)
            values(R_C1.department_id, R_C1.department_name);
      EXCEPTION
         WHEN DUP_VAL_ON_INDEX THEN
         dbms_output.put_line('Duplicate=');
         insert into duplicate(id, name)
            values(R_C1.department_id, R_C1.department_name);
      END;
      insert into SECTIONS(id, name)
         values(R_C1.SECTIONS_ID, R_C1.SECTIONS_NAME);
      var1:= var1+1;
      dbms_output.put_line('Insert=' || var1);
   END LOOP;

您还可以使用跟踪器变量(如果您看到与department_id之前看到的记录相同的记录,请不要尝试插入department记录,只需执行sections插入),或者您可以使用嵌套循环:

declare
   cursor dept_cur is
      select d.department_id
         , d.department_name
         , d.sections
      from xml_unit_download t
         , xmltable(
            '/ROWSET/DATA'
            passing t.xml_file
            columns
              "DEPARTMENT_ID"   varchar2(20) path 'DEPARTMENT/DEPARTMENT_ID'
            , "DEPARTMENT_NAME" varchar2(30) path 'DEPARTMENT/DEPARTMENT_NAME'
            , "SECTIONS"        xmltype      path 'SECTIONS'
         ) d
      where
         t.Status = 4;

   cursor sect_cur(sections xmltype) is
      select s.sections_id
         , s.sections_name
      from xmltable(
            '/SECTIONS'
            passing sections
            columns
              "SECTIONS_ID"     varchar2(20) path 'SECTIONS_ID'
           , "SECTIONS_NAME"   varchar2(30) path 'SECTIONS_NAME'
         ) s;
begin
   for dept in dept_cur loop
      insert into department(id, name)
         values (dept.department_id, dept.department_name);
      for sect in sect_cur(dept.sections) loop
         insert into sections(id, name, department_id)
            values (sect.sections_id, sect.sections_name, dept.department_id);
      end loop;
   end loop;
end;
/

PL/SQL procedure successfully completed.

这使用一个循环来获取部门信息(不会有重复)以及 XMLTYPE 部分,然后将该部分传递给第二个光标以展开。

select * from department;

ID                             NAME
------------------------------ ------------------------------
DEP1                           myDEPARTMENT1
DEP2                           myDEPARTMENT2
DEP3                           myDEPARTMENT3

select * from sections;

ID                             NAME                           DEPARTMENT_ID
------------------------------ ------------------------------ ------------------------------
6390135666643567               mySection1                     DEP1
6390135666643567               mySection2                     DEP1
63902                          mySection1                     DEP2

您不必使用 PL/SQL,只需执行两次插入即可:

insert into department(id, name)
select d.department_id
   , d.department_name
from xml_unit_download t
   , xmltable(
      '/ROWSET/DATA'
      passing t.xml_file
      columns
        "DEPARTMENT_ID"   varchar2(20) path 'DEPARTMENT/DEPARTMENT_ID'
      , "DEPARTMENT_NAME" varchar2(30) path 'DEPARTMENT/DEPARTMENT_NAME'
   ) d
where
   t.Status = 4;

... 和:

insert into sections(id, name, department_id)
select s.sections_id
   , s.sections_name
   , d.department_id
from xml_unit_download t
   , xmltable(
      '/ROWSET/DATA'
      passing t.xml_file
      columns 
        "DEPARTMENT_ID"   varchar2(20) path 'DEPARTMENT/DEPARTMENT_ID'
      , "DEPARTMENT_NAME" varchar2(30) path 'DEPARTMENT/DEPARTMENT_NAME'
      , "SECTIONS"        xmltype      path 'SECTIONS'
   ) d
   , xmltable(
      '/SECTIONS'
      passing d.sections
      columns 
        "SECTIONS_ID"     varchar2(20) path 'SECTIONS_ID'
     , "SECTIONS_NAME"   varchar2(30) path 'SECTIONS_NAME'
   ) s
where
   t.Status = 4;

...它将与 PL/SQL 块相同的数据放入表中。

在这两种情况下,我都假设您想要一个链接两个表的列,但也许它们不是唯一链接的,您需要单独section的和department_section表,这可以很容易地以相同的方式生成。

另请注意,这两种方法都会为 创建一个department记录DEP3,除非您使用先前答案中的外连接,否则您的原始方法不会这样做;然后你必须注意到没有部分信息并且不要尝试第二次插入。

于 2013-05-04T11:42:20.750 回答