問題
我需要一種將多行匯總為一行和一列的方法。我知道我可以使用 Pivot將多行匯總為一行 ,但是我需要將所有數據串聯到一行中的一列中。在本文中,我們將介紹一種簡單的方法來完成此任務。
解
為了說明需要什么,下面是表中的數據示例:

這是我們希望最終結果看起來像的一個示例:

如何在SQL Server中將多行匯總為單行
將數據從多行匯總到單行對於連接數據,報告,在系統之間交換數據等等可能是必需的。這可以通過以下方式完成:
- 本技巧文章中提出的解決方案探討了兩個SQL Server命令,這些命令可以幫助我們獲得預期的結果。使用的SQL Server T-SQL命令是 STUFF和 FOR XML。
- T-SQL STUFF命令用於將結果連接在一起。在此示例中,結果用分號分隔。
- SELECT命令的FOR XML選項具有四個選項(即RAW,AUTO,EXPLICIT或PATH)以返回結果。在此示例中,PATH參數用於以XML字符串的形式檢索結果。
請查看下面的示例,以遍歷代碼示例和最終解決方案,以將多行匯總到SQL Server中的單行中。
准備樣品數據
在開始之前,我們將創建一些表和示例數據,以下腳本將為我們完成這些工作。
CREATE TABLE SALES_SECTORS( SEC_ID INT, SEC_NAME VARCHAR(30)) GO CREATE TABLE USRS( USR_ID INT, USR_NAME VARCHAR(30), SEC_ID INT ) GO CREATE TABLE ADV_CAMPAIGN( ADV_ID INT, ADV_NAME VARCHAR(30) ) GO CREATE TABLE USR_ADV_CAMPAIGN( USR_ID INT, ADV_ID INT ) GO CREATE TABLE SEC_ADV_CAMPAIGN( SEC_ID INT, ADV_ID INT ) GO INSERT INTO SALES_SECTORS( SEC_ID, SEC_NAME ) VALUES ( 1, 'ENTERTAINMENT' ) INSERT INTO SALES_SECTORS( SEC_ID, SEC_NAME ) VALUES ( 2, 'CLOTHES' ) GO INSERT INTO USRS( USR_ID, USR_NAME, SEC_ID ) VALUES ( 1, 'ANDERSON', 1 ) INSERT INTO USRS( USR_ID, USR_NAME, SEC_ID ) VALUES ( 2, 'CHARLES', 1 ) INSERT INTO USRS( USR_ID, USR_NAME, SEC_ID ) VALUES ( 3, 'DANNY', 1 ) INSERT INTO USRS( USR_ID, USR_NAME, SEC_ID ) VALUES ( 4, 'LUCAS', 1 ) INSERT INTO USRS( USR_ID, USR_NAME, SEC_ID ) VALUES ( 5, 'KEITH', 2 ) INSERT INTO USRS( USR_ID, USR_NAME, SEC_ID ) VALUES ( 6, 'STEFAN', 2 ) INSERT INTO USRS( USR_ID, USR_NAME, SEC_ID ) VALUES ( 7, 'EDUARD', 2 ) INSERT INTO USRS( USR_ID, USR_NAME, SEC_ID ) VALUES ( 8, 'BRAD', 2 ) GO INSERT INTO ADV_CAMPAIGN( ADV_ID, ADV_NAME ) VALUES ( 1, 'SONY ENTERTAINMENT' ) INSERT INTO ADV_CAMPAIGN( ADV_ID, ADV_NAME ) VALUES ( 2, 'BEATS SOUNDS' ) INSERT INTO ADV_CAMPAIGN( ADV_ID, ADV_NAME ) VALUES ( 3, 'BOOSE' ) INSERT INTO ADV_CAMPAIGN( ADV_ID, ADV_NAME ) VALUES ( 4, 'POLO RALPH LAUREN' ) INSERT INTO ADV_CAMPAIGN( ADV_ID, ADV_NAME ) VALUES ( 5, 'LACOSTE' ) GO INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 1, 1 ) INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 1, 2 ) INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 2, 2 ) INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 2, 3 ) INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 3, 3 ) INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 4, 2 ) INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 5, 4 ) INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 6, 5 ) INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 7, 4 ) INSERT INTO USR_ADV_CAMPAIGN( USR_ID, ADV_ID ) VALUES ( 8, 5 ) GO INSERT INTO SEC_ADV_CAMPAIGN( SEC_ID, ADV_ID ) VALUES ( 1, 1 ) INSERT INTO SEC_ADV_CAMPAIGN( SEC_ID, ADV_ID ) VALUES ( 1, 2 ) INSERT INTO SEC_ADV_CAMPAIGN( SEC_ID, ADV_ID ) VALUES ( 1, 3 ) INSERT INTO SEC_ADV_CAMPAIGN( SEC_ID, ADV_ID ) VALUES ( 2, 4 ) INSERT INTO SEC_ADV_CAMPAIGN( SEC_ID, ADV_ID ) VALUES ( 2, 5 ) GO
SQL Server STUFF()函數
在轉到示例之前,我們需要了解上述命令的工作原理。STUFF()函數從一個初始位置將一個字符串放入另一個字符串中。這樣,我們可以插入,替換或刪除一個或多個字符。
此語法為STUFF(character_expression,開始,長度,replaceWith_expression):
- character_expression:要操作的字符串
- 開始:開始的初始位置
- 長度:要操縱的字符數
- replaceWith_expression:要使用的字符
這是如何使用STUFF命令的示例。
對於我們的示例,我們有一個如下所示的字符串:
;KEITH;STEFAN;EDUARD;BRAD
我們要刪除第一個; 從列表中,所以我們最終得到以下輸出:
KEITH;STEFAN;EDUARD;BRAD
為此,我們可以使用STUFF命令來替換第一個;在帶有空字符串的字符串中。
SELECT STUFF(';KEITH;STEFAN;EDUARD;BRAD', 1, 1, '')
這將返回以下輸出:
KEITH;STEFAN;EDUARD;BRAD
SQL Server SELECT語句的FOR XML子句
FOR XML子句將以XML形式返回SQL查詢的結果。FOR XML有四種模式,分別是RAW,AUTO,EXPLICIT或PATH。我們將使用PATH選項,該選項為返回的每一行生成單個元素。
如果我們使用如下常規查詢,它將返回如下所示的結果集。
SELECT SS.SEC_NAME, US.USR_NAME FROM SALES_SECTORS SS INNER JOIN USRS US ON US.SEC_ID = SS.SEC_ID ORDER BY 1, 2
如果更進一步,我們可以使用FOR XML PATH選項以XML字符串的形式返回結果,該結果會將所有數據放入一行和一列。
SELECT SS.SEC_NAME, US.USR_NAME FROM SALES_SECTORS SS INNER JOIN USRS US ON US.SEC_ID = SS.SEC_ID ORDER BY 1, 2 FOR XML PATH('')
SQL Server Example to Rolling up Multiple Rows into a Single Row
Example 1
SELECT SS.SEC_NAME, (SELECT '; ' + US.USR_NAME FROM USRS US WHERE US.SEC_ID = SS.SEC_ID FOR XML PATH('')) [SECTORS/USERS] FROM SALES_SECTORS SS GROUP BY SS.SEC_ID, SS.SEC_NAME ORDER BY 1

