Blogging this so I don’t forget it…
It used to require some fairly ugly, resource intensive hacks (cursors, temp tables, etc.) to emit an inclusive list between two data points when the source data might not include an entry for every point (for example, a calendar, where not every day contains an event). In SQL Server 2005 and above, this is trivially easy, with a Common Table Expression (CTE) and a Recursive Query. To emit one record for every date between 1/1/2008 and 1/31/2008, you do this:
WITH datecte(anydate) AS (SELECT CAST('1/1/2008' AS datetime) AS anydate
UNION ALL
SELECT anydate + 1 AS anydate
FROM datecte AS datecte_1
WHERE (anydate < CAST('2/1/2008' AS datetime) - 1))
SELECT anydate
FROM datecte AS datecte_2
If you need more than 100 days (the recursion limit is 100), add this to the end:
OPTION (MAXRECURSION 1000)
The fact that they stop recursion short at 100 by default would seem to indicate that this is an expensive procedure, but even if you’re just using this to produce a dummy table with all the dates for several years, it’s a nice shortcut.
I just tried the following query, which emits a record for every day between 1/1/2000 and 12/31/2020:
WITH datecte(anydate) AS (SELECT CAST('1/1/2000' AS datetime) AS anydate
UNION ALL
SELECT anydate + 1 AS anydate
FROM datecte AS datecte_1
WHERE (anydate < CAST('1/1/2021' AS datetime) - 1))
SELECT anydate
FROM datecte AS datecte_2
OPTION (MAXRECURSION 10000)
On my P4-641+ the script emits 7671 records in 0 (that’s zero) seconds and “spikes” the processor to all of 3%. Granted this is not a complex query, but at least we know the recursion (if it really is recursion internally, which I doubt) isn’t expensive by itself.