2013-08-31 53 views
0

目前我有類似下面的數據(但更大!)複雜滾動場景(CROSS APPLY和OUTER APPLY例子)

/*--::::::::::: 
DROP TABLE #target 
DROP TABLE #Fact 
*/--::::::::::: 
CREATE TABLE #target 
    (
    PlayerKey INT, 
    Name   VARCHAR(8), 
    LiveKey  INT 
    ); 
INSERT INTO #target 
    values 
    (1,'michael',20130103), 
    (2,'jackson',20130107); 

CREATE TABLE #Fact 
    (
    DateKey  INT, 
    PlayerKey INT, 
    Amount  INT 
    ); 
INSERT INTO #Fact 
    values 
    (20130101,1,10), 
    (20130102,1,90), 
    (20130103,1,18), 
    (20130103,2,79), 
    (20130103,3,99), 
    (20130104,2,15), 
    (20130105,1,12), 
    (20130105,2,15), 
    (20130106,1,60), 
    (20130107,1,96), 
    (20130107,2,88), 
    (20130107,4,28), 
    (20130108,1,13), 
    (20130108,2,15), 
    (20130109,1,33), 
    (20130109,2,67), 
    (20130110,1,19), 
    (20130110,2,17) 
    ; 

查詢的起始如下。

DECLARE @NumDays INT = 3; 

WITH basic_cte AS 
     (
     SELECT rn = ROW_NUMBER() OVER(PARTITION BY d.Name ORDER BY f.DateKey), 
       f.DateKey, 
       d.Name, 
       f.Amount 
     FROM #Fact f 
       INNER JOIN #target d ON 
        f.PlayerKey = d.PlayerKey AND 
        f.DateKey >= d.LiveKey AND 
        f.DateKey < CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),d.LiveKey,112))[email protected]),112) 
     ) 
SELECT x.*, 
     "RollingAmount" = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey) 
FROM basic_cte x; 

這給出了以下:

enter image description here

假設我們有一個可用的DimDate生產觀我怎麼保證michael20130104一排量爲0?

也有可能在同一個腳本中,添加新的列「AmountAll」和「AmountAllRolling」,它會給包括PlayerKeys 3和4在內的所有玩家的數字?我猜這將涉及將INNER JOIN更改爲LEFT OUTER JOIN?通過全優的幫助下波格丹我有以下

enter image description here


編輯

所以給出了上述的最終結果將是如下。
我已經添加了一個額外的TotalGroupGroup,它是指定玩家的總數 - 這只是「很好玩」,而不是原始規格的一部分。

DECLARE @NumDays INT = 3; 

WITH basic_cte AS 
     (
     SELECT rn = ROW_NUMBER() OVER(PARTITION BY Name ORDER BY x.DateKey), 
       x.DateKey, 
       d.Name, 
       Amount  = ISNULL(f.Amount,0), 
       AmountGroup = ISNULL(f.AmountGroup,0), 
       AmountAll = ISNULL(f.AmountAll,0) 
     FROM (
       SELECT t.*, 
       EndLiveKey = CONVERT(INT,CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),t.LiveKey,112))[email protected]),112)) 
       FROM #target t 
       ) d 
       CROSS APPLY 
        (
       SELECT dm.DateKey 
       FROM WHData.dbo.vw_DimDate dm 
       WHERE dm.DateKey >= d.LiveKey AND 
         dm.DateKey < d.EndLiveKey   
       ) x 
       OUTER APPLY 
       (
       SELECT Amount = SUM(CASE WHEN PlayerKey1 = PlayerKey2 THEN fbase.Amount END), 
         AmountGroup = SUM(CASE WHEN inGroup = 1 THEN fbase.Amount ELSE 0 END), 
         AmountAll = SUM(fbase.Amount) 
       FROM 
        (
        SELECT fct.Amount, 
          fct.PlayerKey AS PlayerKey1, 
         d.PlayerKey AS PlayerKey2, 
         CASE WHEN tt.PlayerKey IS NULL THEN 0 ELSE 1 END AS inGroup 
        FROM #Fact fct 
         LEFT OUTER JOIN #target tt ON 
         fct.PlayerKey = tt.PlayerKey 
        WHERE fct.DateKey = x.DateKey 
       ) fbase 
      ) f 
     ) 
SELECT y.*, 
     "RollingAmount"  = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey), 
     "RollingAmountGroup" = SUM(AmountGroup) OVER(PARTITION BY Name ORDER BY DateKey), 
     "RollingAmountAll" = SUM(AmountAll) OVER(PARTITION BY Name ORDER BY DateKey) 
FROM basic_cte y; 
+0

@向下選民請你能也標誌着關閉問題,所以我知道具體是什麼問題?太具體了? – whytheq

+1

*我沒有downvote *,但我猜是因爲用於LiveKey和DateKey列的數據類型(INT代替DATE:這意味着4個字節而不是3個字節,以及那些轉換CONVERT(DATE,...))。 –

