PLSQL:将数据从 XML 解析并插入到表中

Posted

技术标签:

【中文标题】PLSQL:将数据从 XML 解析并插入到表中【英文标题】:PLSQL: Parsing and inserting data from XML to tables 【发布时间】:2019-08-11 18:13:16 【问题描述】:

我有以下表格:

 create table TBL$ORDERS  
(  
  order_num  VARCHAR2(25),  
  employee   VARCHAR2(100),  
  createddt  VARCHAR2(20),  
  modifieddt VARCHAR2(20),  
  deleteddt  VARCHAR2(20),  
  state      VARCHAR2(15),  
  ts         VARCHAR2(20),  
  nni        VARCHAR2(20),  
  constraint pk_orders primary key (order_num)  
);  

create table TBL$METERS  
(  
  order_num  VARCHAR2(25),  
  xmlid      VARCHAR2(5),  
  createddt  VARCHAR2(20),  
  modifieddt VARCHAR2(20),  
  deleteddt  VARCHAR2(20),  
  vendor     VARCHAR2(25),  
  model      VARCHAR2(25),  
  serial     VARCHAR2(25),  
  constraint pk_meters primary key (order_num, xmlid),  
  constraint fk_orders foreign key (order_num) references TBL$ORDERS (order_num)  
);  

create table TBL$TESTS  
(  
  order_num    VARCHAR2(25),  
  meterxmlid   VARCHAR2(5),    
  xmlid        VARCHAR2(5),    
  createddt    VARCHAR2(20),  
  testcount    VARCHAR2(5),  
  successcount VARCHAR2(5),  
  test1        VARCHAR2(50),  
  test2        VARCHAR2(50),  
  test3        VARCHAR2(50),  
  test4        VARCHAR2(50),  
  constraint pk_tests primary key (order_num, meterxmlid, xmlid),  
  constraint fk_meters foreign key (order_num, meterxmlid) references TBL$METERS (order_num, xmlid)  
); 

我想用这段代码填充:

procedure PopulateTables(pXMLClob clob) is  
begin  
  for recOrders in (  
    select *  
    from   XMLTable(  
             '/Orders/Order'  
             passing XMLType(pXMLClob)  
               columns  
                 order_num  VARCHAR2(25)  path '@order_num',  
                 employee   VARCHAR2(100) path '@employee',  
                 createddt  VARCHAR2(20)  path '@createdDT',  
                 modifieddt VARCHAR2(20)  path '@modifiedDT',  
                 deleteddt  VARCHAR2(20)  path '@deletedDT',  
                 state      VARCHAR2(15)  path '@state',  
                 ts         VARCHAR2(20)  path '@ts',  
                 nni        VARCHAR2(20)  path '@nni',  
                 Meters     xmltype      path 'Meter'  
             )  
  )  
  loop  
    insert into TBL$ORDERS(order_num, employee, createddt, modifieddt, deleteddt, state, ts, nni)  
    values (recOrders.order_num, recOrders.employee, recOrders.createddt, recOrders.modifieddt, recOrders.deleteddt, recOrders.state, recOrders.ts, recOrders.nni);  
    for recMeters in (  
      select *  
      from   XMLTable(  
               '/Orders/Order'  
               passing XMLType(recOrders.Meters)  

                 columns  
                    xmlid      VARCHAR2(5)  path '@xmlid',  
                    createddt  VARCHAR2(20) path '@createdDT',  
                    modifieddt VARCHAR2(20) path '@modifiedDT',  
                    deleteddt  VARCHAR2(20) path '@deletedDT',  
                    vendor     VARCHAR2(25) path '@vendor',  
                    model      VARCHAR2(25) path '@model',  
                    serial     VARCHAR2(25) path '@serial',  
                    order_num  VARCHAR2(25) path '@order_num',  
                    tests      xmltype      path 'test'  
             )      
    )  
    loop  
      insert into TBL$METERS (order_num, xmlid, createddt, modifieddt, deleteddt, vendor, model, serial)  
      values (recOrders.order_num, recMeters.xmlid, recMeters.createddt, recMeters.modifieddt, recMeters.deleteddt, recMeters.vendor, recMeters.model, recMeters.serial);  
      for recTest in (  
        select *  
        from   XMLTable(  
                 '/Orders/Order'  
                 passing XMLType(recMeters.Tests)  
                 columns 
  xmlid        VARCHAR2(5) path '@xmlID',
  createddt    VARCHAR2(20) path '@createdDT',
  testcount    VARCHAR2(5) path '@testCount',
  successcount VARCHAR2(5) path '@successCount',
  test1        VARCHAR2(50) path '@test1',
  test2        VARCHAR2(50) path '@test2',
  test3        VARCHAR2(50) path '@test3',
  test4        VARCHAR2(50) path '@test4',
  meter_xmlid  VARCHAR2(5) path '@xmlID',
  order_order_num varchar(25) path '@order_num' 

               )          
      )  
      loop  

      insert into TBL$TESTS (xmlid, createddt, testcount, successcount, test1, test2, test3, test4, meter_xmlid , order_order_num  )  
      values (recTest.xmlid, recTest.createddt, recTest.testcount, recTest.successcount, recTest.test1, recTest.test2, recTest.test3, recTest.test4, recMeters.xmlid, recOrders.Order_num);

      end loop;    
    end loop;   
  end loop;  
  commit;    
