【问题标题】:Xpath with XMLTABLEXpath 与 XMLTABLE
【发布时间】:2019-11-22 18:49:09
【问题描述】:

使用这个 XML :

<attrs>
    <attr multiple="true" name="LETTER">
        <string>A</string>
        <string>B</string>
        <string>C</string>
        <string>D</string>
    </attr>
    <attr multiple="true" name="NUMBER">
        <string>1</string>
        <string>2</string>
        <string>3</string>
        <string>4</string>
    </attr>
    ...
</attrs>

我正在尝试使用 XMLTABLE 获得此结果:

+------+--------+--------+
| REF  | LETTER | NUMBER |
+------+--------+--------+
| REF1 | A      |      1 |
| REF1 | B      |      2 |
| REF1 | C      |      3 |
| ...  | ...    |    ... |
+------+--------+--------+

我对 Xpath 很不满意,我被困在这里,但这个 concat all values :

XMLTABLE('/attrs'
    PASSING XMLTYPE(XML)
    COLUMNS LETTER VARCHAR2(50) PATH 'attr[@name="LETTER"]',
            NUMBER VARCHAR2(50) PATH 'attr[@name="NUMBER"]'
) X

谢谢。

【问题讨论】:

  • 请始终说明您正在使用的数据库引擎的确切版本。 (应该有标签,添加标签就足够了。)
  • 对不起,我刚刚添加了 oracle12c 标签
  • 你有固定数量的string元素(更容易)还是动态数量(更难;)

标签: sql oracle xpath oracle12c


【解决方案1】:

这里是需要固定数量的string 元素的更简单情况的解决方案。

第一步以转置表格中的形式获取数据

create table tab as 
with doc as (
select 
xmltype(q'{<attrs>
    <attr multiple="true" name="LETTER">
        <string>A</string>
        <string>B</string>
        <string>C</string>
        <string>D</string>
    </attr>
    <attr multiple="true" name="NUMBER">
        <string>1</string>
        <string>2</string>
        <string>3</string>
        <string>4</string>
    </attr>
    ...
</attrs>}') as doc from  DUAL)
select x.* from doc,
         XMLTable(
          'for $i in /attrs/attr    
           return $i'
          passing  (doc.doc)
          columns
                 col_name varchar2(10) path '//attr/@name',
                 s1 varchar2(10) path '//string[1]',
                 s2 varchar2(10) path '//string[2]',
                 s3 varchar2(10) path '//string[3]',
                 s4 varchar2(10) path '//string[4]'
                  ) x
;

COL_NAME   S1         S2         S3         S4        
---------- ---------- ---------- ---------- ----------
LETTER     A          B          C          D         
NUMBER     1          2          3          4   

这几乎是您所期望的,但必须转置。我确信有一个简单的方法,但是这个查询有效。应用 UNPIVOT 而不是 PIVOT 后跟列重命名。

with q1 as ( 
select * from tab 
UNPIVOT (
     x 
    FOR src 
    IN (
        s1 AS 'X', 
        s2 AS 'Y',
        s3 as 'Z',
        s4 as 'U'
    )
)),
q2 as (
select * from q1
PIVOT (max(x) "VAL"  for (col_name) in 
('LETTER' as "LETTER",
'NUMBER' as "NUMBER")
))
select LETTER_VAL as "LETTER", NUMBER_VAL as "NUMBER"
from q2
order by 1;

UNPIVOT的结果

COL_NAME   S X         
---------- - ----------
LETTER     X A         
LETTER     Y B         
LETTER     Z C         
LETTER     U D         
NUMBER     X 1         
NUMBER     Y 2         
NUMBER     Z 3         
NUMBER     U 4

PIVOT结果

S LETTER_VAL NUMBER_VAL
- ---------- ----------
X A          1         
Y B          2         
Z C          3         
U D          4  

最终结果

LETTER     NUMBER    
---------- ----------
A          1         
B          2         
C          3         
D          4 

【讨论】:

  • 非常感谢 Marmite Bomber,它正在工作 :) 非常棘手。
【解决方案2】:

谢谢 Marmite Bomber,这很棘手。

这是我使用的请求:

WITH Q1 AS (
    SELECT * FROM (
        SELECT X.*
        FROM PRODUCT P,
        XMLTABLE('for $i in /attrs/attr return $i'
            PASSING XMLTYPE(P.XML)
            COLUMNS ATTRIBUTENAME VARCHAR2(50) PATH '//@name',
                    S1 VARCHAR2(50) path '//string[1]',
                    S2 VARCHAR2(50) path '//string[2]',
                    S3 VARCHAR2(50) path '//string[3]',
                    S4 VARCHAR2(50) path '//string[4]',
                    S5 VARCHAR2(50) path '//string[5]',
                    S6 VARCHAR2(50) path '//string[6]',
                    S7 VARCHAR2(50) path '//string[7]',
                    S8 VARCHAR2(50) path '//string[8]',
                    S9 VARCHAR2(50) path '//string[9]',
                    S10 VARCHAR2(50) path '//string[10]'
        ) X
        WHERE X.ATTRIBUTENAME IN ('LETTER', 'NUMBER')
        AND P.REF = 'REF1')
    UNPIVOT (
        X
        FOR SRC 
        IN (
            S1 AS 'Q', 
            S2 AS 'R',
            S3 as 'S',
            S4 as 'T',
            S5 as 'U',
            S6 as 'V',
            S7 as 'W',
            S8 as 'X',
            S9 as 'Y',
            S10 as 'Z'
        )
    )
), Q2 AS (
    SELECT * FROM Q1
    PIVOT (
        MAX(X) "VAL" 
        FOR (ATTRIBUTENAME)
        IN (
            'LETTER' as "LETTER",
            'NUMBER' as "NUMBER"
        )
    )
) SELECT LETTER_VAL AS "LETTER", NUMBER_VAL AS "NUMBER"
FROM Q2
ORDER BY 1;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-15
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 2016-02-28
    • 2013-10-31
    • 1970-01-01
    相关资源
    最近更新 更多