muhqa muhqa - 3 months ago 19
SQL Question

Ordering dates for 4 weeks

I am writing a SQL query that pulls some information for the last 4 weeks.
I am seeing two issues with my results (see below).

First problem is that when I look back four weeks, the range should be August 10 - September 6. When I order by 'Day of the Month', the dates in September get moved to the top of the results when they should actually be at the end. So my results should start from 10 (august) and end at 6 (September).

Second problem is I'm missing a few random dates (3, 4, 13, 27).

Day of the Month Number of X
1 125
2 77
5 5
6 23
10 145
11 177
12 116
14 2
15 199
16 154
17 134
18 140
19 154
21 8
22 166
23 145
24 151
25 107
26 79
28 3
29 151
30 163
31 147


Here a general version of my query:

DECLARE @startDate datetime, @endDate datetime;
SET @startDate = dateadd(day, -28, GETDATE());
SET @endDate = dateadd(day, -1, GETDATE());

Select DATEPArt(dd, Time) AS 'Day of the Month', count(*) AS ' Number of X'
from SomeTable ST
where Time >= @startDate
AND Time < @endDate
group by DATEPArt(dd, Time)
order by 'Day of the Month'

Answer

For the first problem you can order by date to get the correct date order. I use convert to get a time-free date so that the entries group correctly.

DECLARE @StartDate datetime, @EndDate datetime;
SET @StartDate = DATEADD(day, -28, GETDATE());
SET @EndDate = DATEADD(day, -1, GETDATE());

SELECT 
DATEPART(dd, Convert(date, Time)) AS 'Day of the Month', COUNT(*) AS ' Number of X' 
FROM SomeTable ST
WHERE Time >= @StartDate
AND Time <  @EndDate 
GROUP BY Convert(date, Time) 
ORDER BY Convert(date, Time) 

As for the missing days, this is more complicated as the data needs to be there for the group-by to work.

One option is to create a temporary table with all the dates in, then join in the data. This will still leave a row where the join does not find any data, and can get a "zero" count.

DECLARE @StartDate datetime, @EndDate datetime;
SET @StartDate = DATEADD(day, -28, GETDATE());
SET @EndDate = DATEADD(day, -1, GETDATE());

--Create temporary table with all days between the two dates
;WITH d(d) AS 
(
  SELECT DATEADD(DAY, n, DATEADD(DAY, DATEDIFF(DAY, 0, @StartDate), 0))
  FROM ( SELECT TOP (DATEDIFF(DAY, @StartDate, @EndDate) + 1) 
    n = ROW_NUMBER() OVER (ORDER BY [object_id]) - 1
    FROM sys.all_objects ORDER BY [object_id] ) AS n
)
--Join in our query to the table temporary table
SELECT  
    DATEPART(dd, d.d) AS 'Day of the Month', 
    COUNT(Time) AS ' Number of X' 
FROM d LEFT OUTER JOIN SomeTable ST ON DATEPART(dd, d.d) = DATEPART(dd, Time) 
                              AND Time >= @StartDate 
                              AND Time <  @EndDate 
GROUP BY d.d
ORDER BY d.d