PIVOT SQL数据&填空

时间:2012-02-13 18:15:18

标签: sql sql-server-2005 tsql pivot

我正在尝试转换这些数据:

ItemID  MonthAsInt  Month     Year  InvType     Quantity
4643    4           April     2011  Shipment    10
4643    5           May       2011  Shipment    10
4643    7           July      2011  Shipment    10
4643    8           August    2011  Destroy     10
4643    11          November  2011  Shipment    25
4643    12          December  2011  Picking     1

进入这个(基本上是12个月的快照):

              February    March    April     May    June    July    August    ...

Shipment      0           0        10        10     0       10      0
Picking       0           0        0         0      0       0       0
Destroy       ...

我搞砸了PIVIOT方法,但我运气不好。此时,我所拥有的只是GETDATE()GETDATE() - 12 months之间所需的日期列表(通过以下查询检索):

DECLARE @BeginDate DATETIME
DECLARE @EndDate DATETIME

SET @BeginDate = GETDATE();
SET @EndDate = DATEADD(MONTH, -12, GETDATE());

WITH CTE_DatesTable
AS
(
    SELECT @EndDate AS [Date]

    UNION ALL

    SELECT  DATEADD(dd, 1, [date])
    FROM    CTE_DatesTable
    WHERE   DATEADD(dd, 1, [date]) <= @BeginDate
)

SELECT DISTINCT DATENAME(MONTH, [date]) + ' ' 
        + CAST(YEAR([date]) AS VARCHAR(4)) AS MonthYear,
                YEAR([date]) AS YearAsInt,
                MONTH([Date]) AS MonthAsInt
FROM            CTE_DatesTable
ORDER BY        YEAR([date]), MONTH([Date])
OPTION          (MAXRECURSION 0)

查看操作中的查询here

我正在努力实现的目标是什么?我正朝着正确的方向前进吗?任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:2)

您可以在没有数据透视的情况下执行此操作(我也发现令人生畏的语法)。由于您事先不知道列的实际布局,我认为这对于动态SQL来说是最简单的。给出以下表/样本数据:

USE tempdb;
GO

CREATE TABLE dbo.foo
(
    ItemID INT, 
    MonthAsInt INT, 
    [Month] VARCHAR(12), 
    [Year] INT, 
    InvType VARCHAR(12), 
    Quantity INT
);

INSERT dbo.foo SELECT 4643,4 ,'April   ',2011,'Shipment',10
UNION ALL SELECT 4643,5 ,'May     ',2011,'Shipment',10
UNION ALL SELECT 4643,7 ,'July    ',2011,'Shipment',10
UNION ALL SELECT 4643,8 ,'August  ',2011,'Destroy ',10
UNION ALL SELECT 4643,11,'November',2011,'Shipment',25
UNION ALL SELECT 4643,12,'December',2011,'Picking ',1;

您可以使用更简单的CTE生成月份列表,并基于此构建动态SQL语句:

DECLARE @sql NVARCHAR(MAX) = N'';

;WITH n AS 
(
    SELECT TOP (12) d = DATEADD
    (
        MONTH, 
        -(ROW_NUMBER() OVER (ORDER BY [object_id]) - 1), 
        GETDATE()
    )
    FROM sys.objects
    ORDER BY d DESC
)
SELECT @sql = @sql + N',' + CHAR(13) + CHAR(10) + DATENAME(MONTH, d) 
    + ' = SUM(CASE WHEN [Year] = ' + CONVERT(VARCHAR(4), DATEPART(YEAR, d))
    + ' AND MonthAsInt = ' + CONVERT(VARCHAR(2), DATEPART(MONTH, d)) 
    + ' THEN Quantity ELSE 0 END)'
FROM n
ORDER BY d;

SELECT @sql = N'SELECT InvType' + @sql + '
    FROM dbo.foo
    GROUP BY InvType';

PRINT @sql;
-- EXEC sp_executesql @sql;

我把PRINT放在那里,所以你可以在运行之前测试它。我不确定您是否需要12个月或13个月,如果您需要13个月,则可以将TOP (12)更改为TOP (13),如果您不想要,则可以删除-1包括当前月份。

答案 1 :(得分:2)

如果您需要动态支点:see here

,否则

这是你如何做到这一点。当然,PIVOT要求您事先了解数据的样子。如果您需要动态数据透视表,那么人们已经编写了大量动态交叉表/数据透视查询/ sps。

DECLARE @BeginDate DATETIME
DECLARE @EndDate DATETIME

SET @BeginDate = GETDATE();
SET @EndDate = DATEADD(MONTH, -12, GETDATE());

WITH CTE_DatesTable
AS
(
    SELECT @EndDate AS [Date]

    UNION ALL

    SELECT  DATEADD(dd, 1, [date])
    FROM    CTE_DatesTable
    WHERE   DATEADD(dd, 1, [date]) <= @BeginDate
)

    SELECT DISTINCT DATENAME(MONTH, [date]) + ' ' + CAST(YEAR([date]) AS VARCHAR(4)) AS MonthYear,
                    YEAR([date]) AS YearAsInt,
                    MONTH([Date]) AS MonthAsInt,
                    case when month([date]) < 5 then 'Shipment' else 'Picking' end InvType,
                    floor(10 * RAND() * month([date])) Quantity
    into #orig
    FROM            CTE_DatesTable
    ORDER BY        YEAR([date]), MONTH([Date])
    OPTION          (MAXRECURSION 0)

update #orig 
set Quantity = null
where MonthYear = 'February 2011'

select * from #orig

select *
from
(
    select isnull(Quantity, 0) Quantity, MonthYear from #orig
) SourceTbl
PIVOT
(
    sum(Quantity)
    for MonthYear in ([February 2011], [March 2011])
) PivotTbl

drop table #orig

结果:

February 2011   March 2011
0                   29