2013-07-02 184 views
3

我有數據需要轉到網格視圖。它的2行加入一行,然後旋轉到列。SQL數據透視表 - 多行多列

以下是數據設置。我只包括1月至4月,但這將在所有月份運行,並且年份可能會改變,但它將始終連續2年。

Sample Data 


CREATE TABLE #tmpData (
    [YEAR] INT, 
    [AMOUNT] DECIMAL(12,2), 
    [PAX] INT, 
    [PRODUCTID] INT, 
    [MONTH] INT, 
    [MONTHNAME] VARCHAR(10) 
) 

INSERT INTO #tmpData SELECT 2012 ,3309  ,10 ,1 ,1 ,'January' 
INSERT INTO #tmpData SELECT 2013 ,3257.25 ,11 ,1 ,1 ,'January' 
INSERT INTO #tmpData SELECT 2012 ,4351.2  ,21 ,1 ,2 ,'February' 
INSERT INTO #tmpData SELECT 2013 ,3719.25 ,31 ,1 ,2 ,'February' 
INSERT INTO #tmpData SELECT 2012 ,4687  ,11 ,1 ,3 ,'March' 
INSERT INTO #tmpData SELECT 2013 ,4120.74 ,11 ,1 ,3 ,'March' 
INSERT INTO #tmpData SELECT 2012 ,6123.1  ,21 ,1 ,4 ,'April' 
INSERT INTO #tmpData SELECT 2013 ,5417.25 ,21 ,1 ,4 ,'April' 

INSERT INTO #tmpData SELECT 2012 ,5416.5  ,10 ,3 ,1 ,'January' 
INSERT INTO #tmpData SELECT 2013 ,6104.6  ,20 ,3 ,1 ,'January' 
INSERT INTO #tmpData SELECT 2012 ,9748.16 ,30 ,3 ,2 ,'February' 
INSERT INTO #tmpData SELECT 2013 ,10797.43 ,30 ,3 ,2 ,'February' 
INSERT INTO #tmpData SELECT 2012 ,12706.32 ,30 ,3 ,3 ,'March' 
INSERT INTO #tmpData SELECT 2013 ,13194.3 ,4 ,3 ,3 ,'March' 
INSERT INTO #tmpData SELECT 2012 ,16429.03 ,33 ,3 ,4 ,'April' 
INSERT INTO #tmpData SELECT 2013 ,14339.92 ,37 ,3 ,4 ,'April' 

SELECT * FROM #tmpData 
DROP TABLE #tmpData 

預期的效果

CREATE TABLE #tmpResults (
    [PRODUCTID]   INT, 

    [2013_JAN_AMOUNT] DECIMAL(12,2), 
    [2013_JAN_AMOUNT_%] INT, 
    [2013_JAN_PAX]  INT, 
    [2013_JAN_PAX_%] INT, 

    [2013_FEB_AMOUNT] DECIMAL(12,2), 
    [2013_FEB_AMOUNT_%] INT, 
    [2013_FEB_PAX]  INT, 
    [2013_FEB_PAX_%] INT, 

    [2013_MAR_AMOUNT] DECIMAL(12,2), 
    [2013_MAR_AMOUNT_%] INT, 
    [2013_MAR_PAX]  INT, 
    [2013_MAR_PAX_%] INT, 

    [2013_APR_AMOUNT] DECIMAL(12,2), 
    [2013_APR_AMOUNT_%] INT, 
    [2013_APR_PAX]  INT, 
    [2013_APR_PAX_%] INT  
) 

INSERT INTO #tmpResults 
    SELECT 1, -- PRODUCTID 
     --AMOUNT CALC= (JAN2013-JAN2012)/JAN2012       2013PAX CALC= (JAN2013-JAN2012)/JAN2012 
     3257.25, ROUND((SELECT ((3257.25-3309)/3309)*100),0),   11,  CONVERT(INT,ROUND(CONVERT(DECIMAL,11-10)/10*100,0))--JAN2013 
     ,3719.25, ROUND((SELECT ((3719.25-4351.2)/4351.2)*100),0),  31,  CONVERT(INT,ROUND(CONVERT(DECIMAL,31-21)/21*100,0)) --FEB2013 
     ,4120.74, ROUND((SELECT ((4120.74-4687)/4687)*100),0),   11,  CONVERT(INT,ROUND(CONVERT(DECIMAL,11-10)/10*100,0)) --MAR2013 
     ,5417.25, ROUND((SELECT ((5417.25-6123.1)/6123.1)*100),0),  21,  CONVERT(INT,ROUND(CONVERT(DECIMAL,11-10)/10*100,0)) --APR2013 

