XML Oracle:标识现有但为空的元素

时间:2019-02-13 11:01:51

标签: sql xml oracle

我是使用Oracle的xml DB的新手,我开始了解它,但是有一件重要的事情我无法确定:我需要确定 1.如果一个元素存在 2.如果为空

不幸的是XMLExists()只是混合了两个答案。

查询的一小部分:

SELECT case when XMLEXISTS('/' passing by ref c3.CLASS) then 1 else 0 end E_CLASS,        
       c3.CLASS      
FROM XML_TEST x,
   XMLTABLE ('/Data/EMPLOYER'
                    PASSING x.File_XML
                    COLUMNS DOSSIER           NUMBER(8)       PATH     'DOSSIER',
                            SUMMARY           XMLTYPE         PATH 'SUMMARY'
            ) e,
   XMLTABLE ('/SUMMARY'
                PASSING e.SUMMARY
                COLUMNS BEGINDATE          DATE            PATH 'BEGINDATE',                            
                        WORKER             XMLTYPE         PATH 'WORKER'
            ) c1,
   XMLTABLE ('/WORKER'
                PASSING c1.WORKER
                COLUMNS NRWORKER           NUMBER(7)       PATH 'NRWORKER',                            
                        RESULT_DETAIL      XMLTYPE         PATH 'RESULT_DETAIL'
            ) c2 ,
    XMLTABLE ('/RESULT_DETAIL'
                PASSING c2.RESULT_DETAIL
                COLUMNS CODE               CHAR(5)         PATH 'CODE',                            
                        MINUTES            NUMBER(5)       PATH 'MINUTES',                            
                        CLASS              CHAR(1)         PATH 'CLASS'                            
            ) c3 ;

2 个答案:

答案 0 :(得分:0)

XMLEXISTS确实为空元素返回true,您只是向其发送c3.CLASS,它是CHAR(1)列而不是XMLTYPE,因此它没有检测根元素(/)并返回false。

这是您可以使用此方法的不同示例。我将CLASS的XMLTYPE副本添加到c3,并在SELECT子句中引用了它。尝试从示例数据或整个CLASS节点中删除“ z”,然后看看会发生什么。

请参见how to write an XPATH query to check if a node is empty的类似问题。

-- sample data
with xml_test as (select xmltype('<Data><EMPLOYER><SUMMARY><WORKER><RESULT_DETAIL><CLASS>z</CLASS></RESULT_DETAIL></WORKER></SUMMARY></EMPLOYER></Data>') as file_xml from dual)
-- your query
SELECT --case when XMLEXISTS('/' passing by ref c3.class) then 1 else 0 end E_CLASS, /* won't work if CLASS is non-empty */
       case when XMLEXISTS('/' passing by ref c3.class_x) then 1 else 0 end E_CLASS_X,
       case when XMLEXISTS('/RESULT_DETAIL/CLASS' passing by ref c2.RESULT_DETAIL) then 1 else 0 end E_RD_CLASS,
       c3.CLASS, 
       c3.CLASS_X, 
       -- but this probably does what you want, detect if CLASS exists and is empty:
       case when XMLEXISTS('/CLASS' passing by ref c3.class_x)
             and not XMLEXISTS('/CLASS/text()' passing by ref c3.class_x) then 1 else 0 end EMPTY_CLASS
FROM XML_TEST x,
   XMLTABLE ('/Data/EMPLOYER'
                    PASSING x.File_XML
                    COLUMNS DOSSIER           NUMBER(8)       PATH     'DOSSIER',
                            SUMMARY           XMLTYPE         PATH 'SUMMARY'
            ) e,
   XMLTABLE ('/SUMMARY'
                PASSING e.SUMMARY
                COLUMNS BEGINDATE          DATE            PATH 'BEGINDATE',                            
                        WORKER             XMLTYPE         PATH 'WORKER'
            ) c1,
   XMLTABLE ('/WORKER'
                PASSING c1.WORKER
                COLUMNS NRWORKER           NUMBER(7)       PATH 'NRWORKER',                            
                        RESULT_DETAIL      XMLTYPE         PATH 'RESULT_DETAIL'
            ) c2 ,
    XMLTABLE ('/RESULT_DETAIL'
                PASSING c2.RESULT_DETAIL
                COLUMNS CODE               CHAR(5)         PATH 'CODE',                            
                        MINUTES            NUMBER(5)       PATH 'MINUTES',                            
                        CLASS              CHAR(1)         PATH 'CLASS',
                        CLASS_X            XMLTYPE         PATH 'CLASS'  -- added an XMLTYPE column with the same data                          
            ) c3 ;

答案 1 :(得分:0)

您可以使用//[not(text())][not(*)]find elements without any text and without children

