如何PIVOT这个SQL来实现所需的结果?

时间:2012-01-24 01:45:56

标签: sql sql-server sql-server-2008 tsql pivot

我有一个存储过程,它生成一个这样的表:

PeriodStart     PeriodEnd     TotalActive     TotalAdded     TotalRemoved
2011-03-01      2011-03-07    123             456            789
2011-03-08      2011-03-14    567             789            123
2011-03-15      2011-03-21    444             555            666

等...

我需要在一个如下所示的表格中显示结果:

          03-01-2011     03-08-2011     03-15-2011
Active    123            567            444
Added     456            789            555
Removed   789            123            666

我之前从未使用过PIVOT所以我有点不确定如何修改下面的SQL以获得所需的结果。这是sproc:

ALTER PROCEDURE [dbo].[TrendReport]
    @DateStart datetime,
    @DateEnd datetime,
    @Frequency varchar(5)
AS
BEGIN
    SELECT
      PeriodStart,
      PeriodEnd,
      SUM(TotalActive) As TotalActive,
      SUM(TotalAdded) As TotalAdded,
      SUM(TotalRemoved) As TotalRemoved
    FROM (
      SELECT
        PeriodStart = CASE @Frequency
          WHEN 'day'     THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, [Date]))
          WHEN 'week'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(DAY, 1 - DATEPART(WEEKDAY, [Date]), [Date])))
          WHEN 'month'   THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(MONTH,   DATEDIFF(MONTH,   0, [Date]), 0)))
          WHEN 'quarter' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]), 0)))
          WHEN 'year'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(YEAR,    DATEDIFF(YEAR,    0, [Date]), 0)))
        END,
        PeriodEnd   = CASE @Frequency
          WHEN 'day'     THEN DATEADD(s, -1, DATEADD(day, 1, DATEDIFF(DAY, 0, [Date])))
          WHEN 'week'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, 7 - DATEPART(WEEKDAY, [Date]), [Date]))))
          WHEN 'month'   THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(MONTH,   DATEDIFF(MONTH,   0, [Date]) + 1, 0)))))
          WHEN 'quarter' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]) + 1, 0)))))
          WHEN 'year'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(YEAR,    DATEDIFF(YEAR,    0, [Date]) + 1, 0)))))
        END,
        TotalActive,
        TotalAdded,
        TotalRemoved
      FROM TrendReport P
      WHERE [Date] BETWEEN @DateStart AND @DateEnd
    ) s
    GROUP BY
      PeriodStart,
      PeriodEnd
    ORDER BY PeriodStart

END

2 个答案:

答案 0 :(得分:1)

实际上,您需要首先应用UNPIVOT将三种数量类型从列旋转到行,然后应用PIVOT将周期开始日期旋转到列并聚合数量。

这是使用PIVOT运算符的事情,你需要知道你将提前有哪些列(03-01-2011,03-08-2011,03-15-2011),它将需要硬编码进入查询(除非你想要使用动态sql来解决这个问题,我将在生产中使用之前阅读它)

以下是解决方案:

WITH CTE AS (
    SELECT
      PeriodStart,
      PeriodEnd,
      SUM(TotalActive) As TotalActive,
      SUM(TotalAdded) As TotalAdded,
      SUM(TotalRemoved) As TotalRemoved
    FROM (
      SELECT
        PeriodStart = CASE @Frequency
          WHEN 'day'     THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, [Date]))
          WHEN 'week'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(DAY, 1 - DATEPART(WEEKDAY, [Date]), [Date])))
          WHEN 'month'   THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(MONTH,   DATEDIFF(MONTH,   0, [Date]), 0)))
          WHEN 'quarter' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]), 0)))
          WHEN 'year'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(YEAR,    DATEDIFF(YEAR,    0, [Date]), 0)))
        END,
        PeriodEnd   = CASE @Frequency
          WHEN 'day'     THEN DATEADD(s, -1, DATEADD(day, 1, DATEDIFF(DAY, 0, [Date])))
          WHEN 'week'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, 7 - DATEPART(WEEKDAY, [Date]), [Date]))))
          WHEN 'month'   THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(MONTH,   DATEDIFF(MONTH,   0, [Date]) + 1, 0)))))
          WHEN 'quarter' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]) + 1, 0)))))
          WHEN 'year'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(YEAR,    DATEDIFF(YEAR,    0, [Date]) + 1, 0)))))
        END,
        TotalActive,
        TotalAdded,
        TotalRemoved
      FROM TrendReport P
      WHERE [Date] BETWEEN @DateStart AND @DateEnd
    ) s
    GROUP BY
      PeriodStart,
      PeriodEnd
    ORDER BY PeriodStart

), U AS (
    SELECT qty_type, PeriodStart, qty
    FROM CTE
    UNPIVOT(qty FOR qty_type IN([TotalActive], [TotalAdded], [TotalRemoved]))Q
)     
SELECT * FROM U 
PIVOT (SUM(Qty) FOR PeriodStart IN([03/01/2011], [03/08/2011], [03/15/2011])) AS P

