【问题标题】:How to select values between a XML tag in SQL Query如何在 SQL Query 中的 XML 标记之间选择值
【发布时间】:2021-05-24 09:24:44
【问题描述】:

我有一个带有存储 XML 的 CLOB 列的表。 XML 的结构是不可读的。我想在<DOMAINID>等几个标签之间获取值;示例如下所示。

XML:

<ID>
  <DOMAIN>IND<DOMAIN>
  <DOMAINID>112AC<DOMAINID>
<ID>
  
<GROUP>
  <GP>ASIA<GP>
  <RSN>GOOD<RSN>
<GROUP>

我正在使用这个:

SELECT REGEXP_REPLACE(COL,'^.*<DOMAINID>(.*)</DOMAINID>.*$','\1',1,0,'mn') col1 FROM tab;

预期结果:

112AC

实际的 XML:

<?xml version="1.0" encoding="US-ASCII"?>
<GML:GMMessage 
   xmlns:GML="GML" 
   xmlns:GMLType="GML.Type" 
   
   xsi:schemaLocation="GML ../schema/gml..xsd" SchemaVersion="9.8">
    <BusinessHdr>
       <busHdr:BusObjectType>ABC</busHdr:BusObjectType>
       <busHdr:BusObjectOwner>HDHDH</busHdr:BusObjectOwner>
       <busHdr:BusObjectId>DJHDAHDAJHDA</busHdr:BusObjectId>
       <busHdr:BusObjectVersion>1</busHdr:BusObjectVersion>
      </BusinessHdr>
    <Transaction>
     <GenericEvent>NEW</GenericEvent>
     <Group>
        <GroupId>3424234</GroupId>
        <Reason>MANUAL</Reason>
    </Group>
    < xsi:type="mm:MMIam">
       <Id>
           <Domain>ssdsgdsg</Domain>
           <DomainId>123456ACC</DomainId>
           <Version>1</Version>
       </Id>
       <Date>2021-02-01</Date>
     </Transaction>
    </GML:GMMessage>