+0

@BogdanSahlean'DATE'相當新穎 - 當我們的倉庫(星形模式和SSAS集中)首次創建時,只有'DATETIME'可用,因此'INT'較小。現在標準做法是讓datekeys輸入'DATE'格式的'YYYYMMDD'嗎? – whytheq

回答

3

我假設你有一個DimDate表具有以下結構:

CREATE TABLE DimDate 
(
DateKey INT PRIMARY KEY 
); 

DateKey列不具有差距。

解決方案:

DECLARE @NumDays INT = 3; 

WITH basic_cte AS 
     (
      SELECT x.DateKey, 
        d.Name, 
        Amount = ISNULL(f.Amount,0) 
      FROM  
      (
       SELECT t.*, CONVERT(INT,CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),t.LiveKey,112))[email protected]),112)) AS EndLiveKey 
       FROM #target t 
      ) d 
      CROSS APPLY 
      (
       SELECT dm.DateKey 
       FROM DimDate dm 
       WHERE dm.DateKey >= d.LiveKey 
       AND  dm.DateKey < d.EndLiveKey   
      ) x 
      LEFT OUTER JOIN #Fact f 
      ON f.PlayerKey = d.PlayerKey 
      AND f.DateKey = x.DateKey 
     ) 
SELECT rn = ROW_NUMBER() OVER(PARTITION BY Name ORDER BY DateKey), 
     y.*, 
     "RollingAmount" = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey) 
FROM basic_cte y; 

編輯#1:

DECLARE @NumDays INT = 3; 

WITH basic_cte AS 
     (
      SELECT rn = ROW_NUMBER() OVER(PARTITION BY Name ORDER BY x.DateKey), 
        x.DateKey, 
        d.Name, 
        Amount  = ISNULL(f.Amount,0), 
        AmountAll = ISNULL(fall.AmountAll,0) 
      FROM  
      (
       SELECT t.*, CONVERT(INT,CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),t.LiveKey,112))[email protected]),112)) AS EndLiveKey 
       FROM #target t 
      ) d 
      CROSS APPLY 
      (
       SELECT dm.DateKey 
       FROM DimDate dm 
       WHERE dm.DateKey >= d.LiveKey 
       AND  dm.DateKey < d.EndLiveKey   
      ) x 
      OUTER APPLY 
      (
       SELECT SUM(fct.Amount) AS Amount 
       FROM #Fact fct 
       WHERE fct.DateKey = x.DateKey 
       AND  fct.PlayerKey = d.PlayerKey 
      ) f 
      OUTER APPLY 
      (
       SELECT SUM(fct.Amount) AS AmountAll 
       FROM #Fact fct 
       WHERE fct.DateKey = x.DateKey 
      ) fall 
     ) 
SELECT 
     y.*, 
     "RollingAmount"  = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey), 
     "RollingAmountAll" = SUM(AmountAll) OVER(PARTITION BY Name ORDER BY DateKey) 
FROM basic_cte y; 

編輯#2:

DECLARE @NumDays INT = 3; 

WITH basic_cte AS 
     (
      SELECT rn = ROW_NUMBER() OVER(PARTITION BY Name ORDER BY x.DateKey), 
        x.DateKey, 
        d.Name, 
        Amount  = ISNULL(f.Amount,0), 
        AmountAll = ISNULL(f.AmountAll,0) 
      FROM  
      (
       SELECT t.*, EndLiveKey = CONVERT(INT,CONVERT(CHAR(8),CONVERT(DATETIME,CONVERT(DATETIME,CONVERT(CHAR(8),t.LiveKey,112))[email protected]),112)) 
       FROM #target t 
      ) d 
      CROSS APPLY 
      (
       SELECT dm.DateKey 
       FROM DimDate dm 
       WHERE dm.DateKey >= d.LiveKey 
       AND  dm.DateKey < d.EndLiveKey   
      ) x 
      OUTER APPLY 
      (
       SELECT AmountAll = SUM(fbase.Amount), 
         Amount  = SUM(CASE WHEN PlayerKey1 = PlayerKey2 THEN fbase.Amount END) 
       FROM 
       (
        SELECT fct.Amount, fct.PlayerKey AS PlayerKey1, d.PlayerKey AS PlayerKey2 
        FROM #Fact fct 
        WHERE fct.DateKey = x.DateKey 
       ) fbase 
      ) f 
     ) 
SELECT 
     y.*, 
     "RollingAmount"  = SUM(Amount) OVER(PARTITION BY Name ORDER BY DateKey), 
     "RollingAmountAll" = SUM(AmountAll) OVER(PARTITION BY Name ORDER BY DateKey) 
FROM basic_cte y; 
+0

+1您對DimDate的假設是正確的。 – whytheq

+0

...您認爲將包含「AmountAll」和「AmountRollingAll」列的容易程度如何?這是建立一個新的查詢的情況嗎? – whytheq

+0

請參閱編輯1.這不是最好的解決方案,我知道。 –