需要帮助了解

问题描述:

我一直在使用一个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://*.com/questions/14274942/sql-server-cte-and-recursion-example – sgeddes

+0

我不会使用递归CTE “...在两个日期范围之间获得月份”:巨大的过度杀伤力。使用数字/计数表代替... –

此查询使用两个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设置非常低的默认限制。

SELECT 1 num 

是你的递归CTE的起点,这是(数n)在第一teration.In第二迭代出来把第一

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

变成数(n)和等。

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值。可以选择相应开始和结束月份中的任何日期以生成相同的序列。