レポートのデータをまとめようとしていますが、いずれかのテーブルの行の値を連結する必要があります。基本的なテーブル構造は次のとおりです。
レビュー
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 これを行う方法を説明した記事を見つけましたが、これらのほとんどは複数ではなく1つのテーブルのみを扱っているようです。残念ながら、私の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分間コードで遊んだ後、甘い解決策が見つかりました
declare @result varchar(1000)
select @result = COALESCE(@result+','+A.col1, A.col1)
FROM ( select col1
from [table]
) A
select @result
結果をvalue1、value2、value3、value4として返します
楽しむ;)
SQL Server 2017には STRING_AGG があり、指定されたセパレーターを使用して複数の文字列を1つに集約します。
説明したように、ロールアップデータを処理した3つの方法があります。1。カーソルを使用、2。UDFを使用、または3.カスタム集計(.NET CLRで記述)を使用します。
カーソルとUDFはかなり遅いです。 (行ごとに約0.1秒)。 CLRカスタム集計は驚くほど高速です。 (行ごとに約0.001秒)
Microsoftは、SQL 2005のSDKの一部としてコードを出荷します(希望どおりに実行します)。インストールしている場合、このフォルダーでコードを見つけることができるはずです: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;
上に示したような結果セットを取得します
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,
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
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;
[〜#〜] udf [〜#〜] は、これを解決するのに適切な方法です。
Int param(製品ID)を取り、文字列(製品に関連付けられた名前の連結)を返すT-SQL関数(UDF)を定義するだけです。メソッドの名前が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
データをダンプする一時テーブルを作成します。次に、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
group_concatの機能(mysqlから)が必要なようです。これは別のテストデータセットについてここで対処されています。 1つの列に複数の値を返す方法(T-SQL)?
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
STRING_AGG ( expression, separator ) [ <order_clause> ]
<order_clause> ::=
WITHIN GROUP ( ORDER BY <order_by_expression_list> [ ASC | DESC ] )
SQLサーバーの文字列集計関数を探してStackoverflowに来ました。
関連する質問は閉じられており、この質問の複製としてマークされているため、ここで回答するか、まったく回答しないようにします。
詳細については、 https://docs.Microsoft.com/en-us/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-2017 を参照してください。
アイテムの数が少ない場合、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