从For XML子查询中检索数据

时间:2013-10-05 06:59:47

标签: sql sql-server xml xpath sqlxml

我正在创建一个表格如下:

CREATE TABLE dbo.Test
(
  A int,
  B int
)

GO

INSERT INTO Test VALUES (1, 11)
GO

INSERT INTO Test VALUES (5, 55)
GO

INSERT INTO Test VALUES (4, 44)
GO

我有一个查询将其转换为XML格式:

  SELECT A,B
  FROM Test
  ORDER BY A
  FOR XML AUTO, ROOT ('myroot'), ELEMENTS

我需要使用上面的查询作为子查询来获得以下结果:

A           B
1           11
4           44
5           55

我正在尝试这样的查询,但它会出错:

SELECT Z.Value('@A', 'INT'),
   Z.Value('@B', 'INT')
FROM (SELECT A, B
  FROM Test
  ORDER BY A
  FOR XML AUTO,Elements, ROOT ('myroot')) Doc(Z)
  

Msg 4121,Level 16,State 1,Line 1   找不到列“Z”或用户定义的函数或聚合“Z.Value”,或者>名字含糊不清。

我可以编写一个简单的查询来获取结果,但要求是我必须将其转换为XMl,然后使用子查询从中检索相同的结果。 Select * from test order by A

我知道我可以在表变量中插入For XML返回的记录,然后使用Cross apply来获取结果,但如上所述,我希望在没有任何临时表或临时表的单个查询中完成此操作变量

2 个答案:

答案 0 :(得分:1)

这里有几个问题。首先,你的xml看起来像这样:

<myroot>
    <Test>
         <A>1</A><B>11</B>
    </Test>
    <Test>
         <A>4</A><B>44</B>
    </Test>
    <Test>
         <A>5</A><B>55</B>
    </Test>
</myroot>

您尝试将数据作为属性(@A@B)获取。您需要将其作为元素(A[1](A/text())[1])获取。

其次,如果您希望type为xml类型,则必须使用xml关键字。 第三,要按行拆分数据,需要nodes()功能。所以你的查询变为:

select
   D.Z.value('(A/text())[1]', 'int'),
   D.Z.value('(B/text())[1]', 'int')
from (
   select A, B
   from Test
   order by A
   for xml auto, elements, root('myroot'), type
) as Doc(Z)
    outer apply Doc.Z.nodes('myroot/Test') as D(Z)
顺便说一句,我最好使用属性,比如:

select
   D.Z.value('@A', 'int'),
   D.Z.value('@B', 'int')
from (
   select A, B
   from Test
   order by A
   for xml raw('Test'), root('myroot'), type
) as Doc(Z)
    outer apply Doc.Z.nodes('myroot/Test') as D(Z)

<强> sql fiddle demo

答案 1 :(得分:0)

您忘记了TYPE模式(没有它你得到的是nvarchar而不是xml),而value关键字应该是小写的。

试试这个:

SELECT Z.Z.value('@A', 'INT'),
    Z.Z.value('@B', 'INT')
FROM (
    SELECT A, B
    FROM Test
    ORDER BY A
    FOR XML AUTO, ROOT ('myroot'), TYPE
) Doc(Doc)
CROSS APPLY Doc.nodes('/myroot/Test')Z(Z)

但我更倾向于在没有AUTO模式的情况下生成XML(如果您编写dbo.Test而不是Test,则查询会中断),使用PATH关键字以更加声明的方式:

SELECT Z.Z.value('@A', 'INT'),
    Z.Z.value('@B', 'INT')
FROM (
    SELECT A AS '@A', B AS '@B'
    FROM dbo.Test
    ORDER BY A
    FOR XML PATH('Test'), ROOT ('myroot'), TYPE
) Doc(Doc)
CROSS APPLY Doc.nodes('/myroot/Test')Z(Z)