解决行转列,列分行,行合并列的疑难问题
来源:岁月联盟
时间:2008-02-02
本文以SQL Server 2000为例:
SQL code/*================fcuandy========2008.1.10================*/CREATE TABLE ta(id INT IDENTITY(1,1),cid INT,name VARCHAR(10))GOINSERT ta SELECT 1,'a'UNION ALL SELECT 1,'b'UNION ALL SELECT 1,'c'UNION ALL SELECT 2,'d'UNION ALL SELECT 3,'e'UNION ALL SELECT 3,'f'GO/* |
示例1
同一分类中取1条或n条。单表及多表的写法*/-------------------------------------------------------------以ta为例,cid为分类id,每个id取一条,我以取最小id为约束条件SELECT a.* FROM ta a WHERE NOT EXISTS(SELECT 1 FROM ta WHERE cid=a.cid AND id<a.id)SELECT a.* FROM ta a WHERE 1>(SELECT COUNT(*) FROM ta WHERE cid=a.cid AND id<a.id)SELECT a.* FROM ta a WHERE id IN (SELECT TOP 1 ID FROM ta WHERE cid=a.cid ORDER BY id)SELECT a.* FROM ta a WHERE id = (SELECT TOP 1 ID FROM ta WHERE cid=a.cid ORDER BY id)SELECT a.* FROM ta a WHERE id IN (SELECT MIN(ID) FROM ta WHERE cid=a.cid)SELECT a.* FROM ta a WHERE id = (SELECT MIN(ID) FROM ta WHERE cid=a.cid)SELECT a.* FROM ta aINNER JOIN (SELECT MIN(id) mi FROM ta GROUP BY cid) b ON id = mi--etc.其它的组合写法再不累赘--以cid为分类,每个cid取id最小的2条记录,2可以适当修改SELECT a.* FROM ta a WHERE 2>(SELECT COUNT(*) FROM ta WHERE cid=a.cid AND id<a.id)SELECt a.* FROM ta a WHERE id IN (SELECT TOP 2 ID FROM ta WHERE cid=a.cid ORDER BY ID)SELECT a.* FROM ta aINNER JOIN(SELECT ID,CNT=(SELECT COUNT(*) FROM ta WHERE cid=x.cid AND id<x.id) FROM ta x) b ON a.id = b.id AND cnt<2--etc.其它的组合写法省略 |
以上是针对ta单表。 假如多表,下面以二表为例
CREATE TABLE tb(cid INT,className VARCHAR(10))GOINSERT tb SELECT 1,'A'UNION ALL SELECT 2,'B'UNION ALL SELECT 3,'C'GO--ta,tb以cid关联,取每个cid中id最小的一条记录,需要 tb.className,tb.cid,ta.id,ta.name列。SELECT b.*,a.* FROM tb bINNER JOIN ta a ON a.cid = b.cid WHERE NOT EXISTS(SELECT 1 FROM ta WHERE cid=a.cid AND id<a.id)GO--多表与单表取数思路一对致,多一次连表操作,可以照上面单表的把其它写法改出来.--同样,取每个cid中id最小的前n条记录,一样的方法--需要注重的是连表时,可以用内连,左连,或是老式的多表写法(from ta ,tb where ta.cid=tb.cid默认转换为内连),采用哪种方式依具你的业务需求。--------------------------------------------/* |
示例2
所谓的多行同组合并
*/--以ta为例,以cid分组合并,产生如下的数据结果/*cid nameS1 a,b,c2 d3 e,f*/--函数实现CREATE FUNCTION myJoinSTR(@cid INT)RETURNS VARCHAR(1000)ASBEGINDECLARE @s VARCHAR(1000)SELECT @s=ISNULL(@s ',','') name FROM ta WHERE cid = @cidRETURN @sENDGOSELECT DISTINCT cid,dbo.myJoinSTR(cid) nameS FROM taGO/* |
示例3
列拆分为行.
以上例生成的数据格式为示例表,将a,b,c以,分融成行,即上个示例的反操作
*/SELECT DISTINCT cid,dbo.myJoinSTR(cid) nameS INTO tx FROM taGOSELECT * FROM txGO--以系统表构建identity列,并以连表方式来将列拆成行SELECT IDENTITY(INT,1,1) id INTO # FROM syscolumns,sysobjectsSELECT id,cid,RIGHT(STUFF(nameS ',',id,LEN(names),''),CHARINDEX(',',REVERSE(STUFF(',' nameS ',',id,LEN(names),'')))) name FROM tx aINNER JOIN # b ON SUBSTRING(names ',',id,1)=','ORDER BY cid--以动态语句或循环,或函数的方式略去 |
示例4
行转列*/--单表,以ta为例. 静态行转列,设cid所有出现的可能值已知SELECT cid_1=MAX(CASE WHEN cid=1 THEN name ELSE NULL END), cid_2=MAX(CASE WHEN cid=2 THEN name ELSE NULL END), cid_3=MAX(CASE WHEN cid=3 THEN name ELSE NULL END) FROM ta --单表,以ta为例,动态行转列,设cid所有出现的可能值未知DECLARE @s VARCHAR(8000)SET @s=''SELECT @s=@s ',cid_' RTRIM(cid) '= MAX(CASE WHEN cid=' RTRIM(cid) ' THEN name ELSE null END) ' FROM ta GROUP BY cidSELECT @s='SELECT ' STUFF(@s,1,1,'') ' FROM ta'--你可以在这里PRINT @s 看看,就知道跟上面的静态行转列一样的了。 会写静态行转列,就没理由写不出动态的。多表的同理,把多表的静态行转列写出来,那么动态的也就出来了EXEC(@s)GODROP TABLE tx,#GODROP TABLE ta,tbDROP FUNCTION myJoinSTRGO |