2016-03-07 143 views
0

我一直在使用一個CTE需要幫助瞭解

declare 
    @date_start DateTime, 
    @date_end DateTime 

;WITH totalMonths AS 
(
    SELECT 
     DATEDIFF(MONTH, @date_start, @date_end) totalM 
), 
numbers AS 
(
    SELECT 1 num 

    UNION ALL 

    SELECT n.num + 1 num 
    FROM numbers n, totalMonths c 
    WHERE n.num <= c.totalM 
) 
SELECT 
    CONVERT(varchar(6), DATEADD(MONTH, numbers.num - 1, @date_start), 112) 
FROM 
    numbers 
OPTION (MAXRECURSION 0); 

這個工程下面的代碼,得到兩個日期範圍之間的幾個月SQL查詢,但我不明白它是如何工作

特別是這部分

numbers AS 
(
    SELECT 1 num 

    UNION ALL 

    SELECT n.num + 1 num 
    FROM numbers n, totalMonths c 
    WHERE n.num <= c.totalM 
) 

在此先感謝,對不起我的英語

+1

查找遞歸公用表表達式...這裏可能是一個重複的帖子:http://stackoverflow.com/questions/14274942/sql-server-cte-and-recursion-example – sgeddes

+0

我不會使用遞歸CTE 「...在兩個日期範圍之間獲得月份」:巨大的過度殺傷力。使用數字/計數表代替... –

回答

1

此查詢使用兩個CTE(一個遞歸)來從無到有生成一個值列表(SQL並不擅長這樣做)。

totalMonths AS (SELECT DATEDIFF(MONTH, @date_start, @date_end) totalM), 

這部分,基本上是DATEDIFF的結果結合到名稱totalM一個令人費解的方式。這可能已經實現只是一個變量,如果你能申報物品:

DECLARE @totalM int = DATEDIFF(MONTH, @date_start, @date_end); 

那麼你當然會使用@totalM來引用值。

numbers AS (
    SELECT 1 num 
    UNION ALL 
    SELECT n.num+1 num FROM numbers n, totalMonths c 
    WHERE n.num<= c.totalM 
) 

這部分基本上是使用遞歸來生成數字1到totalMonths實現一個簡單的循環。第一個SELECT指定第一個值(1),之後的那個指定下一個值,它的int大於前一個值。評估遞歸CTE有somewhat special semantics,所以最好閱讀它們。最後,WHERE指定停止條件,以便遞歸不會永遠持續下去。

所有這些都會生成一個等同於物理「數字」的表格,其中只有一列是從1開始的數字。

最後的SELECT使用numbers CTE的結果生成一堆日期。請注意0​​最後也與遞歸CTE相關。這會禁用服務器範圍的遞歸深度限制,以便在範圍非常長的情況下生成查詢的數字不會停止,或者令人煩惱的DBA設置非常低的默認限制。

0
SELECT 1 num 

是你的遞歸CTE的起點,這是(數n)在第一teration.In第二迭代出來把第一

SELECT n.num+1 num FROM numbers n, totalMonths c 
WHERE n.num <= c.totalM 

變成數(n)和等。

1

totalMonths查詢的計算結果爲標量結果(單值),表示需要生成的月數。使用內聯代替使用命名的CTE可能更有意義。

numbers生成的行的順序與一個稱爲num起始於1totalM + 1其計算在前面步驟結束列。它可以通過交叉連接來引用該值。由於只有一行,它基本上只是將這一列附加到表中。該查詢是遞歸的,因此每次通過將1行添加到最後添加的行(實際上只是一列),直到之前添加的行的值超過totalM,結果向結果添加新行。 union的前半部分是起始值;下半部分通過from numbers參照本身,並逐步建立循環結果。

輸出來自numbers輸入。從num中減去一個,給出從0totalM的範圍,並且該值被視爲要添加到開始日期的月數。日期值被轉換爲長度爲6的varchar,這意味着包含該日的最後兩個字符被截斷。

假設@date_start是2016年1月31日,而@date_end是2016年3月1日。實際日期值從來沒有任何比較,因此無關緊要的是3月31日在序列中生成,但也落後於通過@date_end值。可以選擇相應開始和結束月份中的任何日期以生成相同的序列。