连接行值T-SQL

Ses*_*ame 33 sql t-sql sql-server concatenation

我试图将一些数据汇总到一个报告中,并需要连接其中一个表的行值.这是基本的表结构:

评测

 ReviewID  
 ReviewDate  
Run Code Online (Sandbox Code Playgroud)

评审

 ReviewerID  
 ReviewID  
 UserID  
Run Code Online (Sandbox Code Playgroud)

用户

UserID  
FName  
LName  
Run Code Online (Sandbox Code Playgroud)

这是M:M的关系.每个评论都可以有很多评论者; 每个用户都可以与许多评论相关联.

基本上,我想要看的是Reviews.ReviewID,Reviews.ReviewDate,以及该Review的所有相关用户的FName的串联字符串(逗号分隔).

代替:

ReviewID---ReviewDate---User  
1----------12/1/2009----Bob  
1----------12/1/2009----Joe  
1----------12/1/2009----Frank  
2----------12/9/2009----Sue  
2----------12/9/2009----Alice  
Run Code Online (Sandbox Code Playgroud)

显示这个:

ReviewID---ReviewDate----Users  
1----------12/1/2009-----Bob, Joe, Frank  
2----------12/9/2009-----Sue, Alice
Run Code Online (Sandbox Code Playgroud)

我发现这篇文章描述了一些方法,但是大多数方法似乎只处理一个表而不是多个; 不幸的是,我的SQL-fu不够强大,无法根据我的情况进行调整.我对该网站上使用FOR XML PATH()的示例特别感兴趣,因为它看起来最干净,最直接.

SELECT p1.CategoryId,
( SELECT ProductName + ', '
  FROM Northwind.dbo.Products p2
  WHERE p2.CategoryId = p1.CategoryId
  ORDER BY ProductName FOR XML PATH('')
) AS Products
FROM Northwind.dbo.Products p1
GROUP BY CategoryId;
Run Code Online (Sandbox Code Playgroud)

任何人都可以帮我一把吗?任何帮助将不胜感激!

Adr*_*der 33

看看这个

DECLARE @Reviews TABLE(
        ReviewID INT,
        ReviewDate DATETIME
)

DECLARE @Reviewers TABLE(
        ReviewerID   INT,
        ReviewID   INT,
        UserID INT
)

DECLARE @Users TABLE(
        UserID  INT,
        FName  VARCHAR(50),
        LName VARCHAR(50)
)

INSERT INTO @Reviews SELECT 1, '12 Jan 2009'
INSERT INTO @Reviews SELECT 2, '25 Jan 2009'

INSERT INTO @Users SELECT 1, 'Bob', ''
INSERT INTO @Users SELECT 2, 'Joe', ''
INSERT INTO @Users SELECT 3, 'Frank', ''
INSERT INTO @Users SELECT 4, 'Sue', ''
INSERT INTO @Users SELECT 5, 'Alice', ''

INSERT INTO @Reviewers SELECT 1, 1, 1
INSERT INTO @Reviewers SELECT 2, 1, 2
INSERT INTO @Reviewers SELECT 3, 1, 3
INSERT INTO @Reviewers SELECT 4, 2, 4
INSERT INTO @Reviewers SELECT 5, 2, 5

SELECT  *,
        ( 
            SELECT  u.FName + ','
            FROM    @Users u INNER JOIN 
                    @Reviewers rs ON u.UserID = rs.UserID
            WHERE   rs.ReviewID = r.ReviewID
            FOR XML PATH('')
        ) AS Products
FROM    @Reviews r
Run Code Online (Sandbox Code Playgroud)

  • 这不能正确处理像`>`和`&`这样的XML特殊字符.因此,如果数据包含"Frank&Bill",那么你就会得到`Frank& 结果集中的Bill`.有一种很好的方法可以解决这个问题,请参阅:http://stackoverflow.com/questions/5031204/does-t-sql-have-an-aggregate-function-to-concatenate-strings/5031297#5031297 (5认同)

小智 20

事实证明,有一种更简单的方法可以做到这一点,不需要UDF:

select replace(replace(replace((cast((
        select distinct columnName as X
        from tableName 
        for xml path('')) as varchar(max))), 
   '</X><X>', ', '),'<X>', ''),'</X>','')
Run Code Online (Sandbox Code Playgroud)


小智 10

有类似的问题,并在玩代码15分钟后找到了一个甜蜜的解决方案

declare @result varchar(1000)
select @result = COALESCE(@result+','+A.col1, A.col1)
                FROM (  select  col1
                        from [table] 
                ) A
select @result
Run Code Online (Sandbox Code Playgroud)

返回结果为value1,value2,value3,value4

请享用 ;)

  • 这不受Microsoft支持,可能会产生意外结果.请参阅https://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/或我的博客http://marc.durdin.net/2015/07/ concatenating-strings-in-sql-server-or-undefined-behavior-by-design /更多讨论. (2认同)

Joh*_*ohn 7

SqlServer 2017现在有STRING_AGG,它使用给定的分隔符将多个字符串聚合为一个.


Tim*_*imG 6

正如您所描述的,有三种处理汇总数据的方法,1.使用游标,2.使用UDF或3.使用自定义聚合(用.NET CLR编写).
Cursor和UDF非常慢.(每行大约0.1秒).CLR自定义聚合速度惊人的快.(每排约0.001秒)

Microsoft提供代码(完全按照您的意愿)作为SDK for SQL 2005的一部分.如果安装了它,您应该能够在此文件夹中找到代码:C:\ Program Files\Microsoft SQL Server\90 \样本\发动机\可编程\ CLR\StringUtilities.您可能还想在MSDN中阅读本文.它讨论了如何安装自定义聚合并启用它:http: //msdn.microsoft.com/en-us/library/ms161551(SQL.90).aspx

编译并安装自定义聚合后,您应该能够像这样查询:

SELECT Reviews.ReviewID, ReviewDate, dbo.StringUtilities.Concat(FName) AS [User]
FROM Reviews INNER JOIN Reviewers ON Reviews.ReviewID = Reviewers.ReviewID
   INNER JOIN Users ON Reviews.UserID = Users.UserID
GROUP BY ReviewID, ReviewDate;
Run Code Online (Sandbox Code Playgroud)

并得到一个像你所示的结果集(上图)


小智 5

select p1.Availability ,COUNT(*),
(select  name+','  from AdventureWorks2008.Production.Location p2 where 
p1.Availability=p2.Availability for XML path(''),type).value('.','varchar(max)') 
as Name  from AdventureWorks2008.Production.Location p1 group by Availability
Run Code Online (Sandbox Code Playgroud)

结果

Availability  COUNT     Name  
---------------------------------------------------------------------------------
0.00    7   Tool Crib,Sheet Metal Racks,Paint Shop,Paint Storage,Metal 
                    Storage,Miscellaneous Storage,Finished Goods Storage,
80.00   1   Specialized Paint,
96.00   1   Frame Forming,
108.00  1   Frame Welding,
120.00  4   Debur and Polish,Paint,Subassembly,Final Assembly,
Run Code Online (Sandbox Code Playgroud)


小智 5

现在从 SQL Server 2017 开始,有一个名为的新T-SQL函数STRING_AGG
它是一个新的聚合函数,用于连接字符串表达式的值并在它们之间放置分隔符值。
字符串末尾不添加分隔符。

例子:

SELECT STRING_AGG ( ISNULL(FirstName,'N/A'), ',') AS csv 
FROM Person.Person; 
Run Code Online (Sandbox Code Playgroud)

结果集:

John,N/A,Mike,Peter,N/A,N/A,Alice,Bob
Run Code Online (Sandbox Code Playgroud)