This query is using two CTEs, one recursive, to generate a list of values from nothing (SQL isn't really good at doing this).
totalMonths AS (SELECT DATEDIFF(MONTH, @date_start, @date_end) totalM),
This is part is basically a convoluted way of binding the result of the DATEDIFF
to the name totalM
. This could've been implemented as just a variable if you can declare things:
DECLARE @totalM int = DATEDIFF(MONTH, @date_start, @date_end);
Then you would of course use @totalM
to refer to the value.
numbers AS (
SELECT 1 num
UNION ALL
SELECT n.num+1 num FROM numbers n, totalMonths c
WHERE n.num<= c.totalM
)
This part is essentially a simple loop implemented using recursion to generate the numbers from 1 to totalMonths
. The first SELECT
specifies the first value (1) and the one after that specifies the next value, which is int greater than the previous one. Evaluating recursive CTEs has somewhat special semantics so it's a good idea to read up on them. Finally the WHERE
specifies the stopping condition so that the recursion doesn't go on forever.
What all this does is generate an equivalent to a physical "numbers" table that just has one column the numbers from 1 onwards.
The SELECT
at the very end uses the result of the numbers
CTE to generate a bunch of dates.
Note that the OPTION (MAXRECURSION 0)
at the end is also relevant to the recursive CTE. This disables the server-wide recursion depth limit so that the number generating query doesn't stop short if the range is very long, or a bothersome DBA set a very low default limit.