SQL2005下利用XML进行项目的合并与拆分
作者:胡林
时间:2009年1月4日
转载请注明出处
通常情况下我们对相同数值项目进行分组求和,那是相当的简单啦,只要select.. group by加聚合函数就行了,可是对于串一类的项目进行合并时就不这么简单了,同样分解一个按指定分隔符分隔的串或分析指定位置的串,在下我们通常是创建一个函数,然后分组或提取就行了,现我们主要讨论在下利用xml来完成这个工作。
- 先来一个简单点,如下的例子对aaa相同的项目合并。
- ------------------------------------------------------------------------
- -- Author: HappyFlyStone
- -- Date : 2009-01-04 20:57:59
- -- Ver: Microsoft SQL Server 2005 - 9.00.2047.00 (Intel X86)
- -- Apr 14 2006 01:12:25
- -- Copyright (c) 1988-2005 Microsoft Corporation
- -- Standard Edition on Windows NT5.0(Build 2195: Service Pack 4)
- --
- ------------------------------------------------------------------------
- -- Test Data: tb
- IF OBJECT_ID('tb') IS NOT NULL
- DROP TABLE tb
- Go
- CREATE TABLE tb(aaa INT,bbb INT)
- Go
- INSERT INTO tb
- SELECT 1,2 UNION ALL
- SELECT 1,3 UNION ALL
- SELECT 1,4 UNION ALL
- SELECT 2,2 UNION ALL
- SELECT 2,5
- GO
-
- --查询1
- select aaa,[values]=stuff(replace(replace((select [bbb]
- from tb
- where aaa=t.aaa for xml AUTO), '"/><tb bbb="',','), '"/>',''),1,9,'')
- from tb t
- group by aaa
-
- --查询2
- SELECT * FROM(
- SELECT DISTINCT aaa
- FROM tb
- ) A
- OUTER APPLY(
- SELECT [bbb]= STUFF(REPLACE(REPLACE(
- ( SELECT [bbb] FROM tb N
- WHERE aaa = A.aaa
- FOR XML AUTO
- ), '<N bbb="', ','), '"/>', ''), 1, 1, '')
- ) N
-
- --查询3
- select aaa,[values]=stuff((select ','+ltrim([bbb])
- from tb t
- where aaa=tb.aaa for xml path('')), 1, 1, '')
- from tb
- group by aaa
-
-
- drop table tb
-
- --查询结果
- /*
- aaa values
- ----------- ---------
- 1 2,3,4
- 2 2,5
- (2 行受影响)
- */
- 来个两个表关联操作并实现行列转换的
- ------------------------------------------------------------------------
- -- Author: HappyFlyStone
- -- Date : 2009-01-04 20:57:59
- -- Ver: Microsoft SQL Server 2005 - 9.00.2047.00 (Intel X86)
- -- Apr 14 2006 01:12:25
- -- Copyright (c) 1988-2005 Microsoft Corporation
- -- Standard Edition on Windows NT5.0(Build 2195: Service Pack 4)
- --
- ------------------------------------------------------------------------
-
- -- Test Data: ta
- IF OBJECT_ID('ta') IS NOT NULL
- DROP TABLE ta
- Go
- CREATE TABLE ta(pid INT,tid INT,name NVARCHAR(6))
- Go
- INSERT INTO ta
- SELECT 1,1,'hy3500' UNION ALL
- SELECT 1,2,'aabbcc' UNION ALL
- SELECT 2,3,'1111' UNION ALL
- SELECT 2,4,'2222'
- GO
- -- Test Data: tb
- IF OBJECT_ID('tb') IS NOT NULL
- DROP TABLE tb
- Go
- CREATE TABLE tb(NAME NVARCHAR(2),id INT)
- Go
- INSERT INTO tb
- SELECT '型号',1 UNION ALL
- SELECT '参数',2
- GO
- --Start
- SELECT t.[name],A,B
- FROM(
- SELECT B.[NAME], CAST((SELECT [name]
- FROM TA
- WHERE PID = A.PID FOR XML PATH('')) AS XML) AS X
- FROM TA A
- LEFT JOIN TB B ON A.PID = B.ID
- GROUP BY B.[NAME],A.PID
- ) t
- CROSS APPLY
- (SELECT A=t.x.value('/name[1]','VARCHAR(10)'),B = t.x.value('/name[2]','VARCHAR(10)')) M
-
- --Result:
- /*
-
- c a b
- ---- ---------- ----------
- 参数 1111 2222
- 型号 hy3500 aabbcc
-
- (2 行受影响)
-
- */
- --End
- 取特定分隔符分隔的串中指定位置的串
- ------------------------------------------------------------------------
- -- Author: HappyFlyStone
- -- Date : 2009-01-04 20:57:59
- -- Ver: Microsoft SQL Server 2005 - 9.00.2047.00 (Intel X86)
- -- Apr 14 2006 01:12:25
- -- Copyright (c) 1988-2005 Microsoft Corporation
- -- Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)
- --
- ------------------------------------------------------------------------
-
- DECLARE @t TABLE(c VARCHAR(20))
- INSERT @t SELECT '双桥,9.6米,30.0吨'
- UNION ALL SELECT 'aa,bb,cc,dd'
- --通常情况如果项目在四个项目以内时,推荐一种方法:
-
-
- SELECT REPLACE(PARSENAME(XX,3),'$$','.') C ,
- REPLACE(PARSENAME(XX,2),'$$','.') B
- FROM
- (
- SELECT REPLACE(REPLACE(c,'.','$$'),',','.') XX FROM @T
- )AA
- --result
- /*
- c b
- --------------------------------- -----------
- 双桥 9.6米
-
- (所影响的行数为1 行)
- */
好,那我们来看看下XML如何处理的
- SELECT A,B FROM
- (SELECT CAST('<r>' + REPLACE(c,',','</r><r>') + '</r>' AS XML) x,c FROM @t) a
- CROSS APPLY
- (SELECT A=a.x.value('/r[1]','VARCHAR(10)'),B=a.x.value('/r[2]','VARCHAR(10)')) b
-
- /*
- A B
- ---------- ----------
- 双桥 9.6米
- aa bb
-
- (2 行受影响)
- */
- 把项目串的编码用相应名称代替
- ------------------------------------------------------------------------
- -- Author: HappyFlyStone
- -- Date : 2009-01-04 20:57:59
- -- Ver: Microsoft SQL Server 2005 - 9.00.2047.00 (Intel X86)
- -- Apr 14 2006 01:12:25
- -- Copyright (c) 1988-2005 Microsoft Corporation
- -- Standard Edition on Windows NT 5.(Build 2195: Service Pack 4)
- --
- ------------------------------------------------------------------------
-
- -- Test Data: ta
- IF OBJECT_ID('ta') IS NOT NULL
- DROP TABLE ta
- Go
- CREATE TABLE ta(ID NVARCHAR(3),number varchar(20))
- Go
- INSERT INTO ta
- SELECT '001','1,2' UNION ALL
- SELECT '002','1,2,3'
- GO
- -- Test Data: tb
- IF OBJECT_ID('tb') IS NOT NULL
- DROP TABLE tb
- Go
- CREATE TABLE tb(ID INT,name NVARCHAR(7))
- Go
- INSERT INTO tb
- SELECT 1,'测试一' UNION ALL
- SELECT 2,'测试二' UNION ALL
- SELECT 3,'测试三'
- GO
- --Start
- --查询一
- SELECT C.ID,NUMBER = STUFF(REPLACE(REPLACE((
- SELECT B.NAME AS NAME
- FROM TA A
- LEFT JOIN
- ( SELECT ID,NAME
- FROM TB
- ) B
- ON CHARINDEX(','+LTRIM(B.ID)+',',','+A.NUMBER+',')>0
- WHERE A.ID = C.ID
- FOR XML AUTO
- ),'"/><B NAME="',','),'"/>',''),1,9,'')
- FROM TA C
- --查询二
- SELECT A.ID,NUMBER=STUFF(
- (SELECT ','+NAME
- FROM TB
- WHERE CHARINDEX(','+LTRIM(ID)+',',','+A.NUMBER+',')>0
- FOR XML PATH(''),ROOT('R'),TYPE).value('/R[1]','NVARCHAR(MAX)')
- ,1,1,'')
- FROM TA A
-
- --Result:
- /*
- id number
- ---- -----------------
- 001 测试一,测试二
- 002 测试一,测试二,测试三
-
- (2 行受影响)
- */
- --End
- 最后我们来说说折分吧
如:
- /*
- id value
- ----------- -----------
- 1 aa,bb
- 2 aaa,bbb,ccc
- 欲按id,分拆value列, 分拆后结果如下:
- id value
- ----------- --------
- 1 aa
- 1 bb
- 2 aaa
- 2 bbb
- 2 ccc
- */
一般我们在下会借助中间生成一个连续的序列,然后和表关联折分,在下我们可借助CTE生成一个序列然后再拆分
- ------------------------------------------------------------------------
- -- Author: HappyFlyStone
- -- Date : 2009-01-04 20:57:59
- -- Ver: Microsoft SQL Server 2005 - 9.00.2047.00 (Intel X86)
- -- Apr 14 2006 01:12:25
- -- Copyright (c) 1988-2005 Microsoft Corporation
- -- Standard Edition on Windows NT 5.0Build 2195: Service Pack 4)
- ------------------------------------------------------------------------
-
- CREATE TABLE TB(ID INT,VALUE VARCHAR(30))
- INSERT INTO TB VALUES(1,'AA,BB')
- INSERT INTO TB VALUES(2,'AAA,BBB,CCC')
- GO
- SELECT A.ID, B.VALUE
- FROM(
- SELECT ID,
- [value] = CONVERT(XML,'<ROOT><V>' + REPLACE([VALUE], ',', '</V><V>') + '</V></ROOT>')
- FROM tb
- )A
- OUTER APPLY(
- SELECT value = N.v.value('.', 'varchar(100)')
- FROM A.[value].nodes('/ROOT/V') N(v)
- )B
-
- DROP TABLE tb
- --查询结果
- /*
- id value
- ----------- ------------------------------
- 1 aa
- 1 bb
- 2 aaa
- 2 bbb
- 2 ccc
-
- (5 行受影响)
- */