【问题标题】:PIVOT SQL Data & Fill In BlanksPIVOT SQL 数据和填空
【发布时间】:2012-03-05 03:50:06
【问题描述】:

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

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

我想要完成的事情可能吗?我是否朝着正确的方向前进?任何帮助将不胜感激!

【问题讨论】:

  • @subt13,。我从来没有用过它,我不太清楚如何“填空”。例如,我需要为未包含在第一个结果集中的所有月份显示零。
  • 使用pivot,这就是它的用途。您可以使用 isnull(..., 0) 处理空值
  • @subt13,如果我能在几个小时后自己弄清楚,我就不会在 SO 上发帖了。
  • 没问题。请看我的帖子。

标签: sql sql-server-2005 tsql pivot


【解决方案1】:

如果您需要动态枢轴: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

【讨论】:

  • case when month([date]) &lt; 5是什么意思?
  • @AaronBertrand - 添加示例数据。
  • 好的,我明白了。希望 OP 明白这不是解决方案的一部分。现在,剩下的 10-11 个月呢? :-)
  • @AaronBertrand - 我相信 OP 的能力 :)。我不想全部输入。
  • 当然,其他月份应该是显而易见的。但是作为答案的一部分,混合样本数据让我非常困惑。随意借用我的表格作为示例数据,我认为这可以更好地将问题与答案分离。此外,您的解决方案似乎必须对 PIVOT 部分进行硬编码...在 3 月或 4 月看起来如何?
【解决方案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

【讨论】:

  • 您可能会以类似的方式使用动态 SQL 构建 PIVOT。您也可以考虑将年份添加到列名中,以便结果明确。最后,输出 SQL 没有 ORDER BY - 你的意思是 ORDER BY InvType DESC 还是你不关心订单?
  • 完美运行。我以前从未使用过动态 SQL。这是一个完美的例子,说明了为什么我需要更多地研究它。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-09
  • 1970-01-01
相关资源
最近更新 更多