Reputation: 6973
I have a query that returns some data
Name Hour1 hour2 hour3 etc
A 1 2 3 ...
B 50 2 25 ...
C 25 2 3 ...
D 1 30 50 ...
E 1 40 3 ...
I would like to just have the values for the top two results in each column (values are made up)
Name Hour1 hour2 hour3 etc
A null null null ...
B 50 null 25 ...
C 25 null null ...
D null 30 50 ...
E null 40 null ...
There can be lots of names, and 24 hour columns. Is this possible?
The existing query is in the form of
SELECT Name,
ISNULL(Count(CASE WHEN DATEPART(HOUR,OpenedDate)=1 THEN 1 ELSE null END), 0) AS Hour1,
etc
etc
From DataTable
Upvotes: 0
Views: 48
Reputation: 44911
One option might be to use a common table expression together with a pivot.
with cte as (
select
name
, h = datepart(hour,openeddate)
, c = count(*)
, r = rank() over (partition by datepart(hour,openeddate) order by count(*) desc)
from datatable
group by name, datepart(hour,openeddate)
)
select
name
, hour1 = max(case when r <= 2 then [1] else null end)
, hour2 = max(case when r <= 2 then [2] else null end)
, hour3 = max(case when r <= 2 then [3] else null end)
from cte
pivot ( sum(c) for h in ([1],[2],[3]) )p
group by name
order by name
Note that the rank function will return more than two rows if there are ties.
Upvotes: 1
Reputation: 1270873
I would write your query as:
SELECT Name,
SUM(CASE WHEN DATEPART(HOUR, OpenedDate) = 1 THEN 1 ELSE 0 END) AS Hour1,
. . .
FROM DataTable
GROUP BY Name;
To get the top 2 values, you can enumerate DataTable
:
select name,
max(case when hr = 1 and seqnum <= 2 then cnt end) as hr_1,
. . .
from (select t.*, DATEPART(HOUR, OpenedDate) as hr, count(*) as cnt,
row_number() over (partition by datepart(hour, OpenedDate)
order by count(*) desc) as seqnum
from datatable t
group by name, DATEPART(HOUR, OpenedDate)
) t
group by name;
The subquery summarizes the data by name and hour. It then enumerates the values and uses that for conditional aggregation.
You might want dense_rank()
rather than row_number()
depending on how you want to handle ties.
Upvotes: 1