【问题标题】:How can one extract data into columns from nested XML in a CLOB using SQL?如何使用 SQL 从 CLOB 中的嵌套 XML 中将数据提取到列中?
【发布时间】:2015-05-21 07:21:11
【问题描述】:

在 Oracle 11g 中工作并使用 SQL 函数(不是 PL/SQL、Java 等),我想从限制为 4000 个字符的 CLOB 字段中将嵌套的 XML 数据提取到列中。

最初,我开发了一个解决方案,它使用 REGEXP_SUBSTR 和 CONNECTED BY LEVEL 来提取和扩展数据。这种方法适用于有限的样本数据集,但当应用于更大的数据集时,它花费了非常长的时间(几个小时)并且没有返回。 (我认为这是因为它会生成一个交叉连接并在之后过滤结果?)

现在我有了另一种使用 XMLTYPE 和 XMLTABLE 的解决方案,它适用于小型和大型集合,但有一个重要警告:数据集中的任何无效 XML 条目都会导致整个查询失败。在大型数据集中,我只在 CLOB 字段中文本的 LENGTH 等于 4000 的情况下观察到这种情况。在这种情况下,超过 4000 个字符的字符串被插入到 CLOB 中并被截断,从而破坏了XML 结构。因此,XMLTYPE 转换在这些行上失败并以错误结束查询。我有一个粗略的解决方法,它使用 WHERE 子句过滤掉 CLOB >= 4000 长度的行,以避免该特定错误。但这种方法显然不允许提取这些行中的可用数据,更重要的是,它暗示了依赖于仅包含有效 XML 的输入的解决方案的脆弱性。下面包含此方法的示例查询,可以在以下链接中找到 SQL Fiddle:http://sqlfiddle.com/#!4/d41d8/40794/0

SELECT
            orig_string.message_seq_id,
            xml_layer_1.unit_srl,
            xml_layer_2.AB_Type

FROM        (

    SELECT
                xml.message_seq_id,
                XMLTYPE(
                    xml.xml_messg_text
                ) AS xml_messg_text

    FROM        (

        SELECT 1001 AS message_seq_id, RPAD(Q'#<location_data unit_srl="MLRR-1" time_marker="02-04-2015 21:54:42" location_number="2335350"><RunData AB_Type="Rezoned" mr_cod="9982349482-2422422234" Timing="02-04-2015 21:54:42" Handle="L1L_upperband" LongCount="82742288322983299222759200000004" ClassTypeTest="HUCOHOTMMMWMM" Boc="242242" Closeout="LongOp" Founding="14" init_link="lls://rtg.ootir.orzz/prp"/>#', 3888, ' ') || Q'#<RunData AB_Type="Rezoned" mr_cod="9834398399934-39493943-###-09923049,0003-002,12" Timing="02-04-2015 21:54:42"#' AS xml_messg_text, TO_DATE('2015-02-04 21:06:00', 'YYYY-MM-DD HH24:MI:SS') AS create_date  FROM DUAL   UNION ALL
        SELECT 1002 AS message_seq_id, Q'#<location_data unit_srl="UNTOUT-3" time_marker="02-05-2015 10:18:34" location_number="23422"><RunData AB_Type="Rezoned" mr_cod="9234092349,03940388,748209,23" Timing="02-05-2015 10:18:34" Handle="L1L_upperband" ClassTypeTest="URCHTPH" Boc="3245" Closeout="LongOp" Founding="21" init_link="lls://rtg.ootir.orzz/prp"/><RunData AB_Type="Rezoned" mr_cod="93488284983292492394242-####-0200234" Timing="02-05-2015 10:18:34" Handle="L1L_upperband" ClassTypeTest="BERDBP" Boc="6445" Closeout="LongOp" Founding="9453" init_link="lls://rtg.ootir.orzz/prp"/><RunData AB_Type="Lopped" mr_cod="4537747565656565" Timing="02-05-2015 10:18:34" Handle="L1L_upperband" ClassTypeTest="HIUFDDU" Boc="3324" Closeout="LongOp" Founding="29" init_link="lls://rtg.ootir.orzz/prp"/></location_data>#' AS xml_messg_text, TO_DATE('2015-02-05 09:31:23', 'YYYY-MM-DD HH24:MI:SS') AS create_date  FROM DUAL   UNION ALL
        SELECT 1003 AS message_seq_id, Q'#<location_data unit_srl="LURET-22" time_marker="02-04-2015 15:34:55" location_number="1135461"><RunData Timing="02-04-2015 15:34:55" Boc="5644" Closeout="ChpstHousTul" init_link="lls://rtg.ootir.orzz/prp" Dest=""/></location_data>#' AS xml_messg_text, TO_DATE('2015-02-04 16:24:42', 'YYYY-MM-DD HH24:MI:SS') AS create_date  FROM DUAL   UNION ALL
        SELECT 1004 AS message_seq_id, Q'#<location_data unit_srl="GBT-14" time_marker="02-04-2015 15:31:27" location_number="4395822"><RunData AB_Type="Rezoned" mr_cod="5948573853085309485938094853098345" Timing="02-04-2015 15:31:27" Handle="L1L_upperband" ClassTypeTest="BTTXRCH" Boc="232" Closeout="LongOp" Founding="3" init_link="lls://rtg.ootir.orzz/prp"/></location_data>#' AS xml_messg_text, TO_DATE('2015-02-04 16:35:37', 'YYYY-MM-DD HH24:MI:SS') AS create_date  FROM DUAL

    ) xml

    WHERE       LENGTH(xml.xml_messg_text) < 4000

) orig_string,

