【问题标题】:Concatenate from rows in SQL server从 SQL Server 中的行连接
【发布时间】:2021-05-15 14:21:16
【问题描述】:

我想连接多行

表:

|id |属性|值|
|--------|------------|---------|
|101 |经理 |鲁道夫 |
|101 |帐户 |456 |
|101 |代码 |B |
|102 |经理 |安娜 |
|102 |卡诺 |123 |
|102 |代码 |B |
|102 |代码 |C |

我要找的结果是:

|id |经理|帐户|Cardno|代码|
|--------|--------|--------|------|----------|
|101 |鲁道夫 |456 | |B |
|102 |安娜 | |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 值都存在。

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

【问题讨论】:

  • 啊,可怕的 EAV 模式反模式。有时你种下什么就收什么。您想要的每个字段都是返回到同一个表的另一个连接,对于 (102,Code),您可能会使用 string_agg()。此外,我们需要确切了解您使用的是哪种数据库,因为正确的解决方案将取决于数据库类型和版本,因为一些数据库在最近的版本中添加了工具来帮助解决这个问题.
  • 这是 Microsoft SQL Server 2012
  • 我忘记了一件重要的事情。我是 SQL 的初学者。也许这比我想象的要复杂。
  • 好吧,string_agg() 直到 Sql Server 2017 才可用。那仍然会很痛苦(你需要按每一列分组),但没有它你'将不得不以非常困难的方式做到这一点。
  • 谢谢乔尔。我对第一个问题有点混搭。我一直在使用 Excel,但我必须承认 SQL 有很多优势。猜猜我必须在 Excel 中完成这项工作。再次非常感谢。

标签: sql sql-server sql-server-2012 concatenation


【解决方案1】:

如果您使用的是 SQL SERVER 2017 或更高版本,那么 string_agg()PIVOT() 将易于使用,但在性能解决方案中更快 (Query#1)。

如果您使用的是旧版本的 SQL Server,请使用 Query#2STUFF()XML PATH FOR() 连接值以及 PIVOT()

架构:

 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

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

 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

【讨论】:

  • 谢谢。它工作正常。现在我必须将它与我关于使用连接将列表与行表连接起来的相关问题合并。再次感谢
  • 欢迎您并感谢您分享输入、输出、数据库名称和您的努力。用这么多资源解决问题要容易得多。你的问题值得一票。
【解决方案2】:

通过 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  |
+-----+---------+---------+--------+------+

【讨论】:

    【解决方案3】:

    UPD:“STRING_AGG only 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'),
    (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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-02
      • 2016-07-01
      • 2021-02-08
      • 2014-12-25
      • 2014-02-09
      • 2011-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多