【问题讨论】:

    标签: sql regex xml oracle regexp-replace


    【解决方案1】:

    不要使用正则表达式解析 XML;使用适当的 XML 解析器。

    但是,您所拥有的 XML 格式不正确,因为它缺少根元素,并且您在所有结束标记中都缺少 /;所以你首先需要修复你的 XML 并给它一个根元素,然后你可以使用 XML 解析器来解析它。

    SELECT x.*
    FROM   table_name t
           CROSS APPLY XMLTABLE(
             '//root'
             PASSING XMLTYPE( '<root>' || t.data || '</root>' )
             COLUMNS
               domain   VARCHAR2(10) PATH './ID/DOMAIN',
               domainid VARCHAR2(10) PATH './ID/DOMAINID',
               gp       VARCHAR2(50) PATH './GROUP/GP',
               rsn      VARCHAR2(50) PATH './GROUP/RSN'
           ) x
    

    其中,对于样本数据:

    CREATE TABLE table_name ( data ) AS
    SELECT '<ID>
      <DOMAIN>IND</DOMAIN>
      <DOMAINID>112AC</DOMAINID>
    </ID>
    <GROUP>
      <GP>ASIA</GP>
      <RSN>GOOD</RSN>
    </GROUP>' FROM DUAL
    

    输出:

    域名 |域名 |全科医生 | RSN :----- | :------- | :--- | :--- IND | 112AC |亚洲 |好的

    如果你只想要一个值,那么你可以使用XMLQUERY:

    SELECT  XMLQUERY(
              '/root/ID/DOMAINID/text()'
              PASSING XMLTYPE( '<root>'||data||'</root>' )
              RETURNING CONTENT
            ) AS domainid
    FROM    table_name
    

    哪些输出:

    |域名 | | :------- | | 112AC |

    db小提琴here


    更新

    我将假设您的 XML 还定义了 xsibusHdr 命名空间(如果没有,那么 Oracle 将无法解析 XML,因为它不知道这些命名空间是什么);这会给你这个样本数据:

    CREATE TABLE table_name ( data ) AS
    SELECT '<?xml version="1.0" encoding="US-ASCII"?>
    <GML:GMMessage 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:GML="GML" 
       xmlns:busHdr="busHdr"
       xmlns:GMLType="GML.Type"
       
       xsi:schemaLocation="GML ../schema/gml.xsd busHdr ../schema/bushdr.xsd"
       SchemaVersion="9.8">
        <BusinessHdr>
           <busHdr:BusObjectType>ABC</busHdr:BusObjectType>
           <busHdr:BusObjectOwner>HDHDH</busHdr:BusObjectOwner>
           <busHdr:BusObjectId>DJHDAHDAJHDA</busHdr:BusObjectId>
           <busHdr:BusObjectVersion>1</busHdr:BusObjectVersion>
          </BusinessHdr>
        <Transaction>
         <GenericEvent>NEW</GenericEvent>
         <Group>
            <GroupId>3424234</GroupId>
            <Reason>MANUAL</Reason>
        </Group>
           <Id>
               <Domain>ssdsgdsg</Domain>
               <DomainId>123456ACC</DomainId>
               <Version>1</Version>
           </Id>
           <Date>2021-02-01</Date>
         </Transaction>
        </GML:GMMessage>' FROM DUAL
    

    然后,您只需要添加您正在使用的命名空间并将路径更新到新的(区分大小写)位置:

    SELECT x.*
    FROM   table_name t
           CROSS APPLY XMLTABLE(
             XMLNAMESPACES( 'GML' AS "GML" ),
             '//GML:GMMessage/Transaction'
             PASSING XMLTYPE( t.data )
             COLUMNS
               domain   VARCHAR2(10) PATH './Id/Domain',
               domainid VARCHAR2(10) PATH './Id/DomainId',
               version  NUMBER(3,0)  PATH './Id/Version',
               groupid  VARCHAR2(50) PATH './Group/GroupId',
               reason   VARCHAR2(50) PATH './Group/Reason',
               dt       DATE         PATH './Date'
           ) x
    

    输出:

    域名 |域名 |版本 |组ID |原因 | DT :------- | :-------- | ------: | :-------- | :----- | :-------- ssdsgdsg | 123456ACC | 1 | 3424234 |手册 | 01-FEB-21

    db小提琴here

    【讨论】:

    • 感谢您的回答,但是这不起作用,因为我有 Oracle 11g。它给了ORA-00905: missing keyword。你能建议吗?
    • @vnsingh 在 Oracle 11g 上,使用 CROSS JOIN 而不是 CROSS APPLY db<>fiddle
    • 我不确定我需要放什么路径,我的 XML 看起来像
    • 感谢@MTO。由于数据库有问题,所以不适合我,但这看起来很有希望,
    【解决方案2】:

    很高兴看到你的想法与你的方法......

    建议查看这个工具(如果你没有类似的工具)来帮助你使用正则表达式https://regexr.com/,对我有很大帮助。

    您的 SQL 看起来是正确的(使用“m”和“n”标志进行多行),但不确定是否是您的 XML 输入错误,因为您的正则表达式字符串不适用于您的 XML已粘贴,但如果是 XML,我确实可以使用它。

    您当前的 SQL 输出是什么?您可能需要使用$1 代替您的\1

    我也建议

    1. 也许还逃避了你的正斜杠,因为这可能是你的罪魁祸首。
    2. 为您的捕获添加更多特异性,以防止您的搜索变得贪婪。
    SELECT REGEXP_REPLACE(COL,'^.*<DOMAINID>([0-9A-z]+)<\/DOMAINID>.*$','$1',1,0,'mn') col1 FROM tab;
    

    【讨论】:

    • 这里只是得到数字部分,但缺少字母。你能检查一下吗?获取112 但需要112AC
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 2017-07-14
    • 2018-12-14
    • 2012-04-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多