XMLTABLE(
    '/location_data'
    PASSING orig_string.xml_messg_text
    COLUMNS
        unit_srl VARCHAR2(15) PATH '@unit_srl',
        OBJECT XMLTYPE PATH 'RunData'
) xml_layer_1,

XMLTABLE(
    '/RunData'
    PASSING xml_layer_1.object
    COLUMNS
        AB_Type VARCHAR2(40) PATH '@AB_Type'
) xml_layer_2
;

请注意,message_seq_id = 1001 是问题行,如果将 WHERE 子句注释掉,则查询会导致错误。

由于我对数据库的访问是只读的,我将无法添加表约束或以其他方式排除格式错误的数据,因此任何解决方案都需要能够按原样处理数据。

如何解决 XMLTYPE 转换问题,以便所有行都可以访问,并且即使查询包含无效的 XML,也不会导致查询以错误结尾?

是否有比上述两种方法更可靠的数据提取方法?

【问题讨论】:

  • 也许坏数据不应该从一开始就进入数据库?
  • @OldProgrammer 这也是我的偏好,但这是我无法控制的客户端系统的一个方面。

标签: sql oracle oracle11g


【解决方案1】:

这不是对您最初问题的回答,而是对您对@OldProgrammer 的评论的回复,只是它太长/太复杂,无法放入评论中。

如果你不能让客户端系统停止传入坏数据,也许你至少可以在表上设置一个约束,以防止坏数据被添加。此外,前端产生的错误实际上可能会迫使客户端系统编码人员处理他们的问题......

例如:

create table test1 (col1 clob);

alter table test1 add constraint t1_chk check (col1 is null or length(xmltype(col1)) > 0);

-- works
insert into test1 values (null);

-- works
insert into test1 values ('<root><a>fred</a></root>');

-- fails to insert
insert into test1 values ('<root><a>fred</a>');

commit;

drop table test1;

【讨论】:

  • 我喜欢你对此的看法。不幸的是,我可能无法进行更改,因为我对数据库的访问是(并且可能会保持)只读的。我会更改问题以说明这一点。
  • 嗯,我能想到的唯一其他选择是有一个名为“valid_xml”之类的函数,然后该函数将 clob 转换为 xmltype,如果有效则返回 xml , 但如果不是,则为 null - 这样您可以确保您需要查询的列中的 xml 是有效的。但是,我不希望它很快,而且它需要有人在数据库上创建函数(除非你在 12c 上,也许,我可以看到你不是)。
  • 如果我无法找到仅 SQL 的解决方案,我会研究该选项,并且当数据库更新到 12c 时,我一定会记住这个选项。跨度>
猜你喜欢
  • 2014-01-04
  • 2012-11-16
  • 2016-07-19
  • 2015-09-08
  • 2014-04-09
  • 2019-07-29
  • 2015-06-12
  • 2019-10-06
  • 1970-01-01
相关资源
最近更新 更多