因此,在您的上下文中,您可以使用:

CASE
WHEN c2.RESULT_DETAIL IS NULL
     OR
     XMLEXISTS(
       '/RESULT_DETAIL/CLASS[not(text())][not(*)]'
       PASSING c2.RESULT_DETAIL
     )
THEN 1
ELSE 0
END AS isClassEmpty

您还可以LEFT OUTER JOIN您的表,以便仍然出现缺少的元素,然后可以测试CLASS的值是否为NULL

Oracle设置

CREATE TABLE XML_TEST ( File_XML ) AS
SELECT XMLTYPE( '<Data>
  <EMPLOYER>
    <DOSSIER>1</DOSSIER>
    <SUMMARY>
      <BEGINDATE>2019-02-13</BEGINDATE>
      <WORKER>
        <NRWORKER>42</NRWORKER>
        <RESULT_DETAIL>
          <CODE>12345</CODE>
          <MINUTES>0</MINUTES>
          <CLASS></CLASS>
        </RESULT_DETAIL>
      </WORKER>
    </SUMMARY>
  </EMPLOYER>
</Data>' ) FROM DUAL UNION ALL
SELECT XMLTYPE( '<Data>
  <EMPLOYER>
    <DOSSIER>2</DOSSIER>
    <SUMMARY>
      <BEGINDATE>2019-02-14</BEGINDATE>
      <WORKER>
        <NRWORKER>1</NRWORKER>
        <RESULT_DETAIL>
          <CODE>98765</CODE>
          <MINUTES>600</MINUTES>
          <CLASS>B</CLASS>
        </RESULT_DETAIL>
      </WORKER>
    </SUMMARY>
  </EMPLOYER>
</Data>' ) FROM DUAL UNION ALL
SELECT XMLTYPE( '<Data>
  <EMPLOYER>
    <DOSSIER>3</DOSSIER>
    <SUMMARY>
      <BEGINDATE>2019-02-14</BEGINDATE>
      <WORKER>
        <NRWORKER>7</NRWORKER>
      </WORKER>
    </SUMMARY>
  </EMPLOYER>
</Data>' ) FROM DUAL

查询

SELECT Dossier,
       BeginDate,
       NRWorker,
       Code,
       Minutes,
       Class,
       CASE
       WHEN c2.RESULT_DETAIL IS NULL
            OR
            XMLEXISTS(
              '/RESULT_DETAIL/CLASS[not(text())][not(*)]'
              PASSING c2.RESULT_DETAIL
            )
       THEN 1
       ELSE 0
       END AS isClassEmpty,
       CASE WHEN Class IS NULL THEN 1 ELSE 0 END AS isClassEmpty2
FROM XML_TEST x
     LEFT OUTER JOIN
     XMLTABLE(
       '/Data/EMPLOYER'
       PASSING x.File_XML
       COLUMNS DOSSIER  NUMBER(8) PATH 'DOSSIER',
               SUMMARY  XMLTYPE   PATH 'SUMMARY'
     ) e
     ON ( 1 = 1 )
     LEFT OUTER JOIN
     XMLTABLE(
       '/SUMMARY'
       PASSING e.SUMMARY
       COLUMNS BEGINDATE DATE    PATH 'BEGINDATE',                            
               WORKER    XMLTYPE PATH 'WORKER'
     ) c1
     ON ( 1 = 1 )
     LEFT OUTER JOIN
     XMLTABLE(
       '/WORKER'
       PASSING c1.WORKER
       COLUMNS NRWORKER      NUMBER(7) PATH 'NRWORKER',                            
               RESULT_DETAIL XMLTYPE   PATH 'RESULT_DETAIL'
     ) c2
     ON ( 1 = 1 )
     LEFT OUTER JOIN
     XMLTABLE(
       '/RESULT_DETAIL'
       PASSING c2.RESULT_DETAIL
       COLUMNS CODE    CHAR(5)   PATH 'CODE',                            
               MINUTES NUMBER(5) PATH 'MINUTES',                            
               CLASS   CHAR(1)   PATH 'CLASS'                          
     ) c3
     ON ( 1 = 1 );

输出

DOSSIER | BEGINDATE | NRWORKER | CODE  | MINUTES | CLASS | ISCLASSEMPTY | ISCLASSEMPTY2
------: | :-------- | -------: | :---- | ------: | :---- | -----------: | ------------:
      1 | 13-FEB-19 |       42 | 12345 |       0 | null  |            1 |             1
      2 | 14-FEB-19 |        1 | 98765 |     600 | B     |            0 |             0
      3 | 14-FEB-19 |        7 | null  |    null | null  |            1 |             1

db <>提琴here