سؤال

I am creating a table as follows:

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

I have a query which converts this into XML as :

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

I need to use the above query as a subquery to get the following result:

A           B
1           11
4           44
5           55

I am trying a query like this but it gives an error:

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 Cannot find either column "Z" or the user-defined function or aggregate "Z.Value", or the > name is ambiguous.

I can write a simple query like below to get the result but the requirement is that I have to convert it into XMl and then retrieve the same result from it using the subquery. Select * from test order by A

I know that I can insert the records returned by For XML in a table variable and then use Cross apply to fetch the result but as said above, I am looking to get this done in a single query without any temporary table or temporary variable.

هل كانت مفيدة؟

المحلول

There're a several issues here. First, your xml looks like this:

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

And you're trying to fetch data as attributes (@A, @B). You need to fetch it as elements (A[1] or (A/text())[1]).

Second, you have to use type keyword if you want your xml to be xml type. Third, to split data by rows you need nodes() function. So your query becomes:

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)

BTW, I'd better to use attributes, like this:

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

نصائح أخرى

You forgot TYPE mode (without it you get nvarchar instead of xml) and value keyword should be in lower-case.

Try this:

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)

But I would prefer generating XML without AUTO mode (your query would break down if you write dbo.Test instead of Test), in more declaretive way using PATH keyword:

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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top