需要帮助来理解SQL查询

时间:2016-03-07 23:28:33

标签: sql sql-server common-table-expression

我有以下代码,使用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);

这有效,但我不明白它是如何工作的

特别是这部分

numbers AS 
(
    SELECT 1 num

    UNION ALL

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

提前致谢,对不起我的英文

3 个答案:

答案 0 :(得分: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的结果生成一堆日期。

请注意,最后的OPTION (MAXRECURSION 0)也与递归CTE相关。这将禁用服务器范围的递归深度限制,以便在范围很长或者令人烦恼的DBA设置非常低的默认限制时,生成数字的查询不会停止。

答案 1 :(得分:1)

totalMonths查询计算标量结果(单个值),指示需要生成的月数。仅仅使用内联而不是使用命名的CTE可能更有意义。

numbers生成一系列行,其中一列名为num,从1开始,结束于totalM + 1,这是在上一步中计算出来的。它可以通过交叉连接来引用该值。由于只有一行,它基本上只是将一列水平附加到表中。该查询是递归的,因此每次传递都会在结果中添加1到最后添加的行(实际上只是一列),直到以前添加的行的值超过totalMunion的前半部分是起始值;下半部分通过from numbers引用本身,并以一种循环递增地构建结果。

输出来自numbers输入。从每个num中减去一个,得出从0totalM的范围,该值被视为要添加到开始日期的月数。日期值将转换为长度为6的varchar,这意味着包含该日期的最后两个字符将被截断。

假设@date_start是2016年1月31日,而@date_end是2016年3月1日。实际日期值从未进行任何比较,因此3月31日生成日期值并不重要序列,但也落后于传递的@date_end值。可以选择相应开始和结束月份中的任何日期以生成相同的序列。

答案 2 :(得分:0)

SELECT 1 num

是你的递归CTE的起点,也就是第一次teration中的(数字n)。在第二次迭代中,第一次的输出

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

变成数字(n),依此类推。