需要帮助来理解 SQL 查询
Need help to understand SQL query
我有以下代码使用 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
)
提前致谢,对不起我的英语
SELECT 1 num
是递归 CTE 的起点,即第一个 teration.In 中的 (numbers n) 第二个迭代是第一个
的输出
SELECT n.num+1 num FROM numbers n, totalMonths c
WHERE n.num <= c.totalM
变成数字(n)等等
此查询使用两个 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),之后的一个指定下一个值,该值比前一个值大。评估递归 CTE 有 somewhat special semantics,因此阅读它们是个好主意。最后 WHERE
指定停止条件,这样递归就不会永远继续下去。
所有这一切所做的是生成一个等同于物理 "numbers" table 的等价物,其中只有一列是从 1 开始的数字。
最后的 SELECT
使用 numbers
CTE 的结果生成一堆日期。
注意最后的OPTION (MAXRECURSION 0)
也和递归CTE有关。这将禁用服务器范围的递归深度限制,以便生成查询的数字不会在范围很长时突然停止,或者麻烦的 DBA 设置了非常低的默认限制。
totalMonths
查询的计算结果为标量结果(单值),指示需要生成的月数。内联执行此操作而不是使用命名的 CTE 可能更有意义。
numbers
生成一个行序列,其中有一列名为 num
,从 1
开始,到 totalM + 1
结束,这是在上一步中计算的。它能够通过交叉连接引用此值。由于只有一行,它基本上只是将该列水平附加到 table 。该查询是递归的,因此每次通过将 1 添加到最后添加的行(实际上只是一列),直到 先前添加的行 的值超过 totalM
。 union
的前半部分是起始值;后半部分通过 from numbers
引用 自身 并在某种循环中逐步构建结果。
输出来自 numbers
输入。从每个 num
中减去一个,给出从 0
到 totalM
的范围,并且该值被视为要添加到开始日期的月数。日期值被转换为长度为 6 的 varchar
,这意味着包含日期的最后两个字符被截断。
假设 @date_start
是 2016 年 1 月 31 日,@date_end
是 2016 年 3 月 1 日。实际日期值从来没有任何比较,因此生成 3 月 31 日并不重要在序列中,但也晚于传递的 @date_end
值。可以选择相应开始和结束月份中的任何日期来生成相同的序列。
我有以下代码使用 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
)
提前致谢,对不起我的英语
SELECT 1 num
是递归 CTE 的起点,即第一个 teration.In 中的 (numbers n) 第二个迭代是第一个
的输出SELECT n.num+1 num FROM numbers n, totalMonths c
WHERE n.num <= c.totalM
变成数字(n)等等
此查询使用两个 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),之后的一个指定下一个值,该值比前一个值大。评估递归 CTE 有 somewhat special semantics,因此阅读它们是个好主意。最后 WHERE
指定停止条件,这样递归就不会永远继续下去。
所有这一切所做的是生成一个等同于物理 "numbers" table 的等价物,其中只有一列是从 1 开始的数字。
最后的 SELECT
使用 numbers
CTE 的结果生成一堆日期。
注意最后的OPTION (MAXRECURSION 0)
也和递归CTE有关。这将禁用服务器范围的递归深度限制,以便生成查询的数字不会在范围很长时突然停止,或者麻烦的 DBA 设置了非常低的默认限制。
totalMonths
查询的计算结果为标量结果(单值),指示需要生成的月数。内联执行此操作而不是使用命名的 CTE 可能更有意义。
numbers
生成一个行序列,其中有一列名为 num
,从 1
开始,到 totalM + 1
结束,这是在上一步中计算的。它能够通过交叉连接引用此值。由于只有一行,它基本上只是将该列水平附加到 table 。该查询是递归的,因此每次通过将 1 添加到最后添加的行(实际上只是一列),直到 先前添加的行 的值超过 totalM
。 union
的前半部分是起始值;后半部分通过 from numbers
引用 自身 并在某种循环中逐步构建结果。
输出来自 numbers
输入。从每个 num
中减去一个,给出从 0
到 totalM
的范围,并且该值被视为要添加到开始日期的月数。日期值被转换为长度为 6 的 varchar
,这意味着包含日期的最后两个字符被截断。
假设 @date_start
是 2016 年 1 月 31 日,@date_end
是 2016 年 3 月 1 日。实际日期值从来没有任何比较,因此生成 3 月 31 日并不重要在序列中,但也晚于传递的 @date_end
值。可以选择相应开始和结束月份中的任何日期来生成相同的序列。