INSERT INTO #tmpResults 
    SELECT 3, -- PRODUCTID 
     --AMOUNT CALC= (JAN2013-JAN2012)/JAN2012       2013PAX CALC= (JAN2013-JAN2012)/JAN2012 
     6104.6, ROUND((SELECT ((6104.6-5416.5)/5416.5)*100),0),   20,  ROUND((SELECT ((20-10)/10)*100),2) --JAN2013 
     ,10797.43, ROUND((SELECT ((10797.43-9748.16)/9748.16)*100),0),  30,  ROUND((SELECT ((30-30)/30)*100),2) --FEB2013 
     ,13194.3, ROUND((SELECT ((13194.3-12706.32)/12706.32)*100),0), 4,  ROUND((SELECT ((4-30)/30)*100),2) --MAR2013 
     ,14339.92, ROUND((SELECT ((14339.92-16429.03)/16429.03)*100),0), 37,  ROUND((SELECT ((37-33)/33)*100),2) --APR2013 



SELECT * FROM #tmpResults 
DROP TABLE #tmpResults 

下面是數據@ SQLFiddle - http://sqlfiddle.com/#!6/e8ed1/7/1
有什麼建議?

回答

2

爲了得到結果,您將不得不考慮擺動和轉移表中的數據,並且因爲您想要在任意兩年內執行此操作,您將不得不考慮應用動態SQL。

首先,我們從最初的查詢開始,獲取2年的數據。您需要兩次加入您的表格才能獲取當年和前一年的數據。查詢將是:

select t.amount, 
    round((t.amount-c.amount)/c.amount*100, 0) AmtPercent, 
    t.pax, 
    round((t.pax-c.pax)/(c.pax*1.0)*100, 0) PaxPercent, 
    t.productid, 
    t.monthname, 
    t.year 
from tmpData t 
inner join tmpData c 
    on t.monthname = c.monthname 
    and t.productid = c.productid 
    and c.year = 2012 
where t.year = 2013; 

請參閱SQL Fiddle with Demo。一旦你有數據計算,那麼我建議unpivoting amount,amtpercent, paxpaxpercent列。這將爲每個productidmonth將多列變爲多行。你最初的SQL小提琴使用SQL Server 2012中,所以我假設你正在使用的版本大於2005年。如果是這樣,那麼你可以使用CROSS APPLY用VALUES子句unpivot的數據:

select d.productid, 
    cast(year as varchar(4)) + '_' + monthname + '_' + col as col, 
    value 
from 
(
    select t.amount, 
    round((t.amount-c.amount)/c.amount*100, 0) AmtPercent, 
    t.pax, 
    round((t.pax-c.pax)/(c.pax*1.0)*100, 0) PaxPercent, 
    t.productid, 
    t.monthname, 
    t.year 
from tmpData t 
inner join tmpData c 
    on t.monthname = c.monthname 
    and t.productid = c.productid 
    and c.year = 2012 
    where t.year = 2013 
) d 
cross apply 
(
    values 
    ('Amount', amount), 
    ('AmountPercent', amtPercent), 
    ('Pax', Pax), 
    ('PaxPercent', paxPercent) 
) c (col, value); 

SQL Fiddle with Demo。這給你一個結果:

| PRODUCTID |       COL | VALUE | 
------------------------------------------------------ 
|   1 |   2013_January_Amount | 3257.25 | 
|   1 | 2013_January_AmountPercent |  -2 | 
|   1 |   2013_January_Pax |  11 | 
|   1 |  2013_January_PaxPercent |  10 | 
|   1 |  2013_February_Amount | 3719.25 | 
|   1 | 2013_February_AmountPercent |  -15 | 
|   1 |   2013_February_Pax |  31 | 
|   1 | 2013_February_PaxPercent |  48 | 
|   1 |   2013_March_Amount | 4120.74 | 

現在您的數據是多行,您可以將旋轉功能的值col。這些值是通過將原始列名與年份和月份連接起來計算的。當您應用的旋轉功能,查詢將是:

select productid, 
    [2013_January_Amount], [2013_January_AmountPercent], 
    [2013_January_Pax], [2013_January_PaxPercent], 
    [2013_February_Amount], [2013_February_AmountPercent], 
    [2013_February_Pax], [2013_February_PaxPercent], 
    [2013_March_Amount], [2013_March_AmountPercent], 
    [2013_March_Pax], [2013_March_PaxPercent], 
    [2013_April_Amount], [2013_April_AmountPercent], 
    [2013_April_Pax], [2013_April_PaxPercent] 
