合并多行查询数据到一行:使用自连接、FOR XML PATH('')、STUFF或REPLACE函数

示例表 tb 数据如下

id value
—————
1 aa
1 bb
2 aaa
2 bbb
2 ccc

第一种

SELECT id, [val]=( SELECT[value]+','FROM tb AS b WHERE b.id = a.id FOR XML PATH('') ) FROM tb AS a

第一种显示结果 
1 aa,bb, 
1 aa,bb, 
2 aaa,bbb,ccc, 
2 aaa,bbb,ccc, 
2 aaa,bbb,ccc,

第二种

SELECT id, [val]=( SELECT[value]+','FROM tb AS b WHERE b.id = a.id FOR XML PATH('') ) FROM tb AS a GROUPBY id

第二种显示结果 
1 aa,bb, 
2 aaa,bbb,ccc,

第三种 (用STUFF函数替换掉首端的逗号)

SELECT id, [val]=STUFF( (SELECT','+[value]FROM tb AS b WHERE b.id = a.id FOR XML PATH('')) , 1 , 1 , '' ) FROM tb AS a GROUPBY id

第三种显示结果 
1 aa,bb 
2 aaa,bbb,ccc

STUFF 函数将字符串插入另一字符串。它在第一个字符串中从开始位置删除指定长度的字符;然后将第二个字符串插入第一个字符串的开始位置。
STUFF ( character_expression , start , length ,character_expression_insert )

第四种 (用REPLACE函数将所有空格替换成逗号)

结果与第三种一样。


合并分拆表数据

--生成测试数据
IF NOT object_id('Tab') IS NULL
DROP TABLE Tab
GO
CREATE TABLE Tab( [Col1] INT ,[Col2] NVARCHAR(1))
INSERT Tab
SELECT 1 ,N'a' UNION ALL
SELECT 1 ,N'b' UNION ALL
SELECT 1 ,N'c' UNION ALL
SELECT 2 ,N'd' UNION ALL
SELECT 2 ,N'e' UNION ALL
SELECT 3 ,N'f'
GO

/*----------
  1.合并表
----------*/

--SQL2000用函数:
GO
IF object_id('F_Str') IS NOT NULL
DROP FUNCTION F_Str
GO 
CREATE FUNCTION F_Str(@Col1 INT) 
RETURNS NVARCHAR(100) AS 
BEGIN 
    DECLARE @S NVARCHAR(100);
    SELECT @S=ISNULL(@S+',','')+Col2 FROM Tab WHERE Col1=@Col1
    RETURN @S
END
GO
SELECT DISTINCT Col1,Col2=dbo.F_Str(Col1) FROM Tab
GO 

--SQL2005用XML:
--方法1: (用STUFF函数替换掉首端的逗号)
SELECT a.Col1,Col2=STUFF((SELECT ','+b.Col2 FROM Tab b WHERE b.Col1=a.Col1 FOR XML PATH('')),1,1,'')
FROM Tab a GROUP BY a.Col1
--方法2:
SELECT a.Col1,Col2=STUFF(b.Col2.value('/R[1]','NVARCHAR(max)'),1,1,'') FROM (SELECT DISTINCT COl1 FROM Tab) a
CROSS APPLY(SELECT COl2=(SELECT N','+Col2 FROM Tab WHERE Col1=a.COl1 FOR XML PATH('') , ROOT('R') , TYPE))b
--方法3: 
SELECT a.Col1,COl2=REPLACE(b.Col2.value('/Tab[1]','NVARCHAR(max)'),CHAR(44)+CHAR(32),CHAR(44))
FROM (SELECT DISTINCT COl1 FROM Tab) a
CROSS APPLY (SELECT Col2=(SELECT COl2
    FROM Tab
    WHERE COl1=a.COl1 FOR XML AUTO
      , TYPE) .query('<Tab>
                {for $i in /Tab[position()<last()]/@COl2 return concat(string($i),",")}
                {concat("",string(/Tab[last()]/@COl2))}
                </Tab>') )b
--SQL2005用CTE:   
WITH roy AS (
    SELECT Col1,Col2,row=row_number()over(PARTITION BY COl1 ORDER BY COl1) FROM Tab
    ) ,Roy2 AS (
    SELECT COl1,CAST(COl2 AS NVARCHAR(100))COl2,row FROM Roy WHERE row=1
    UNION ALL
    SELECT a.Col1,CAST(b.COl2+','+a.COl2 AS NVARCHAR(100)),a.row FROM Roy a JOIN Roy2 b ON a.COl1=b.COl1 AND a.row=b.row+1
    )
SELECT Col1,Col2 FROM Roy2 a WHERE row=(SELECT MAX(row) FROM roy WHERE Col1=a.COl1) ORDER BY Col1 OPTION(MAXRECURSION 0) 
--生成结果:
/*
Col1        COl2
----------- ------------
1           a,b,c
2           d,e
3           f

(3 行受影响)
*/

/*==========================================================================================*/
/*----------
  2.拆分表
----------*/ 

--生成测试数据
IF NOT object_id('Tab') IS NULL
DROP TABLE Tab 
GO
CREATE TABLE Tab( [Col1] INT ,[COl2] NVARCHAR(5))
INSERT Tab
SELECT 1 ,N'a,b,c' UNION ALL 
SELECT 2 ,N'd,e' UNION ALL
SELECT 3 ,N'f'
GO

--SQL2000用辅助表:
IF object_id('Tempdb..#Num') IS NOT NULL 
DROP TABLE #Num
GO   
SELECT TOP 100 ID=IDENTITY(INT,1,1) INTO #Num FROM syscolumns a ,syscolumns b
SELECT a.Col1 ,COl2=SUBSTRING(a.Col2,b.ID,CHARINDEX(',',a.Col2+',',b.ID)-b.ID) FROM Tab a ,#Num b WHERE CHARINDEX(',',','+a.Col2,b.ID)=b.ID 
--也可用 substring(','+a.COl2,b.ID,1)=',' 

--SQL2005用Xml: 
SELECT a.COl1 ,b.Col2
FROM (SELECT Col1,COl2=CONVERT(xml,'<root><v>'+REPLACE(COl2,',','</v><v>')+'</v></root>') FROM Tab)a
OUTER APPLY (SELECT Col2=C.v.value('.','NVARCHAR(100)') FROM a.COl2.nodes('/root/v')C(v))b

--SQL2005用CTE: ;
WITH roy AS(
SELECT Col1,COl2=CAST(LEFT(Col2,CHARINDEX(',',Col2+',')-1) AS NVARCHAR(100)) ,Split=CAST(stuff(COl2+',',1,CHARINDEX(',',Col2+','),'') AS NVARCHAR(100)) FROM Tab
UNION ALL
SELECT Col1,COl2=CAST(LEFT(Split,CHARINDEX(',',Split)-1) AS NVARCHAR(100)),Split= CAST(stuff(Split,1,CHARINDEX(',',Split),'') AS NVARCHAR(100)) 
FROM Roy
WHERE split>''
)
SELECT COl1 ,COl2 FROM roy ORDER BY COl1 OPTION(MAXRECURSION 0)
--生成结果:
/*
Col1        COl2
----------- -----
1           a
1           b
1           c
2           d
2           e
3           f
*/