如何在 SQL 查询中的 XML 标记之间 select 值

How to select values between a XML tag in SQL Query

我有一个 table,其中 CLOB 列存储一个 XML。 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,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>

很高兴看到你用你的方法思考...

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

你 SQL 看起来是正确的(使用多行的“m”和“n”标志),但不确定你的 XML 是否输入错误,因为你是正则表达式字符串在您粘贴的 XML 上不起作用,但如果它是 XML.

我确实让它起作用了

您的 SQL 当前的输出是什么?您可能需要使用 </code> 代替 <code>.

我也建议

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

不要使用正则表达式解析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

输出:

DOMAIN | DOMAINID | GP   | RSN 
:----- | :------- | :--- | :---
IND    | 112AC    | ASIA | GOOD

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

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

输出:

| DOMAINID |
| :------- |
| 112AC    |

db<>fiddle 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

输出:

DOMAIN   | DOMAINID  | VERSION | GROUPID | REASON | DT       
:------- | :-------- | ------: | :------ | :----- | :--------
ssdsgdsg | 123456ACC |       1 | 3424234 | MANUAL | 01-FEB-21

db<>fiddle here