end;

我在传递 XMLTYPE() 的行中遇到错误。

ora-00306 调用 XMLTYPE 时类型或参数的数量错误。

谁能告诉我如何解决这个问题?

【问题讨论】:

请将您的代码直接提供给 Stack Overflow 请添加完整的错误信息,包括行号。它也将有助于尽可能减少代码。代码越大,别人看的可能性就越小。 【参考方案1】:

我确实制定了对您之前问题的答案,您已将其删除。我已经改变它以适应新的问题。问题都是一样的。


方法

在这些表之间建立关系 我需要使用 PK 和 FK 在这些表之间建立关系。

它不是那样工作的。我很欣赏你有一项任务,并且只专注于完成工作,但这种心态充满了问题。不仅是现在,还有未来会暴露的可预测和可预防的错误。

正确的做法是:

创建一个与自然宇宙相匹配(是地图)的数据库。为什么 ?因为宇宙的结构不会改变(内容会改变)。 这将使您的应用免受更改(来自 XML 端的更改)。 否则,每次 XML (a) 更改或 (b) 您发现错误(现在有一些错误!)并需要修复它时,您都必须更改数据库的结构。因此是代码。

相反的是保持开发人员对数据的看法(您现在需要什么),这与现实世界几乎没有关系。在这种情况下,您永远不会真正了解数据的本来面目,您会将数据作为片段进行处理,并且会像玩壳游戏一样不断移动它们。

我有以下表格:

有第一个误解。这些不是表,这只是传入 XML 的定义。将它们视为表意味着您认为这是您需要在数据库中实现的最终产品。然后,对现实世界的感知以及数据如何表示它,就会丢失。

事实是,您有一个 XML 文件定义,它不是 1::1 的表,也不是 1::1 现实世界

有人告诉我,一个订单可以有多个仪表,一个仪表可以有多个测试。 但是,我在这些表中看不到可以建立关系的属性

那是因为没有(在 XML 中)。同样,您必须再次弄清楚,最好的选择是现实世界,而不是 XML。

meter表中应该有order_num字段,Test表中应该有meter_id。

如果您想要一个 1960 年代的记录归档系统,那没关系,这是“理论家”和追随他们的作者错误地宣传为“关系”的东西。高度受限,没有完整性。

我看到您现在已经添加了这些字段。

这是一个很好的例子,说明它没有任何完整性。

