zoukankan      html  css  js  c++  java
  • SQL 行转列和列转行

    行列互转,是一个经常遇到的需求。实现的方法,有case when方式和2005之后的内置pivot和unpivot方法来实现。

    在读了技术内幕那一节后,虽说这些解决方案早就用过了,却没有系统性的认识和总结过。为了加深认识,再总结一次。

    行列互转,可以分为静态互转,即事先就知道要处理多少行(列);动态互转,事先不知道处理多少行(列)。

     1 --创建测试环境
     2 USE tempdb;
     3 GO
     4 
     5 IF OBJECT_ID('dbo.Orders') IS NOT NULL
     6   DROP TABLE dbo.Orders;
     7 GO
     8 
     9 CREATE TABLE dbo.Orders
    10 (
    11   orderid   int        NOT NULL PRIMARY KEY NONCLUSTERED,
    12   orderdate datetime   NOT NULL,
    13   empid     int        NOT NULL,
    14   custid    varchar(5) NOT NULL,
    15   qty       int        NOT NULL
    16 );
    17 
    18 CREATE UNIQUE CLUSTERED INDEX idx_orderdate_orderid
    19   ON dbo.Orders(orderdate, orderid);
    20 
    21 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    22   VALUES(30001, '20020802', 3, 'A', 10);
    23 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    24   VALUES(10001, '20021224', 1, 'A', 12);
    25 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    26   VALUES(10005, '20021224', 1, 'B', 20);
    27 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    28   VALUES(40001, '20030109', 4, 'A', 40);
    29 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    30   VALUES(10006, '20030118', 1, 'C', 14);
    31 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    32   VALUES(20001, '20030212', 2, 'B', 12);
    33 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    34   VALUES(40005, '20040212', 4, 'A', 10);
    35 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    36   VALUES(20002, '20040216', 2, 'C', 20);
    37 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    38   VALUES(30003, '20040418', 3, 'B', 15);
    39 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    40   VALUES(30004, '20020418', 3, 'C', 22);
    41 INSERT INTO dbo.Orders(orderid, orderdate, empid, custid, qty)
    42   VALUES(30007, '20020907', 3, 'D', 30);
    43 GO

    行转列-静态方案:

     1 --行转列的静态方案一:CASE WHEN,兼容sql2000
     2 select custid,
     3 sum(case when YEAR(orderdate)=2002 then qty end) as [2002],
     4 sum(case when YEAR(orderdate)=2003 then qty end) as [2003],
     5 sum(case when YEAR(orderdate)=2004 then qty end) as [2004]
     6 from orders
     7 group by custid;
     8 GO
     9 --行转列的静态方案二:PIVOT,sql2005及以后版本
    10 select *
    11 from (select custid,YEAR(orderdate) as years,qty from orders) as ord
    12 pivot(sum(qty) for years in([2002],[2003],[2004]))as p
    13 GO

    行转列-动态方案:加入了xml处理和SQL注入预防判断

     1 --既然是用到了动态SQL,就有一个老话题:SQL注入。建一个注入性字符的判断函数。
     2 CREATE FUNCTION [dbo].[fn_CheckSQLInjection]
     3 (
     4  @Col nvarchar(4000)
     5 )
     6 RETURNS BIT --如果存在可能的注入字符返回true,反之返回false
     7 AS
     8 BEGIN
     9 DECLARE @result bit;
    10   IF  
    11      UPPER(@Col) LIKE UPPER(N'%0x%')
    12   OR UPPER(@Col) LIKE UPPER(N'%;%')
    13   OR UPPER(@Col) LIKE UPPER(N'%''%')
    14   OR UPPER(@Col) LIKE UPPER(N'%--%')
    15   OR UPPER(@Col) LIKE UPPER(N'%/*%*/%')
    16   OR UPPER(@Col) LIKE UPPER(N'%EXEC%')
    17   OR UPPER(@Col) LIKE UPPER(N'%xp_%')
    18   OR UPPER(@Col) LIKE UPPER(N'%sp_%')
    19   OR UPPER(@Col) LIKE UPPER(N'%SELECT%')
    20   OR UPPER(@Col) LIKE UPPER(N'%INSERT%')
    21   OR UPPER(@Col) LIKE UPPER(N'%UPDATE%')
    22   OR UPPER(@Col) LIKE UPPER(N'%DELETE%')
    23   OR UPPER(@Col) LIKE UPPER(N'%TRUNCATE%')
    24   OR UPPER(@Col) LIKE UPPER(N'%CREATE%')
    25   OR UPPER(@Col) LIKE UPPER(N'%ALTER%')
    26   OR UPPER(@Col) LIKE UPPER(N'%DROP%')
    27   SET @result=1
    28  ELSE
    29   SET @result=0
    30  return @result
    31 END
    32 GO
    33 
    34 --行转列的动态方案一:CASE WHEN,兼容sql2000
    35 DECLARE @T TABLE (years INT NOT NULL PRIMARY KEY);
    36 INSERT INTO @T 
    37 SELECT DISTINCT YEAR(orderdate) from orders;
    38 DECLARE @Y INT;
    39 SET @Y=(SELECT MIN(years) from @T);
    40 DECLARE @SQL NVARCHAR(4000)=N'';
    41 WHILE @Y IS NOT NULL
    42 BEGIN
    43   SET @SQL=@SQL+N',sum(case when YEAR(orderdate)='+CAST(@Y AS NVARCHAR(4)) +N' then qty end) as '+QUOTENAME(@Y);
    44   SET @Y=(SELECT MIN(years) from @T where years>@Y);
    45 END
    46 IF  dbo.fn_CheckSQLInjection(@SQL)=0
    47 SET @SQL=N'SELECT custid'+@SQL+N' FROM orders group by custid'
    48 PRINT @SQL
    49 EXEC sp_executesql  @SQL
    50 GO
    51 
    52 --行转列的动态方案二:PIVOT,sql2005及以后版本
    53 DECLARE @T TABLE (years INT NOT NULL PRIMARY KEY);
    54 INSERT INTO @T 
    55 SELECT DISTINCT YEAR(orderdate) from orders;
    56 DECLARE @Y INT;
    57 SET @Y=(SELECT MIN(years) from @T);
    58 DECLARE @SQL NVARCHAR(4000)=N'';
    59 
    60     --这里使用了xml处理来处理类组字符串
    61 SET @SQL=STUFF((SELECT N','+QUOTENAME(years) FROM @T
    62  FOR XML PATH('')),1,1,N'');
    63 IF  dbo.fn_CheckSQLInjection(@SQL)=0
    64 SET @SQL=N'select * from (select DISTINCT custid,YEAR(orderdate) as years,qty from orders) as ord
    65 pivot(sum(qty) for years in('+@SQL+N'))as p';
    66 PRINT @SQL;
    67 EXEC SP_EXECUTESQL @SQL;
    68 GO

    列转行:

     1 --列转行的静态方案:UNPIVOT,sql2005及以后版本
     2 SELECT * FROM dbo.pvtCustOrders
     3 SELECT custid,years,qty
     4 from dbo.pvtCustOrders
     5 unpivot(qty for years in([2002],[2003],[2004]))as up
     6 GO
     7 --列转行的动态方案:UNPIVOT,sql2005及以后版本
     8 --因为行是动态所以这里就从INFORMATION_SCHEMA.COLUMNS视图中获取列来构造行,同样也使用了XML处理。
     9 DECLARE @SQL NVARCHAR(4000)=N'';
    10 SET @SQL=STUFF((SELECT N','+QUOTENAME(COLUMN_NAME ) FROM INFORMATION_SCHEMA.COLUMNS
    11 WHERE ORDINAL_POSITION>1 AND TABLE_NAME='PvtCustOrders'
    12 FOR XML PATH('')),1,1,N'')
    13 SET @SQL=N'SELECT custid,years,qty
    14          from dbo.pvtCustOrders
    15          unpivot(qty for years in('+@SQL+'))as up';
    16 PRINT @SQL;
    17 EXEC SP_EXECUTESQL @SQL;

    转自:http://www.cnblogs.com/Joe-T/archive/2011/11/28/2266280.html

  • 相关阅读:
    并发与高并发(七)-线程安全性-原子性-atomic
    并发与高并发(六)-并发模拟代码
    springboot跨域请求接口示例
    关于Java编码规范
    并发与高并发(五)-并发模拟的三个工具
    springboot整合redis简单示例
    关于码云如何检出项目
    关于自动化打包部署Jenkins的使用和配置
    针对Oracle的一系列操作
    关于Linux下Oracle安装后启动的问题
  • 原文地址:https://www.cnblogs.com/cpcpc/p/3009021.html
Copyright © 2011-2022 走看看