连接行值T-SQL

时间:2009-12-09 16:13:25

标签: sql sql-server tsql concatenation

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

评论

 ReviewID  
 ReviewDate  

审稿人

 ReviewerID  
 ReviewID  
 UserID  

用户

UserID  
FName  
LName  

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

基本上,我想要看的是Reviews.ReviewID,Reviews.ReviewDate,以及该评论的所有相关用户的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  

显示:

ReviewID---ReviewDate----Users  
1----------12/1/2009-----Bob, Joe, Frank  
2----------12/9/2009-----Sue, Alice

我发现this文章描述了一些方法,但其中大多数似乎只涉及一个表,而不是多个;不幸的是,我的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;

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

15 个答案:

答案 0 :(得分: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

答案 1 :(得分:20)

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

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

答案 2 :(得分:10)

有类似的问题,并在使用代码15分钟后找到了一个很好的解决方案

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

以value1,value2,value3,value4

返回结果

享受;)

答案 3 :(得分:7)

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

答案 4 :(得分: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 \ Samples \ Engine \ Programmability \ 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;

并获得与您展示的结果集(上图)

答案 5 :(得分: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

结果

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,

答案 6 :(得分:3)

UDF是解决这个问题的好方法。

只需定义一个T-SQL函数(UDF),它接受一个int参数(产品ID)并返回一个字符串(与产品关联的名称的串联。)如果您的方法名称是GetProductNames,那么您的查询可能如下所示:

SELECT p1.CategoryId, dbo.GetProductNames(p1.CategoryId)
FROM Northwind.dbo.Products p1
GROUP BY CategoryId

答案 7 :(得分:3)

试试这个:

 Declare @Revs Table 
 (RevId int Priimary Key Not Null,
  RevDt DateTime Null,
  users varChar(1000) default '')

 Insert @Revs (RevId, RevDt)
 Select Distinct ReviewId, ReviewDate
 From Reviews
 Declare @UId Integer
 Set @Uid = 0
 While Exists (Select * From Users
               Where UserID > @Uid)
 Begin
    Update @Revs Set
      users = users + u.fName + ', '
    From @Revs R 
       Join Reviewers uR On ur.ReviewId = R.RId
       Join users u On u.UserId = uR.UserId 
    Where uR.UserId = @UId
    Select @Uid = Min(UserId)
    From users
    Where UserId > @UId
  End
  Select * From @Revs

答案 8 :(得分:3)

Select R.ReviewID, ReviewDate
, (Select  FName + ', ' 
   from Users 
   where UserID = R.UserID 
   order by FName FOR XML PATH(')
) as [Users]
from Reviews
inner join Reviewers AS R
  On Reviews.ReviewID = R.ReviewID
Group By R.ReviewID, ReviewDate;

答案 9 :(得分:3)

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

示例:

SELECT STRING_AGG ( ISNULL(FirstName,'N/A'), ',') AS csv 
FROM Person.Person; 

结果集:

John,N/A,Mike,Peter,N/A,N/A,Alice,Bob

答案 10 :(得分:2)

好像你需要group_concat的功能(来自mysql)。这里已针对另一个测试数据集进行了解决:How to return multiple values in one column (T-SQL)?

答案 11 :(得分:2)

创建临时表以转储数据。然后使用FOR XML PATH方法。需要使用外部查询来修剪列表中的最后一个逗号。

CREATE TABLE #ReviewInfo (
ReviewId INT,
ReviewDate DATETIME,
Reviewer VARCHAR(1000))

INSERT INTO #ReviewInfo (ReviewId, ReviewDate, Reviewer)
SELECT r.ReviewId, r.ReviewDate, u.FName
FROM Reviews r
JOIN Reviewers rs ON r.ReviewId = rs.ReviewId
JOIN Users u ON u.UserId = rs.UserId

SELECT ReviewId, ReviewDate, LEFT(Users, LEN(Users)-1)
FROM (
SELECT ReviewId, ReviewDate, 
(
    SELECT Reviewer + ', '
    FROM #ReviewInfo ri2
    WHERE ri2.ReviewId = ri1.ReviewId
    ORDER BY Reviewer
    FOR XML PATH('')
) AS Users
FROM #ReviewInfo ri1
GROUP BY ReviewId, ReviewDate
) a

DROP TABLE #ReviewInfo

答案 12 :(得分:2)

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

答案 13 :(得分:0)

当项目数量很少时,可以使用ROW_NUMBER()OVER PARTITION BY:

declare @t table (col1 int, col2 varchar)
insert into @t VALUES (1,'A')
insert into @t VALUES (1,'B')
insert into @t VALUES (1,'C')
insert into @t VALUES (1,'D')
insert into @t VALUES (1,'E')
insert into @t VALUES (2,'X')
insert into @t VALUES (3,'Y')

select col1,
    MAX(CASE seq WHEN 1 THEN        col2 ELSE '' END ) + 
    MAX(CASE seq WHEN 2 THEN ', ' + col2 ELSE '' END ) + 
    MAX(CASE seq WHEN 3 THEN ', ' + col2 ELSE '' END ) +
    MAX(CASE seq WHEN 4 THEN ', ' + col2 ELSE '' END ) +
    MAX(CASE seq WHEN 5 THEN ',...' ELSE '' END ) 
    as col2
from (
    select col1, col2, ROW_NUMBER() OVER ( PARTITION BY col1 ORDER BY col2 ) seq
    from @t
    group by col1, col2
) x
group by col1

答案 14 :(得分:0)

STRING_AGG ( expression, separator ) [ <order_clause> ]

<order_clause> ::=   
    WITHIN GROUP ( ORDER BY <order_by_expression_list> [ ASC | DESC ] )

我来到Stackoverflow寻找SQL Server字符串聚合函数。

相关问题已经关闭,标记为该问题的重复项,因此我被迫在此处回答或根本不回答。

有关详细信息,请参见https://docs.microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017