如何使用SQL从CLOB中的嵌套XML中将数据提取到列中?

如何使用SQL从CLOB中的嵌套XML中将数据提取到列中?,sql,oracle,oracle11g,Sql,Oracle,Oracle11g,在Oracle11g中工作并使用SQL函数(而不是PL/SQL、Java等),我想从限制为4000个字符的CLOB字段中将嵌套的XML数据提取到列中 最初,我开发了一个解决方案,使用REGEXP_SUBSTR和CONNECTED BY LEVEL来提取和扩展数据。这种方法只适用于有限的数据样本集,但当应用于更大的数据集时,耗时数小时且无法返回。我假设这是因为它生成了一个交叉连接,然后过滤结果 现在,我有了另一个使用XMLTYPE和XMLTABLE的解决方案,它适用于小集合和大集合,但有一个重要的

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

最初,我开发了一个解决方案,使用REGEXP_SUBSTR和CONNECTED BY LEVEL来提取和扩展数据。这种方法只适用于有限的数据样本集,但当应用于更大的数据集时,耗时数小时且无法返回。我假设这是因为它生成了一个交叉连接,然后过滤结果

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

请注意,message_seq_id=1001是问题所在行,如果WHERE子句被注释掉,查询结果将出错

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

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


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

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

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

例如:


也许坏数据不应该首先进入数据库?@OldProgrammer这也是我的偏好,但这是客户端系统的一个方面,我无法控制。我喜欢你在这方面的想法。不幸的是,我可能无法进行更改,因为我对数据库的访问是只读的,而且很可能仍然是只读的。我将修改这个问题以注意到。嗯,我唯一能想到的另一个选项是使用一个名为valid_xml的函数,然后该函数将clob转换为xmltype,如果有效,则返回xml,如果无效,则返回null-这样可以确保需要查询的列中的xml有效。但是,我不希望它很快,而且它需要有人在数据库上创建函数,除非你在12c上,也许,我可以看到你不是。我会研究这个选项,以防我找不到纯SQL的解决方案,并且当数据库更新到12c时,我一定会记住这个选项。
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
;
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;