答案 1 :(得分:1)

目前我的机器上没有SQL实例,因此语法可能存在一些问题。但是,尝试一下......这是我通过J Cooper提到的动态SQL的狡猾尝试:

CREATE TABLE #Temp1 (ID INT IDENTITY(1,1), PeriodStart DATETIME, PeriodEnd DATETIME, TotalActive INT, TotalAdded INT, TotalRemoved INT)

INSERT INTO #Temp1 (PeriodStart, PeriodEnd, TotalActive, TotalAdded, TotalRemoved)
 SELECT
      PeriodStart,
      PeriodEnd,
      TotalActive,
      TotalAdded,
      TotalRemoved
    FROM (
      SELECT
    PeriodStart = CASE @Frequency
      WHEN 'day'     THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, [Date]))
      WHEN 'week'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(DAY, 1 - DATEPART(WEEKDAY, [Date]), [Date])))
      WHEN 'month'   THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(MONTH,   DATEDIFF(MONTH,   0, [Date]), 0)))
      WHEN 'quarter' THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]), 0)))
      WHEN 'year'    THEN DATEADD(hour, 0, DATEDIFF(DAY, 0, DATEADD(YEAR,    DATEDIFF(YEAR,    0, [Date]), 0)))
    END,
    PeriodEnd   = CASE @Frequency
      WHEN 'day'     THEN DATEADD(s, -1, DATEADD(day, 1, DATEDIFF(DAY, 0, [Date])))
      WHEN 'week'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, 7 - DATEPART(WEEKDAY, [Date]), [Date]))))
      WHEN 'month'   THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(MONTH,   DATEDIFF(MONTH,   0, [Date]) + 1, 0)))))
      WHEN 'quarter' THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(QUARTER, DATEDIFF(QUARTER, 0, [Date]) + 1, 0)))))
      WHEN 'year'    THEN DATEADD(s, -1, DATEADD(hour, 0, DATEDIFF(DAY, -1, DATEADD(DAY, -1, DATEADD(YEAR,    DATEDIFF(YEAR,    0, [Date]) + 1, 0)))))
    END,
    TotalActive,
    TotalAdded,
    TotalRemoved
      FROM TrendReport P
      WHERE [Date] BETWEEN @DateStart AND @DateEnd
    ) s
        GROUP BY
      PeriodStart,
      PeriodEnd
    ORDER BY PeriodStart

DECLARE @CreatePivot VARCHAR(MAX), @PivotColumns VARCHAR(MAX)
SET @CreatePivot = 'CREATE TABLE #Temp2 (BaseColumn VARCHAR(25)'
DECLARE @Count INT, @CurrentDateBeingChecked DATETIME
SET @Count = 1
WHILE(SELECT COUNT(*) FROM #Temp1 WHERE ID >= @Count) > 0
BEGIN
    SELECT @CurrentDateBeingChecked = PeriodStart FROM #Temp1 WHERE ID = @Count
    IF @Count > 1
        SET @PivotColumns = @PivotColumns + ','
    SET @PivotColumns = @PivotColumns + '[' + CAST(@CurrentDateBeingChecked AS VARCHAR(15) + ']'
    SET @CreatePivot = @CreatePivot + ', [' + CAST(@CurrentDateBeingChecked AS VARCHAR(15) + '] INT' 
    SET @Count = @Count + 1
END

SET @CreatePivot = @CreatePivot + ')'

sp_executesql(@CreatePivot)

DECLARE @PivotStatementToRun VARCHAR(MAX)
SET @PivotStatementToRun = 
'
INSERT INTO #Temp2
SELECT 'Active' AS BaseColumn,
' + @PivotColumns + '
FROM
(SELECT PeriodStart, TotalActive
 FROM #Temp1) AS SourceTable
PIVOT
(
    SUM(TotalActive)
    FOR PeriodStart IN (
    ' + @PivotColumns + ')
) AS PivotTable;
'
sp_executesql(@PivotStatementToRun)


SET @PivotStatementToRun = 
'
INSERT INTO #Temp2
SELECT 'Added' AS BaseColumn,
' + @PivotColumns + '
FROM
(SELECT PeriodStart, TotalAdded
 FROM #Temp1) AS SourceTable
PIVOT
(
    SUM(TotalAdded)
    FOR PeriodStart IN (
    ' + @PivotColumns + ')
) AS PivotTable;
'
sp_executesql(@PivotStatementToRun)

SET @PivotStatementToRun = 
'
INSERT INTO #Temp2
SELECT 'Removed' AS BaseColumn,
' + @PivotColumns + '
FROM
(SELECT PeriodStart, TotalRemoved
 FROM #Temp1) AS SourceTable
PIVOT
(
    SUM(TotalRemoved)
    FOR PeriodStart IN (
    ' + @PivotColumns + ')
) AS PivotTable;
'
sp_executesql(@PivotStatementToRun)

SELECT * FROM #Temp2