You can also find missing dates with a Numbers table more easily than is outlined in the article.
To set up the test:
create table MissingDates (
Date datetime primary key)
go
insert into dbo.missingdates(date)
select dateadd(second, subtime.number, subdate.date)
from
(select dateadd(day, number, '1/1/08') as Date
from common.dbo.Numbers
where number <= 100) SubDate
cross join
(select number
from common.dbo.BigNumbers
where number <= (3600 * 24) - 1) SubTime
go
delete from dbo.missingdates
where date between '1/12/08' and '1/13/08'
go
delete from dbo.missingdates
where date between '1/30/08' and '1/31/08'
This time, I tried my usual Numbers table method:
select dateadd(day, number, '1/1/08')
from common.dbo.numbers
left outer join dbo.missingdates
on date >= dateadd(day, number, '1/1/08')
and date < dateadd(day, number + 1, '1/1/08')
where date is null
number between 0 and 31
------------------
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 1 ms.
(2 row(s) affected)
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'MissingDates'. Scan count 32, logical reads 5638, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Numbers'. Scan count 3, logical reads 52, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 688 ms, elapsed time = 687 ms.
------------------
Then I tried the method recommended in the article:
SELECT
CAST('2008-01-01' AS DATETIME) + Number-1
FROM dbo.GetNumbers(1, 30)
where CAST('2008-01-01' AS DATETIME) + Number-1 not in
(select cast(convert(varchar(100), date, 101) as datetime)
from dbo.missingdates)
(The cast-convert is necessary because without that, it will simply detect missing midnights, not whole missing days.)
I killed that process after it had been running for 3 minutes. Not sure how long it would have taken in all.
Converted it to:
select dateadd(day, number, '1/1/08')
from dbo.getnumbers(1,31)
left outer join dbo.missingdates
on date >= dateadd(day, number-1, '1/1/08')
and date < dateadd(day, number, '1/1/08')
where date is null
Which is pretty much identical to the query used with the Numbers table.
----------------
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 1 ms.
(2 row(s) affected)
Table '#28D10FF3'. Scan count 1, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'MissingDates'. Scan count 31, logical reads 5418, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 673 ms, elapsed time = 366 ms.
-----------------
This time, the total elapsed time was shorter, but the CPU time was nearly identical. One less scan count on the MissingDates table, and the server was able to split the process across more than 1 CPU (the computer I'm testing this on is a dual-core Pentium D).
Some advantage to the CTE, but in a loaded up server, it won't make much difference.
- Gus "GSquared", RSVP, OODA, MAP, NMVP, FAQ, SAT, SQL, DNA, RNA, UOI, IOU, AM, PM, AD, BC, BCE, USA, UN, CF, ROFL, LOL, ETC
Property of The Thread
"Nobody knows the age of the human race, but everyone agrees it's old enough to know better." - Anon