2014-01-19 44 views
4

我花了几天的时间寻找简单的解决方案来解决以下问题,我需要一些帮助。我有一个有两列的Oracle表,recid(Account Number)作为主键,xmlrecord存储所有的xml数据。我试图使用SQL查询为我们的应用程序导出我们有多值项目的值。排除数据损坏时,如果存在c1 m =“1”等,则始终存在相应的c2 m =“1”和c3 m =“1”。该表太大而无法多次提取每个项目,因此我需要在行的一次访问中将它们全部从xmlrecord中提取出来。我尝试了内部连接(1 = 1)和xmltables,但总是以返回的数据或新行中的每个新匹配结束NULLS。来自多个元素的Oracle SQL extractvalue

RECID    XMLRECORD 
----------------------------------- 
0000001   <row><c1>test</c1><c2>test2</c2>....</row> 
0000002   <row><c1>test</c1><c2>test2</c2>....</row> 

上面记录将作为工作有罚款:从顶层提取值不为我在这种情况下,由于XML的结构

我们的基表的数据结构工作没有多个有价值的领域。当我挣扎是当存储在XMLRecord数据就像下面:

<row> 
    <c1>test1</c1> 
    <c1 m=1>test1_2</c1> 
    <c2>test2</c2> 
    <c2 m=1>test2_2</c2> 
    <c3>test3</c3> 
    <c3 m=1>test3_2</c3> 
</row> 

我想输出的格式如下:

RECID  Col1  Col2  Col3 
----------------------------------- 
0000003  test1 test2 test3 
0000003  test1_2 test2_2 test3_2 
0000004  test1 test2 test3 
0000004  test1_2 test2_2 test3_2 
+4

问题是,您的XML结构是borked。在同一个环境中名称完全相同的两个不同元素不可避免地会导致问题。 – APC

回答

1

谢谢你的意见,但我设法通过构建一个适用于此实例的联接来获得我需要的解决方案。关于它的好处是,无论供应商向我们提供多少条记录,它都可以工作。在某些情况下,“m”属性最多运行9或10.

我在(1 = 1)上使用了通常的内连接,并基于动态ID构建了后续连接。 第一行ID_NUM的结果是“c”,下一行是“c2”,依此类推。

SELECT 
    t.recid 
    ,t2.VALUE1 
    ,t3.VALUE2 
    ,t4.VALUE3 
FROM t 
INNER JOIN XMLTABLE('/row/c1' 
    PASSING t.xmlrecord 
    ID_NUM VARCHAR(4) path 'concat(substring(ancestor-or-self::*/name(.),1,1), @m)', 
    VALUE1 VARCHAR(20) path '.') t2 
ON (1=1) 
INNER JOIN XMLTABLE('/row/c2' 
    PASSING t.xmlrecord 
    ID_NUM VARCHAR(4) path 'concat(substring(ancestor-or-self::*/name(.),1,1), @m)', 
    VALUE2 VARCHAR(20) path '.') t3 
ON (t2.ID_NUM=t3.ID_NUM) 
INNER JOIN XMLTABLE('/row/c3' 
    PASSING t.xmlrecord 
    ID_NUM VARCHAR(4) path 'concat(substring(ancestor-or-self::*/name(.),1,1), @m)', 
    VALUE3 VARCHAR(20) path '.') t4 
ON (t2.ID_NUM=t4.ID_NUM) 
+0

XMLTABLE救援!我可惜有一天来维护这个数据库的人:-)。但有时管理层会听到“XML”,并认为“噢,我们应该使用它!” –

0

您应该能够使用extractValue一起( )使用XPATH查询来选择基于属性的元素,就像这样。

SELECT RECID 
    , EXTRACTVALUE(XMLRECORD, '/row/c1[@m=''1'']') 
    , EXTRACTVALUE(XMLRECORD, '/row/c2[@m=''1'']') 
    , EXTRACTVALUE(XMLRECORD, '/row/c3[@m=''1'']') 
FROM T 

然后你可以UNION ALL这个结果与

SELECT RECID 
    , EXTRACTVALUE(XMLRECORD, '/row/c1[not(@m)]') 
    , EXTRACTVALUE(XMLRECORD, '/row/c2[not(@m)]') 
    , EXTRACTVALUE(XMLRECORD, '/row/c3[not(@m)]') 
FROM T 

您可以继续为具有多个属性,可能的行数工会。

我不认为这将是一个完整的扫描表很容易做到的,因为您试图为您选择的每个单独的行生成多行。

这是一个很好的例子,说明为什么在关系数据库中存储XML是一个非常糟糕的主意。

我试图想出一种方法来做到这一点与XMLTABLE,我会更新答案,如果我想办法。