示例表 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
*/