连接行值T-SQL
我正在试图将一些数据汇总在一起,需要连接其中一个表的行值。 这里是基本的表结构:
评测
ReviewID ReviewDate
评审
ReviewerID ReviewID UserID
用户
UserID FName LName
这是M:M的关系。 每个评论可以有许多评论者; 每个用户可以与许多评论相关联。
基本上,我只想看到的是Review.ReviewID,Reviews.ReviewDate和该评论的所有关联用户的FName的连接string(逗号分隔)。
代替:
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
我发现这篇文章描述了一些方法来做到这一点,但其中大多数似乎只处理一个表,而不是多个; 不幸的是,我的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;
任何人都可以帮我一个这个吗? 任何帮助将不胜感激!
看看这个
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
原来,有一个更简单的方法来做到这一点,不需要UDF:
select replace(replace(replace((cast(( select distinct columnName as X from tableName for xml path('')) as varchar(max))), '</X><X>', ', '),'<X>', ''),'</X>','')
有类似的问题,并发现代码15分钟后,甜蜜的解决scheme
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种方式处理汇总数据:使用游标,使用UDF或使用自定义聚合(使用.NET CLR编写)。
游标和UDF是相当缓慢的。 (每行约0.1秒)。 CLR自定义聚合速度惊人地快。 (每行约0.001秒)
作为SQL 2005 SDK的一部分,Microsoft提供了代码(按照您的需要进行操作)。如果安装了该代码,则应该能够在该文件夹中find代码: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;
并得到像你所示的结果集(上图)
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,
UDF是解决这个问题的好方法。
只需定义一个T-SQL函数(UDF),它接受一个int参数(产品ID)并返回一个string(与产品关联的名称的拼接)。如果您的方法名称是GetProductNames,那么您的查询可能如下所示:
SELECT p1.CategoryId, dbo.GetProductNames(p1.CategoryId) FROM Northwind.dbo.Products p1 GROUP BY CategoryId
尝试这个:
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
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;
SqlServer 2017现在有STRING_AGG ,它使用给定的分隔符将多个string聚合为一个。
似乎你需要group_concat(从MySQL)的function。 这已经在这里解决了另一个testing数据集: 如何返回一列中的多个值(T-SQL)?
创build一个临时表来转储数据。然后使用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
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