如何[正确]识别仪表? 你会说primary key ( order_num, xmlid ), 但那是错误的,xmlid 只是 XML 开发人员分配给Orders 下的行的行号 如果您使用它,您将在一天记录仪表 X 和仪表 X 的读数,并在第二天记录仪表 X 和仪表 Y 的读数。反之亦然。 即使他们知道存在问题,即 [对于任何给定订单] 领域中的仪表不断变化(注意 modifieddtdeleteddt) 他们给了你( vendor, model, serial ) 这是 Meter 唯一有效的 PK(如果您希望防止可预防的错误) xmlid 与您完全无关(仅与 XML 开发人员相关),可以从 XML 文件中排除

关系数据库

我可以提供您执行此任务所需的关系数据库。它具有完整的数据完整性。但这是第一次剪辑,并不完整。为什么 ?因为 XML 文件中存在严重错误。一旦你修复了这些错误,我就可以调整数据库,它就会完成。

“测试”可能不是一个好的标签。测试阶段完成后,将是Meter读数 一个读数中似乎有四个样本 员工可能没那么重要,我给它是为了完整 我所有的数据模型都在 IDEF1X 中呈现,这是自 1993 年以来用于建模关系数据库的标准 我的IDEF1X Introduction 是必读。

错误是 XML 文件

(参考已删除问题中给出的 XML 文件。

不得让serial 为空 Test(更好的是test1..4)必须通过(供应商、型号、序列号)识别适用的仪表 每个 Test 中的 4 个样本未标准化,应该有 4 个 XML 记录(每个 test1..4test4 有日期时间,但没有 test1..3 test4 中的日期时间需要为格式 23(与其他日期时间相同) 员工识别订单还是抄表?我模拟了前者。

最后,一旦删除了重复和愚蠢,XML file that is required for the purpose 比他们给你的要简单得多。


解析 XML

但是,我的解析器 xml 程序有问题。您能否阅读我的程序并帮助我将 FK 也包括在内?

    没有。这超出了这个问题的范围。提出一个新问题,并让该领域的人帮助您。

    链接失败。

    坦率地说,我从不关心那些声称解析 XML 的产品,它们比它们的价值更麻烦,而且当它们失败时,你必须自己摆弄它。我只是编写代码来解析 XML,这很简单。我将awk 用于所有此类工作,所有ETL。

    perl 更好,因为它有一个到数据库的 PLSQL-Client 连接,但它需要一些设置。我认为 Oracle 变体是oraperl

PLSQL 过程

我没有 Oracle 专业知识,因此无法在代码级别对此做出回应。但是让我说,如果你理解我这里的答案,并实现与现实世界相匹配的表,你的代码会很简单。

首先要理解的是,您不能只将 XML 文件导入数据库(使用 XML 解析器或 Oracle LOOP)。为什么 ?因为,同样,XML 与现实世界或数据库不匹配(而数据库与现实世界匹配)。 您需要的是普通的 SQL ACID 事务(在 Oracle 中,通常作为 proc 实现)。 XML 文件中有很多信息,尚未明确说明: 当有新订单或计量表进来时,您需要先IF NOT EXISTS ... INSERT,然后INSERTing 读数和样本 当 Order 或 Meter 中的 modifieddtdeleteddt 发生变化时,您需要UPDATE 这些列

同样,这就是我使用awk 解析 XML 文件并生成一系列正确、适用的 SQL 命令的原因。

【讨论】:

以上是关于PLSQL:将数据从 XML 解析并插入到表中的主要内容,如果未能解决你的问题,请参考以下文章

PLSQL 中的 XML 解析

PLSQL:将数据从一个表中插入可能的环境到另一个表,同时保持 from_table 名称动态

我必须从 xml 文件中提取数据并将其插入到表中

如何将 PHP 数据从数据库发送回 JQuery 并插入到表中

我想将表单中的数据插入到表中,然后从另一个表中选择另一个数据并使用 PHP 插入到第三个表中

Laravel错误将数据从另一个表插入到表中