zoukankan      html  css  js  c++  java
  • [MSSQL]PIVOT函数

    PIVOT在帮助中这样描述滴:

    可以使用 PIVOT 和 UNPIVOT 关系运算符将表值表达式更改为另一个表。PIVOT 通过将表达式某一列中的唯一值转换为输出中的多个列来旋转表值表达式,并在必要时对最终输出中所需的任何其余列值执行聚合。UNPIVOT 与 PIVOT 执行相反的操作,将表值表达式的列转换为列值。

    简单点理解就是行变列,UNPIVOT则是列变行,一个一个看

    测试用的数据及表结构:

    CREATE TABLE ShoppingCart(
        [Week] INT NOT NULL,
        [TotalPrice] DECIMAL DEFAULT(0) NOT NULL
    )
    INSERT INTO ShoppingCart([Week],[TotalPrice])
    SELECT 1,10 UNION ALL
    SELECT 2,20 UNION ALL
    SELECT 3,30 UNION ALL
    SELECT 4,40 UNION ALL
    SELECT 5,50 UNION ALL
    SELECT 6,60 UNION ALL
    SELECT 7,70
    SELECT * FROM ShoppingCart

    输出结果:

    image


    来看下PIVOT怎么把行变列:

    SELECT 'TotalPrice' AS [Week],[1],[2],[3],[4],[5],[6],[7]
    FROM ShoppingCart PIVOT(SUM(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) AS T


    输出结果

    image

    可以看出来,转换完成了,就这么个功能

    再看一个UNPIVOT函数,与上述功能相反,把列转成行

    我们直接使用WITH关键字把上述PIVOT查询当成源表,然后再使用UNPIVOT关键把它旋转回原来的模样,SQL脚本及结果如下:

    WITH P AS (
        SELECT 'TotalPrice' AS [Week],[1],[2],[3],[4],[5],[6],[7]
        FROM ShoppingCart PIVOT(SUM(TotalPrice) FOR [Week] IN([1],[2],[3],[4],[5],[6],[7])) 
        AS T
    )
    SELECT
        [WeekDay] AS [Week],
        [WeekPrice] AS [TotalPrice]
    FROM P
    UNPIVOT(
        [WeekPrice] FOR [WeekDay] IN([1],[2],[3],[4],[5],[6],[7])
    )AS FOO

    image

    OK介绍完了,大概功能如此,但使用起来远不止如此,灵活运用威力无穷~

    下边这个SQL语句,下边大段注释部分为前同事的作品,上半部分是我重写后的,可以看到,代码量减少了不少!多提宝贵意见!

       1: ALTER PROCEDURE [dbo].[Report_ApplyStat]
       2:  @TenantId int
       3: AS  
       4: BEGIN
       5:     /* 模板表 */
       6:     DECLARE @TEMP TABLE([aType] INT)
       7:     INSERT INTO @TEMP([aType])VALUES(1),(2);
       8:  
       9:     /* 查询条件 */
      10:     WITH CONDITION AS(
      11:         SELECT
      12:             SC.PersonId,
      13:             ISNULL(RE.PhaseId,0) AS [PhaseId],
      14:             DATEDIFF(MONTH,SC.ApplyDate,GETDATE()) AS DIFF
      15:         FROM SearchCV SC 
      16:             LEFT JOIN [REL_PersonJobStoreDB] RE ON SC.PersonId = RE.PersonId
      17:         WHERE SC.TenantId = @TenantId),
      18:     [ApplyCount] AS(SELECT 1 AS [TYPE],DATEDIFF(MONTH,ApplyDate,GETDATE()) AS DIFF,(PersonId) AS [ApplyCount] FROM SearchCV WHERE TenantId = @TenantId),
      19:     [OfferCount] AS(SELECT 2 AS [TYPE],DIFF,(PersonId) AS [OfferCount] FROM CONDITION WHERE PhaseId = 4    ),
      20:     [Result]    AS(
      21:         SELECT [TYPE] AS [aType],[1],[2],[3],[4],[5],[6] FROM [ApplyCount] PIVOT(COUNT([ApplyCount]) FOR DIFF IN([1],[2],[3],[4],[5],[6]))AS T UNION ALL
      22:         SELECT [TYPE] AS [aType],[1],[2],[3],[4],[5],[6] FROM [OfferCount] PIVOT(COUNT([OfferCount]) FOR DIFF IN([1],[2],[3],[4],[5],[6]))AS T )
      23:     SELECT
      24:         TP.[aType]                AS [aType],
      25:         ISNULL(RE.[6],0)        AS [M1],
      26:         ISNULL(RE.[5],0)        AS [M2],
      27:         ISNULL(RE.[4],0)        AS [M3],
      28:         ISNULL(RE.[3],0)        AS [M4],
      29:         ISNULL(RE.[2],0)        AS [M5],
      30:         ISNULL(RE.[1],0)        AS [M6]     
      31:     FROM @TEMP TP 
      32:         LEFT JOIN [Result] RE ON TP.[aType] = RE.[aType] Order by TP.aType
      33:  
      34: --定义表变量    
      35:  
      36: --declare @TenantId  INT
      37: --select @TenantId=100001
      38: --DECLARE @temp1 table(
      39: --      aType INT DEFAULT(0),
      40: --      mon int,--取今天与入库时间的月份差,如SELECT DATEDIFF(MONTH,'2010-7-1',GETDATE()) = 2
      41: --      total int
      42: --      )   
      43: --DECLARE @temp2 table(
      44: --      aType int NOT NULL default 0,
      45: --      mon varchar(40),
      46: --      total int)
      47: --DECLARE @stat_date DATETIME
      48: --DEClARE @Index int
      49: --declare @s varchar(4000), @sql varchar(4000)  
      50: --SET @s = '' 
      51: --SET @Index=1
      52: --SET  @stat_date = GETDATE()-150
      53: ----遍历出要统计的月份
      54: --WHILE    month(@stat_date) <= month(GETDATE())
      55: --BEGIN    
      56: --    Select  @s=@s+ 'M'+convert(varchar(20),@Index)+ ' = max(case when mon = '+QUOTENAME(left(CONVERT(varchar,@stat_date,102),7),'''')+ ' then total else 0 end),'   
      57: --    SET   @stat_date=DATEADD(MONTH, 1, @stat_date)
      58: --    SET @Index=@Index+1
      59:        
      60: --END
      61: --Select @s= SUBSTRING(@s,0,len(@s))
      62: --print @s
      63:  
      64: --应聘总数
      65: --insert into @temp1(aType,mon,total)
      66: --select 1,DATEDIFF(MONTH,CreateDate,GETDATE()) AS mon,COUNT(*) total  from [REL_PersonJobStoreDB] where TenantId=@TenantId group by DATEDIFF(MONTH,CreateDate,GETDATE())
      67: --IF(NOT EXISTS(SELECT 1 FROM @temp1))
      68: --BEGIN
      69: --    insert into @temp1(aType,mon,total)
      70: --    SELECT 1,-1,0
      71: --END
      72: ----匹配应聘标识号
      73: --Update @temp1 set  aType=1
      74:  
      75: --已录用人数
      76: --insert into @temp1(aType,mon,total)
      77: --select 2,DATEDIFF(MONTH,CreateDate,GETDATE()),COUNT(*) total from [REL_PersonJobStoreDB] where TenantId=@TenantId and PhaseId = 4 group by DATEDIFF(MONTH,CreateDate,GETDATE())
      78: --IF(NOT EXISTS(SELECT 1 FROM @temp1 WHERE aType=2))
      79: --BEGIN
      80: --    insert into @temp1(aType,mon,total)
      81: --    SELECT 2,-1,0
      82: --END
      83:  
      84: --SELECT * FROM @temp1
      85: --匹配应聘标识号
      86: --Update @temp2 set  aType=2
      87: ----合并表数据
      88: --insert into @temp1 select * from @temp2
      89:  
      90: --select * from @temp1
      91:  
      92: --DECLARE @DATE DATETIME
      93: --SET @DATE = GETDATE()
      94: --SELECT GETDATE(),DATEADD(MONTH,-1,GETDATE()),DATEDIFF(MONTH,GETDATE(),DATEADD(MONTH,1,GETDATE()))
      95: --select aType,mon,avg(total) total from @temp1
      96: -- group by atype,mon
      97:  
      98: --select [aType],
      99: --M1 = max(case when mon = 6 then total else 0 end),
     100: --M2 = max(case when mon = 5 then total else 0 end),
     101: --M3 = max(case when mon = 4 then total else 0 end),
     102: --M4 = max(case when mon = 3 then total else 0 end),
     103: --M5 = max(case when mon = 2 then total else 0 end),
     104: --M6 = max(case when mon = 1 then total else 0 end)
     105: --from 
     106: --(
     107: -- select aType,mon,avg(total) total from @temp1
     108: -- group by atype,mon
     109: -- ) aa 
     110: -- group by [aType]
     111:  
     112:  
     113:  
     114: -- print @sql 
     115: --exec(@sql)
     116:  
     117: END


    原代码即为32行以后,新代码为前32行,显然重构后减少了Bad Smell

    猜测您可能对下边的文章感兴趣

    SQL SERVER 2008 函数大全 - 字符串函数

    SQL2008系统统计函数

    [MSSQL]GROUPING SETS,ROLLUP,CUBE初体验

    [MSSQL]ROW_NUMBER函数

    [MSQL]RANK函数

    [MSSQL]NTILE另类分页有么有?!

    [MSQL]也说SQL中显示星期几函数

    [MSSQL]COALESCE与ISNULL函数

    [MSSQL]PIVOT函数

    [MSSQL]FOR XML AUTO I

    [MSSQL]FOR XML AUTO II

    [MSSQL]TRY…CATCH…通用格式

    如果您喜欢该博客请点击右下角推荐按钮,您的推荐是作者创作的动力!

  • 相关阅读:
    c++读写MySQL
    感叹游戏行业的飞速发展
    和真正的程序员在一起是怎样的体验
    程序媛是怎样找老公的
    IO和socket编程
    郁金香搜索引擎的方案
    实现一个自己的搜索引擎的初始规划
    JVM知识在离线数据中的运用
    看Lucene源码必须知道的基本规则和算法
    看Lucene源码必须知道的基本概念
  • 原文地址:https://www.cnblogs.com/kkun/p/2145690.html
Copyright © 2011-2022 走看看