如何连接t-sql中对等节点的值

How to connect the values of peer nodes in t-sql

declare @x as xml ='<root>
    <n1>hello world</n1>
    <n2>A0001</n2>
    <n2>A0002</n2>
    <n2>A0003</n2>
    <n2>A0004</n2>
</root>'

select xroot.value('(n1)[1]', 'varchar(255)') as n1,
       xroot.query('n2/text()') as n2
from @x.nodes('/root') as xmlt1(xroot)

本次查询结果为

n1          |  n2
hello world |  A0001A0002A0003A0004

但是我想要下面的结果,怎么写t-sql

n1          |  n2
hello world |  A0001,A0002,A0003,A0004

尝试以下操作。获取值然后连接它们:

declare @x as xml ='<root>
    <n1>hello world</n1>
    <n2>A0001</n2>
    <n2>A0002</n2>
    <n2>A0003</n2>
    <n2>A0004</n2>
</root>';

WITH DataSource (n1, n2, n2order)  AS
(
    select Tn1.c.value('local-name(.)', 'varchar(128)')
          ,Tn2.c.value('(.)[1]', 'varchar(128)')    
          ,ROW_NUMBER() OVER (ORDER BY Tn2.c ASC)
    from @x.nodes('root/n1') Tn1(c)
    CROSS APPLY @x.nodes('root/n2') Tn2(c)
)
SELECT DISTINCT DS1.n1
               ,DS.n2
FROM DataSource DS1
CROSS APPLY
(
    SELECT STUFF
    (
        (
            SELECT ',' + n2
            FROM DataSource DS2
            WHERE DS2.n1 = DS1.n1
            ORDER BY n2order
            FOR XML PATH(''), TYPE
        ).value('.', 'VARCHAR(MAX)')
        ,1
        ,1
        ,''
    )
) DS (n2);

如果您使用的是SQL Server 2017+,您可以使用:

WITH DataSource (n1, n2)  AS
(
    select Tn1.c.value('local-name(.)', 'varchar(128)')
          ,Tn2.c.value('(.)[1]', 'varchar(128)')
    from @x.nodes('root/n1') Tn1(c)
    CROSS APPLY @x.nodes('root/n2') Tn2(c)
)
SELECT n1
      ,STRING_AGG(n2, ',') as n2
FROM DataSource
GROUP BY n1;

但是这里节点的顺序在拼接的时候是不保证的

你可以试试这个:

declare @x as xml ='<root>
    <n1>hello world</n1>
    <n2>A0001</n2>
    <n2>A0002</n2>
    <n2>A0003</n2>
    <n2>A0004</n2>
</root>';

SELECT @x.value('(/root/n1/text())[1]','nvarchar(max)') AS n1
      ,@x.query('data(/root/n2)').value('.','nvarchar(max)');

可惜data()不允许指定分隔符。它永远是一片空白。但是你可以使用 REPLACE():

SELECT @x.value('(/root/n1/text())[1]','nvarchar(max)') AS n1
      ,REPLACE(@x.query('data(/root/n2)').value('.','nvarchar(max)'),' ',',');

倒退:如果您的值可能包含空白,这将失败...

您可以改用 XQuery:

SELECT @x.value('(/root/n1/text())[1]','nvarchar(max)') AS n1
      ,STUFF(
       @x.query('for $n2 in /root/n2/text()
                 return <x>{concat(",",$n2)}</x>').value('.','nvarchar(max)'),1,1,'');

在这种方法中,我们使用 FLWOR 查询 运行 到 <n2> 元素,并创建一个新的 XML,其中内容用逗号扩展:

<x>,A0001</x>
<x>,A0002</x>
<x>,A0003</x>
<x>,A0004</x>

我们可以将 读作一个 与 XQuery 路径 '.'。删除前导逗号使用 STUFF()(就像类似的字符串聚合方法一样)。我们也可以使用 XQuery sub-string()

SELECT @x.value('(/root/n1/text())[1]','nvarchar(max)') AS n1
      ,@x.query('for $n2 in /root/n2/text()
                 return <x>{concat(",",$n2)}</x>')
         .value('substring(.,2,1000)','nvarchar(max)')