从 SQL 服务器中的行连接
Concatenate from rows in SQL server
我想连接多行
Table:
|id |Attribute |Value |
|--------|------------|---------|
|101 |Manager |Rudolf |
|101 |Account |456 |
|101 |Code |B |
|102 |Manager |Anna |
|102 |Cardno |123 |
|102 |Code |B |
|102 |Code |C |
我要找的结果是:
|id |Manager|Account|Cardno|Code |
|--------|-------|-------|------|----------|
|101 |Rudolf |456 | |B |
|102 |Anna | |123 |B,C |
我有以下来自 的代码:
select
p.*,
a.value as Manager,
b.value as Account,
c.value as Cardno
from table1 p
left join table2 a on a.id = p.id and a.attribute = 'Manager'
left join table2 b on b.id = p.id and b.attribute = 'Account'
left join table2 c on c.id = p.id and b.attribute = 'Cardno'
但是,对于 ID# 102 的 Code
属性,它失败了,其中存在 B
和 C
值。
我如何更新它以在同一结果中包含这两个值?
UPD:“STRING_AGG 仅服务器 2017+”
您可以使用 CTE 和 STRING_AGG 函数解决此任务,例如:
declare
@t table (id int, Attribute varchar (100), [Value] varchar (100) )
insert into @t
values
(101, 'Manager', 'Rudolf'),
(101, 'Account', '456'),
(101, 'Code', 'B'),
(102, 'Manager', 'Anna'),
(102, 'Cardno', '123'),
(102, 'Code', 'B'),
(102, 'Code', 'C')
;with cte as
(
select id, Attribute
,STRING_AGG([Value], ', ') WITHIN GROUP (ORDER BY ID ASC) AS [Value]
from @t
group by ID, Attribute
)
select
max(p.ID) ID
,a.Value Manager
,isnull(b.Value, '') Account
,isnull(c.Value, '') Cardno
,isnull(e.Value, '') Code
from cte p
left join cte a on a.id =p.ID and a.attribute = 'Manager'
left join cte b on b.id = p.id and b.attribute = 'Account'
left join cte c on c.id = p.id and c.attribute = 'Cardno'
left join cte e on e.id = p.id and e.attribute = 'Code'
group by p.ID, a.Value,b.Value,c.Value,e.Value
通过 XML 和 XQuery 的另一种方法。
适用于 SQL Server 2008 以上版本。
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT, attribute VARCHAR(20), [Value] VARCHAR(30));
INSERT INTO @tbl (ID, attribute, Value) VALUES
(101,'Manager','Rudolf'),
(101,'Account','456'),
(101,'Code','B'),
(102,'Manager','Anna'),
(102,'Cardno','123'),
(102,'Code','B'),
(102,'Code','C');
-- DDL and sample data population, end
;WITH rs AS
(
SELECT ID, (
SELECT *
FROM @tbl AS c
WHERE c.id = p.id
FOR XML PATH('r'), TYPE, ROOT('root')
) AS xmldata
FROM @tbl AS p
GROUP BY id
)
SELECT ID
, COALESCE(xmldata.value('(/root/r[attribute="Manager"]/Value/text())[1]','VARCHAR(30)'),'') AS Manager
, COALESCE(xmldata.value('(/root/r[attribute="Account"]/Value/text())[1]','VARCHAR(30)'),'') AS Account
, COALESCE(xmldata.value('(/root/r[attribute="Cardno"]/Value/text())[1]','VARCHAR(30)'),'') AS Cardno
, COALESCE(REPLACE(xmldata.query('data(/root/r[attribute="Code"]/Value)').value('.', 'VARCHAR(MAX)'), SPACE(1), ','),'') AS Code
FROM rs
ORDER BY ID;
输出
+-----+---------+---------+--------+------+
| ID | Manager | Account | Cardno | Code |
+-----+---------+---------+--------+------+
| 101 | Rudolf | 456 | | B |
| 102 | Anna | | 123 | B,C |
+-----+---------+---------+--------+------+
如果您使用的是 SQL SERVER 2017 或更高版本,那么 string_agg()
和 PIVOT()
将易于使用,但在性能解决方案中速度更快 (Query#1
)。
如果您使用的是旧版本的 SQL 服务器,则使用 Query#2
和 STUFF()
以及 XML PATH FOR()
将值与 PIVOT()
[= 连接起来23=]
架构:
create table table1 (id int, Attribute varchar(50) , Value varchar(50));
insert into table1 values(101 ,'Manager' ,'Rudolf');
insert into table1 values(101 ,'Account' ,'456');
insert into table1 values(101 ,'Code' ,'B');
insert into table1 values(102 ,'Manager' ,'Anna');
insert into table1 values(102 ,'Cardno' ,'123');
insert into table1 values(102 ,'Code' ,'B');
insert into table1 values(102 ,'Code' ,'C');
GO
查询#1 PIVOT() 与 STRING_AGG():
select *
from
(
select t1.id,t1.attribute,
string_agg(value,',') AS value
from table1 t1
group by t1.id,t1.attribute
) d
pivot
(
max(value)
for attribute in (manager,account,cardno,code)
) piv
输出:
id
manager
account
cardno
code
101
Rudolf
456
<emnull</em
B
102
Anna
<emnull</em
123
B,C
Query#2 PIVOT() WITH STUFF() AND XML PATH FOR():
select *
from
(
select distinct t1.id,t1.attribute,
STUFF(
(SELECT ', ' + convert(varchar(10), t2.value, 120)
FROM table1 t2
where t1.id = t2.id and t1.attribute=t2.attribute
FOR XML PATH (''))
, 1, 1, '') AS value
from table1 t1
) d
pivot
(
max(value)
for attribute in (manager,account,cardno,code)
) piv
输出:
id
manager
account
cardno
code
101
Rudolf
456
<emnull</em
B
102
Anna
<emnull</em
123
B, C
dbhere
我想连接多行
Table:
|id |Attribute |Value | |--------|------------|---------| |101 |Manager |Rudolf | |101 |Account |456 | |101 |Code |B | |102 |Manager |Anna | |102 |Cardno |123 | |102 |Code |B | |102 |Code |C |
我要找的结果是:
|id |Manager|Account|Cardno|Code | |--------|-------|-------|------|----------| |101 |Rudolf |456 | |B | |102 |Anna | |123 |B,C |
我有以下来自
select
p.*,
a.value as Manager,
b.value as Account,
c.value as Cardno
from table1 p
left join table2 a on a.id = p.id and a.attribute = 'Manager'
left join table2 b on b.id = p.id and b.attribute = 'Account'
left join table2 c on c.id = p.id and b.attribute = 'Cardno'
但是,对于 ID# 102 的 Code
属性,它失败了,其中存在 B
和 C
值。
我如何更新它以在同一结果中包含这两个值?
UPD:“STRING_AGG 仅服务器 2017+” 您可以使用 CTE 和 STRING_AGG 函数解决此任务,例如:
declare
@t table (id int, Attribute varchar (100), [Value] varchar (100) )
insert into @t
values
(101, 'Manager', 'Rudolf'),
(101, 'Account', '456'),
(101, 'Code', 'B'),
(102, 'Manager', 'Anna'),
(102, 'Cardno', '123'),
(102, 'Code', 'B'),
(102, 'Code', 'C')
;with cte as
(
select id, Attribute
,STRING_AGG([Value], ', ') WITHIN GROUP (ORDER BY ID ASC) AS [Value]
from @t
group by ID, Attribute
)
select
max(p.ID) ID
,a.Value Manager
,isnull(b.Value, '') Account
,isnull(c.Value, '') Cardno
,isnull(e.Value, '') Code
from cte p
left join cte a on a.id =p.ID and a.attribute = 'Manager'
left join cte b on b.id = p.id and b.attribute = 'Account'
left join cte c on c.id = p.id and c.attribute = 'Cardno'
left join cte e on e.id = p.id and e.attribute = 'Code'
group by p.ID, a.Value,b.Value,c.Value,e.Value
通过 XML 和 XQuery 的另一种方法。
适用于 SQL Server 2008 以上版本。
SQL
-- DDL and sample data population, start
DECLARE @tbl TABLE (ID INT, attribute VARCHAR(20), [Value] VARCHAR(30));
INSERT INTO @tbl (ID, attribute, Value) VALUES
(101,'Manager','Rudolf'),
(101,'Account','456'),
(101,'Code','B'),
(102,'Manager','Anna'),
(102,'Cardno','123'),
(102,'Code','B'),
(102,'Code','C');
-- DDL and sample data population, end
;WITH rs AS
(
SELECT ID, (
SELECT *
FROM @tbl AS c
WHERE c.id = p.id
FOR XML PATH('r'), TYPE, ROOT('root')
) AS xmldata
FROM @tbl AS p
GROUP BY id
)
SELECT ID
, COALESCE(xmldata.value('(/root/r[attribute="Manager"]/Value/text())[1]','VARCHAR(30)'),'') AS Manager
, COALESCE(xmldata.value('(/root/r[attribute="Account"]/Value/text())[1]','VARCHAR(30)'),'') AS Account
, COALESCE(xmldata.value('(/root/r[attribute="Cardno"]/Value/text())[1]','VARCHAR(30)'),'') AS Cardno
, COALESCE(REPLACE(xmldata.query('data(/root/r[attribute="Code"]/Value)').value('.', 'VARCHAR(MAX)'), SPACE(1), ','),'') AS Code
FROM rs
ORDER BY ID;
输出
+-----+---------+---------+--------+------+
| ID | Manager | Account | Cardno | Code |
+-----+---------+---------+--------+------+
| 101 | Rudolf | 456 | | B |
| 102 | Anna | | 123 | B,C |
+-----+---------+---------+--------+------+
如果您使用的是 SQL SERVER 2017 或更高版本,那么 string_agg()
和 PIVOT()
将易于使用,但在性能解决方案中速度更快 (Query#1
)。
如果您使用的是旧版本的 SQL 服务器,则使用 Query#2
和 STUFF()
以及 XML PATH FOR()
将值与 PIVOT()
[= 连接起来23=]
架构:
create table table1 (id int, Attribute varchar(50) , Value varchar(50));
insert into table1 values(101 ,'Manager' ,'Rudolf');
insert into table1 values(101 ,'Account' ,'456');
insert into table1 values(101 ,'Code' ,'B');
insert into table1 values(102 ,'Manager' ,'Anna');
insert into table1 values(102 ,'Cardno' ,'123');
insert into table1 values(102 ,'Code' ,'B');
insert into table1 values(102 ,'Code' ,'C');
GO
查询#1 PIVOT() 与 STRING_AGG():
select *
from
(
select t1.id,t1.attribute,
string_agg(value,',') AS value
from table1 t1
group by t1.id,t1.attribute
) d
pivot
(
max(value)
for attribute in (manager,account,cardno,code)
) piv
输出:
id | manager | account | cardno | code |
---|---|---|---|---|
101 | Rudolf | 456 | <emnull</em | B |
102 | Anna | <emnull</em | 123 | B,C |
Query#2 PIVOT() WITH STUFF() AND XML PATH FOR():
select *
from
(
select distinct t1.id,t1.attribute,
STUFF(
(SELECT ', ' + convert(varchar(10), t2.value, 120)
FROM table1 t2
where t1.id = t2.id and t1.attribute=t2.attribute
FOR XML PATH (''))
, 1, 1, '') AS value
from table1 t1
) d
pivot
(
max(value)
for attribute in (manager,account,cardno,code)
) piv
输出:
id | manager | account | cardno | code |
---|---|---|---|---|
101 | Rudolf | 456 | <emnull</em | B |
102 | Anna | <emnull</em | 123 | B, C |
db