Reputation: 7990
In SQL Server 2008, I have data like this (Case: varchar(20), Time: time):
Case Time
-------------
D1 18:44
D2 19:12
C1 21:20
F2 21:05
...
What I would like to do is to count cases per hour. Should include all cases.
Expected result:
.... Column18 Column19 Column20 Column21 ...
1 1 0 2
where Column18
refers to the cases between 18:00
and 18:59
, and same logic for others. I have from Column0
to Column23
, 1 column per hour...
What I am doing is:
Select
...
, Column18 = sum(CASE WHEN Time like '18:%' THEN 1 ELSE 0 END)
, Column19 = sum(CASE WHEN Time like '19:%' THEN 1 ELSE 0 END)
, Column20 = sum(CASE WHEN Time like '20:%' THEN 1 ELSE 0 END)
, Column21 = sum(CASE WHEN Time like '21:%' THEN 1 ELSE 0 END)
...
from
mytable
Even though my query works, it is long and repetitive, so it does not seem professional to me. I wonder if there is any better way to handle this situation. Any advice would be appreciated.
Upvotes: 1
Views: 848
Reputation: 7990
Here is the simplest answer I could come up. Thanks a lot for all the advices. Looks way better now:
create table #temp (CaseID varchar(20),TheTime time)
insert into #temp values ('A1','03:56')
insert into #temp values ('A2','03:12')
insert into #temp values ('B2','03:21')
insert into #temp values ('C1','05:12')
insert into #temp values ('B3','06:00')
insert into #temp values ('B4','07:14')
insert into #temp values ('B5','07:18')
insert into #temp values ('D1','18:44')
insert into #temp values ('D2','19:54')
insert into #temp values ('C2','21:12')
insert into #temp values ('F4','21:50')
select *
from (
select CaseID, DATEPART(hour,TheTime) as HourOfDay
from #temp
) t
PIVOT
(
Count(CaseID)
FOR HourOfDay IN ([00],[01],[02],[03],[04],[05],[06],[07],[08],
[09],[10],[11],[12],[13],[14],[15],[16],[17],
[18],[19],[20],[21],[22],[23])
) AS PivotTable
Upvotes: 0
Reputation: 2032
We can go with Dynamic Pivot
-
declare @ColString varchar(1000)=''
;with cte as(
select 0 as X
union all
select x+1 as X
from cte where X <23
)
select @ColString = @ColString + ',[Column' + cast(X as varchar) + ']' from cte
select @ColString = stuff(@ColString,1,1,'')
declare @DynamicQuery varchar(3000)=''
select @DynamicQuery =
'select *
from (
select [case],''Column''+cast(datepart(hh,[time]) as varchar) as [time]
from #xyz
) src
pivot
(
count([case]) for [Time] in ('+ @ColString + ')
) piv'
exec (@DynamicQuery)
Input data -
create table #xyz ([Case] varchar(10),[Time] time(0))
insert into #xyz
select 'D1','18:44' union all
select 'D2','19:12' union all
select 'C1','21:20' union all
select 'F2','21:05'
Upvotes: 2
Reputation: 1270793
Your query is basically fine, but I strongly discourage you from using string functions on date/time columns.
datepart()
is definitely one solution:
Select ...,
Column18 = sum(CASE WHEN datepart(hour, Time) = 18 THEN 1 ELSE 0 END)
Column19 = sum(CASE WHEN datepart(hour, Time) = 19 THEN 1 ELSE 0 END)
Direct comparison is more verbose, but more flexible:
select . . .,
sum(case when time >= '18:00' and time < '19:00' then 1 else 0 end) as column18,
sum(case when time >= '19:00' and time < '20:00' then 1 else 0 end) as column19,
Note that this uses as
. SQL Server supports the syntax alias =
. However, other databases do not use such syntax, so I prefer to stick with the ANSI-standard method of defining aliases.
Putting the values on rows instead of columns is probably the more "typical" solution:
select datepart(time, hour) as hr, count(*)
from t
group by datepart(time, hour)
order by hr;
As written, this will not return hours with zero counts.
Upvotes: 1