SQL行转列和列转行代码详解
行列互转,是一个经常遇到的需求。实现的方法,有casewhen方式和2005之后的内置pivot和unpivot方法来实现。
在读了技术内幕那一节后,虽说这些解决方案早就用过了,却没有系统性的认识和总结过。为了加深认识,再总结一次。
行列互转,可以分为静态互转,即事先就知道要处理多少行(列);动态互转,事先不知道处理多少行(列)。
--创建测试环境 USEtempdb; GO IFOBJECT_ID('dbo.Orders')ISNOTNULL DROPTABLEdbo.Orders; GO CREATETABLEdbo.Orders ( orderidintNOTNULLPRIMARYKEYNONCLUSTERED, orderdatedatetimeNOTNULL, empidintNOTNULL, custidvarchar(5)NOTNULL, qtyintNOTNULL ); CREATEUNIQUECLUSTEREDINDEXidx_orderdate_orderid ONdbo.Orders(orderdate,orderid); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(30001,'20020802',3,'A',10); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(10001,'20021224',1,'A',12); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(10005,'20021224',1,'B',20); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(40001,'20030109',4,'A',40); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(10006,'20030118',1,'C',14); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(20001,'20030212',2,'B',12); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(40005,'20040212',4,'A',10); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(20002,'20040216',2,'C',20); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(30003,'20040418',3,'B',15); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(30004,'20020418',3,'C',22); INSERTINTOdbo.Orders(orderid,orderdate,empid,custid,qty) VALUES(30007,'20020907',3,'D',30); GO
行转列-静态方案:
--行转列的静态方案一:CASEWHEN,兼容sql2000 selectcustid, sum(casewhenYEAR(orderdate)=2002thenqtyend)as[2002], sum(casewhenYEAR(orderdate)=2003thenqtyend)as[2003], sum(casewhenYEAR(orderdate)=2004thenqtyend)as[2004] fromorders groupbycustid; GO --行转列的静态方案二:PIVOT,sql2005及以后版本 select* from(selectcustid,YEAR(orderdate)asyears,qtyfromorders)asord pivot(sum(qty)foryearsin([2002],[2003],[2004]))asp GO
行转列-动态方案:加入了xml处理和SQL注入预防判断
--既然是用到了动态SQL,就有一个老话题:SQL注入。建一个注入性字符的判断函数。 CREATEFUNCTION[dbo].[fn_CheckSQLInjection] ( @Colnvarchar(4000) ) RETURNSBIT--如果存在可能的注入字符返回true,反之返回false AS BEGIN DECLARE@resultbit; IF UPPER(@Col)LIKEUPPER(N'%0x%') ORUPPER(@Col)LIKEUPPER(N'%;%') ORUPPER(@Col)LIKEUPPER(N'%''%') ORUPPER(@Col)LIKEUPPER(N'%--%') ORUPPER(@Col)LIKEUPPER(N'%/*%*/%') ORUPPER(@Col)LIKEUPPER(N'%EXEC%') ORUPPER(@Col)LIKEUPPER(N'%xp_%') ORUPPER(@Col)LIKEUPPER(N'%sp_%') ORUPPER(@Col)LIKEUPPER(N'%SELECT%') ORUPPER(@Col)LIKEUPPER(N'%INSERT%') ORUPPER(@Col)LIKEUPPER(N'%UPDATE%') ORUPPER(@Col)LIKEUPPER(N'%DELETE%') ORUPPER(@Col)LIKEUPPER(N'%TRUNCATE%') ORUPPER(@Col)LIKEUPPER(N'%CREATE%') ORUPPER(@Col)LIKEUPPER(N'%ALTER%') ORUPPER(@Col)LIKEUPPER(N'%DROP%') SET@result=1 ELSE SET@result=0 return@result END GO --行转列的动态方案一:CASEWHEN,兼容sql2000 DECLARE@TTABLE(yearsINTNOTNULLPRIMARYKEY); INSERTINTO@T SELECTDISTINCTYEAR(orderdate)fromorders; DECLARE@YINT; SET@Y=(SELECTMIN(years)from@T); DECLARE@SQLNVARCHAR(4000)=N''; WHILE@YISNOTNULL BEGIN SET@SQL=@SQL+N',sum(casewhenYEAR(orderdate)='+CAST(@YASNVARCHAR(4))+N'thenqtyend)as'+QUOTENAME(@Y); SET@Y=(SELECTMIN(years)from@Twhereyears>@Y); END IFdbo.fn_CheckSQLInjection(@SQL)=0 SET@SQL=N'SELECTcustid'+@SQL+N'FROMordersgroupbycustid' PRINT@SQL EXECsp_executesql@SQL GO --行转列的动态方案二:PIVOT,sql2005及以后版本 DECLARE@TTABLE(yearsINTNOTNULLPRIMARYKEY); INSERTINTO@T SELECTDISTINCTYEAR(orderdate)fromorders; DECLARE@YINT; SET@Y=(SELECTMIN(years)from@T); DECLARE@SQLNVARCHAR(4000)=N''; --这里使用了xml处理来处理类组字符串 SET@SQL=STUFF((SELECTN','+QUOTENAME(years)FROM@T FORXMLPATH('')),1,1,N''); IFdbo.fn_CheckSQLInjection(@SQL)=0 SET@SQL=N'select*from(selectDISTINCTcustid,YEAR(orderdate)asyears,qtyfromorders)asord pivot(sum(qty)foryearsin('+@SQL+N'))asp'; PRINT@SQL; EXECSP_EXECUTESQL@SQL; GO
列转行:
--列转行的静态方案:UNPIVOT,sql2005及以后版本 SELECT*FROMdbo.pvtCustOrders SELECTcustid,years,qty fromdbo.pvtCustOrders unpivot(qtyforyearsin([2002],[2003],[2004]))asup GO --列转行的动态方案:UNPIVOT,sql2005及以后版本 --因为行是动态所以这里就从INFORMATION_SCHEMA.COLUMNS视图中获取列来构造行,同样也使用了XML处理。 DECLARE@SQLNVARCHAR(4000)=N''; SET@SQL=STUFF((SELECTN','+QUOTENAME(COLUMN_NAME)FROMINFORMATION_SCHEMA.COLUMNS WHEREORDINAL_POSITION>1ANDTABLE_NAME='PvtCustOrders' FORXMLPATH('')),1,1,N'') SET@SQL=N'SELECTcustid,years,qty fromdbo.pvtCustOrders unpivot(qtyforyearsin('+@SQL+'))asup'; PRINT@SQL; EXECSP_EXECUTESQL@SQL;
总结
以上就是本文关于SQL行转列和列转行代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:Mysql中FIND_IN_SET()和IN区别简析、浅谈sqlserver下float的不确定性、MYSQL子查询和嵌套查询优化实例解析等,有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对毛票票网站的支持!