SELECT SS.SEC_NAME, STUFF((SELECT '; ' + US.USR_NAME FROM USRS US WHERE US.SEC_ID = SS.SEC_ID FOR XML PATH('')), 1, 1, '') [SECTORS/USERS] FROM SALES_SECTORS SS GROUP BY SS.SEC_ID, SS.SEC_NAME ORDER BY 1

SELECT SS.SEC_NAME, STUFF((SELECT '; ' + US.USR_NAME FROM USRS US WHERE US.SEC_ID = SS.SEC_ID ORDER BY USR_NAME FOR XML PATH('')), 1, 1, '') [SECTORS/USERS] FROM SALES_SECTORS SS GROUP BY SS.SEC_ID, SS.SEC_NAME ORDER BY 1

Example 2
SELECT SS.SEC_NAME + ': ' + STUFF((SELECT '; ' + US.USR_NAME FROM USRS US WHERE US.SEC_ID = SS.SEC_ID FOR XML PATH('')), 1, 1, '') [SECTORS/USERS] FROM SALES_SECTORS SS GROUP BY SS.SEC_ID, SS.SEC_NAME ORDER BY 1

Example 3
SELECT SS.SEC_ID, SS.SEC_NAME, STUFF((SELECT '; ' + AC.ADV_NAME + ' (' + STUFF((SELECT ',' + US.USR_NAME FROM USR_ADV_CAMPAIGN UAC INNER JOIN USRS US ON US.USR_ID = UAC.USR_ID WHERE UAC.ADV_ID = SAC.ADV_ID FOR XML PATH('')), 1, 1, '') + ')' FROM ADV_CAMPAIGN AC INNER JOIN SEC_ADV_CAMPAIGN SAC ON SAC.ADV_ID = AC.ADV_ID AND SAC.SEC_ID = SS.SEC_ID ORDER BY AC.ADV_NAME FOR XML PATH('')), 1, 1, '') [CAMPAIGNS/USERS PER SECTOR] FROM SALES_SECTORS SS GROUP BY SS.SEC_ID, SS.SEC_NAME

Example Rolling Up Index Columns into One Row
SELECT SCHEMA_NAME(ss.SCHEMA_id) AS SchemaName, ss.name as TableName, ss2.name as IndexName, ss2.index_id, ss2.type_desc, STUFF((SELECT ', ' + name from sys.index_columns a inner join sys.all_columns b on a.object_id = b.object_id and a.column_id = b.column_id and a.object_id = ss.object_id and a.index_id = ss2.index_id and is_included_column = 0 order by a.key_ordinal FOR XML PATH('')), 1, 2, '') IndexColumns, STUFF((SELECT ', ' + name from sys.index_columns a inner join sys.all_columns b on a.object_id = b.object_id and a.column_id = b.column_id and a.object_id = ss.object_id and a.index_id = ss2.index_id and is_included_column = 1 FOR XML PATH('')), 1, 2, '') IncludedColumns FROM sys.objects SS INNER JOIN SYS.INDEXES ss2 ON ss.OBJECT_ID = ss2.OBJECT_ID WHERE ss.type = 'U' ORDER BY 1, 2, 3