from 
(
    select d.productid, 
    cast(year as varchar(4)) + '_' + monthname + '_' + col as col, 
    value 
    from 
    (
    select t.amount, 
     round((t.amount-c.amount)/c.amount*100, 0) AmtPercent, 
     t.pax, 
     round((t.pax-c.pax)/(c.pax*1.0)*100, 0) PaxPercent, 
     t.productid, 
     t.monthname, 
     t.year 
    from tmpData t 
    inner join tmpData c 
     on t.monthname = c.monthname 
     and t.productid = c.productid 
     and c.year = 2012 
    where t.year = 2013 
) d 
    cross apply 
    (
    values 
     ('Amount', amount), 
     ('AmountPercent', amtPercent), 
     ('Pax', Pax), 
     ('PaxPercent', paxPercent) 
) c (col, value) 
) src 
pivot 
(
    max(value) 
    for col in ([2013_January_Amount], [2013_January_AmountPercent], 
       [2013_January_Pax], [2013_January_PaxPercent], 
       [2013_February_Amount], [2013_February_AmountPercent], 
       [2013_February_Pax], [2013_February_PaxPercent], 
       [2013_March_Amount], [2013_March_AmountPercent], 
       [2013_March_Pax], [2013_March_PaxPercent], 
       [2013_April_Amount], [2013_April_AmountPercent], 
       [2013_April_Pax], [2013_April_PaxPercent]) 
) piv; 

SQL Fiddle with Demo。正如你可以看到,如果你要報告所有12個月,那麼你有很多列的硬編碼。此外,如果您想更改年份,您將不得不調整查詢。如果您要根據您傳遞的參數此調整,那麼你就需要使用動態SQL:

DECLARE @cols AS NVARCHAR(MAX), 
    @query AS NVARCHAR(MAX), 
    @currentYear int = 2013, 
    @previousYear int = 2012 

select @cols = STUFF((SELECT ',' + QUOTENAME(cast(year as varchar(4)) + '_' + monthname + '_' + col) 
        from 
        (
         select year, monthname, 
         DATEPART(MM,monthname+' 01 2013') mth 
         from tmpData 
         where year = @currentYear 
        ) d 
        cross apply 
        (
         select 'Amount', 1 union all 
         select 'AmountPercent', 2 union all 
         select 'Pax', 3 union all 
         select 'PaxPercent', 4 
        ) c (col, so) 
        group by year, monthname, col, so, mth 
        order by year, mth, so 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 

set @query = 'SELECT productid,' + @cols + ' 
      from 
      (
       select d.productid, 
       cast(year as varchar(4)) + ''_'' + monthname + ''_'' + col as col, 
       value 
       from 
       (
       select t.amount, 
        round((t.amount-c.amount)/c.amount*100, 0) AmtPercent, 
        t.pax, 
        round((t.pax-c.pax)/(c.pax*1.0)*100, 0) PaxPercent, 
        t.productid, 
        t.monthname, 
        t.year 
       from tmpData t 
       inner join tmpData c 
        on t.monthname = c.monthname 
        and t.productid = c.productid 
        and c.year = '+cast(@previousYear as varchar(4))+' 
       where t.year = '+cast(@currentYear as varchar(4))+' 
      ) d 
       cross apply 
       (
       values 
        (''Amount'', amount), 
        (''AmountPercent'', amtPercent), 
        (''Pax'', Pax), 
        (''PaxPercent'', paxPercent) 
      ) c (col, value) 
      ) x 
      pivot 
      (
       max(value) 
       for col in (' + @cols + ') 
      ) p ' 

execute(@query); 

SQL Fiddle with Demo。這些查詢給出結果:

| PRODUCTID | 2013_JANUARY_AMOUNT | 2013_JANUARY_AMOUNTPERCENT | 2013_JANUARY_PAX | 2013_JANUARY_PAXPERCENT | 2013_FEBRUARY_AMOUNT | 2013_FEBRUARY_AMOUNTPERCENT | 2013_FEBRUARY_PAX | 2013_FEBRUARY_PAXPERCENT | 2013_MARCH_AMOUNT | 2013_MARCH_AMOUNTPERCENT | 2013_MARCH_PAX | 2013_MARCH_PAXPERCENT | 2013_APRIL_AMOUNT | 2013_APRIL_AMOUNTPERCENT | 2013_APRIL_PAX | 2013_APRIL_PAXPERCENT | 
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
|   1 |    3257.25 |       -2 |    11 |      10 |    3719.25 |       -15 |    31 |      48 |   4120.74 |      -12 |    11 |      0 |   5417.25 |      -12 |    21 |      0 | 
|   3 |    6104.6 |       13 |    20 |      100 |    10797.43 |       11 |    30 |      0 |   13194.3 |      4 |    4 |     -87 |   14339.92 |      -13 |    37 |     12 | 
+0

完美。劑量正是我需要的。非常感謝。喜歡非動態代碼中每個部分的細分,真的幫助我理解你在做什麼。做得好。 – stevenmahony