从 SQL 服务器中的行连接

问题描述

我想从多行连接

表格:

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

我有以下来自 related question代码

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 属性失败,其中 BC 值都存在。

如何更新它以在同一结果中包含这两个值?

解决方法

UPD:“STRING_AGG 仅适用于 Server 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'),'Code','B'),(102,'Anna'),'Cardno','123'),'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,'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]','') AS Account,COALESCE(xmldata.value('(/root/r[attribute="Cardno"]/Value/text())[1]','') 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 Server,则使用 Query#2STUFF() 将值与 XML PATH FOR() 一起使用

架构:

PIVOT()

使用 STRING_AGG() 查询#1 PIVOT():

 create table table1 (id int,Attribute varchar(50),Value  varchar(50));
 
 insert into table1 values(101,'Rudolf');   
 insert into table1 values(101,'456');   
 insert into table1 values(101,'B');
 insert into table1 values(102,'Anna');
 insert into table1 values(102,'123');
 insert into table1 values(102,'C');
 GO

输出:

id manager 帐号 cardno 代码
101 鲁道夫 456 B
102 安娜 123 B,C

Query#2 PIVOT() WITH STUFF() 和 XML PATH FOR():

 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 帐号 cardno 代码
101 鲁道夫 456 B
102 安娜 123 B,C

dbhere