In my sql query, I count the number of orders in each Hour of day. My quer开发者_如何学Goy looks something like this:
SELECT COUNT(dbo.Uputa.ID),{ fn HOUR(dbo.Orders.Date) } AS Hour
FROM Orders
WHERE dbo.Orders.Date BETWEEN '2011-05-01' AND '2011-05-26'
GROUP BY { fn HOUR(dbo.Orders.Date) }
ORDER BY Hour
My problem is that the query returns only existing Hours in dbo.Orders.Date.
For example:Number Hour
12 3
12 5
I want to return all hours like this:
Number Hour
0 0
0 1
0 2
12 3
0 4
12 5
...
0 23
Does anybody have idea how to accomplish this?
Use a common table expression to create all hours, then left join your grouped totals to get a result.
with mycte as
(
SELECT 0 AS MyHour
UNION ALL
SELECT MyHour + 1
FROM mycte
WHERE MyHour + 1 < 24
)
SELECT mycte.MyHour, COALESCE(OrderCount,0) FROM mycte
LEFT JOIN
(
SELECT COUNT(dbo.Uputa.ID) AS OrderCount,{ fn HOUR(dbo.Orders.Date) } AS MyHour
FROM Orders
WHERE dbo.Orders.Date BETWEEN '2011-05-01' AND '2011-05-26'
GROUP BY { fn HOUR(dbo.Orders.Date) }
) h
ON
h.MyHour = mycte.MyHour;
A 'numbers table' (SQL, Auxiliary table of numbers for example) is in general quite a useful thing to have in your database; if you create one here you can select all rows between 0 and 23 from your numbers table, left join that against your results and you'll get the results you want without the need to create a custom CTE or similar purely for this query.
SELECT COUNT(dbo.Uputa.ID),n.number AS Hour
FROM (select number from numbers where number between 0 and 23) n
left join Orders o on n.number={ fn HOUR(dbo.Orders.Date) }
WHERE dbo.Orders.Date BETWEEN '2011-05-01' AND '2011-05-26'
GROUP BY n.number
ORDER BY n.number
(I've worded this as per your example for clarity but in practice I'd try and avoid putting a function in the join criteria to maximise performance.)
You can use a CTE
to add the missing hours and JOIN
these with your original query to fill in the blanks.
SQL Statement
;WITH q (Number, Hour) AS (
SELECT 0, 1
UNION ALL
SELECT q.Number, q.Hour + 1
FROM q
WHERE q.Hour < 23
)
SELECT COALESCE(o.Number, q.Number)
, q.Hour
FROM q
LEFT OUTER JOIN (
SELECT COUNT(dbo.Uputa.ID),{ fn HOUR(dbo.Orders.Date) } AS Hour
FROM Orders
WHERE dbo.Orders.Date BETWEEN '2011-05-01' AND '2011-05-26'
GROUP BY { fn HOUR(dbo.Orders.Date) }
) o ON o.Hour = q.Hour
ORDER BY
q.Hour
Test Script
;WITH Orders (Number, Hour) AS (
SELECT 12, 3
UNION ALL SELECT 12, 5
)
, q (Number, Hour) AS (
SELECT 0, 1
UNION ALL
SELECT q.Number, q.Hour + 1
FROM q
WHERE q.Hour < 23
)
SELECT COALESCE(o.Number, q.Number)
, q.Hour
FROM q
LEFT OUTER JOIN Orders o ON o.Hour = q.Hour
精彩评论