如何连接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)')
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)')