I have a SQL Server table that contains task data going back 12 months.
Here's an example:
I am trying to write a query that displays each month and the count of tickets that were open up until that month by their rating. Output example below:
I created the following SQL statement to count by day:
SELECT
created,
COUNT(CASE WHEN rating = ‘high’ THEN 1 ELSE NULL END) AS high,
COUNT(CASE WHEN rating = ‘med’ THEN 1 ELSE NULL END) AS med,
COUNT(CASE WHEN rating = ‘low’ THEN 1 ELSE NULL END) AS low
FROM
taskDB
GROUP BY
created
ORDER BY
created ASC
I am not sure how to group by month and get the proper count up until that month? Is there a better way to do this? My end goal is to display this data as a timeline chart where the yAxis is ticket count and xAxis is the date(yea/month). There will be a line for each “rating”.
UPDATE 8/14/2020
I tried out a couple of the answers there and they seem to count the amount of tickets open only for each month and not from each month + all prior months. I created a SQL script with some test data so everyone can see what i'm working with:
GO
CREATE TABLE [dbo].[taskDB](
[ticket] [varchar](50) NULL,
[created] [date] NULL,
[closed] [date] NULL,
[rating] [varchar](50) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023345', CAST(N'2019-09-01' AS Date), CAST(N'2020-01-17' AS Date), N'Low')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023346', CAST(N'2019-08-01' AS Date), CAST(N'2019-08-03' AS Date), N'Critical')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023347', CAST(N'2019-09-01' AS Date), CAST(N'2019-09-20' AS Date), N'Critical')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023348', CAST(N'2019-08-01' AS Date), CAST(N'2020-08-06' AS Date), N'Critical')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023349', CAST(N'2020-08-01' AS Date), CAST(N'2020-08-05' AS Date), N'Medium')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023350', CAST(N'2019-08-01' AS Date), CAST(N'2019-08-05' AS Date), N'Medium')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023351', CAST(N'2019-12-22' AS Date), CAST(N'' AS Date), N'High')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023352', CAST(N'2019-11-07' AS Date), CAST(N'2020-08-05' AS Date), N'Medium')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023353', CAST(N'2020-08-02' AS Date), CAST(N'' AS Date), N'Low')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023354', CAST(N'2019-08-02' AS Date), CAST(N'2019-08-05' AS Date), N'Medium')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023355', CAST(N'2019-010-02' AS Date), CAST(N'' AS Date), N'Low')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023356', CAST(N'2019-08-02' AS Date), CAST(N'2019-08-05' AS Date), N'Critical')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023357', CAST(N'2019-08-06' AS Date), CAST(N'2020-07-05' AS Date), N'Critical')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023358', CAST(N'2019-10-04' AS Date), CAST(N'' AS Date), N'Low')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023359', CAST(N'2019-12-02' AS Date), CAST(N'2020-02-25' AS Date), N'High')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023360', CAST(N'2019-08-05' AS Date), CAST(N'2019-08-05' AS Date), N'Medium')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023361', CAST(N'2020-08-02' AS Date), CAST(N'' AS Date), N'High')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023362', CAST(N'2019-09-02' AS Date), CAST(N'2019-10-06' AS Date), N'Critical')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023363', CAST(N'2019-10-03' AS Date), CAST(N'2019-11-08' AS Date), N'High')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023365', CAST(N'2019-10-03' AS Date), CAST(N'2019-12-08' AS Date), N'N/A')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023364', CAST(N'2019-11-03' AS Date), CAST(N'2019-11-05' AS Date), N'High')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023366', CAST(N'2020-06-03' AS Date), CAST(N'' AS Date), N'High')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023368', CAST(N'2019-08-03' AS Date), CAST(N'2019-08-05' AS Date), N'High')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023367', CAST(N'2019-11-03' AS Date), CAST(N'' AS Date), N'N/A')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023371', CAST(N'2019-08-03' AS Date), CAST(N'2019-08-05' AS Date), N'N/A')
GO
INSERT [dbo].[taskDB] ([ticket], [created], [closed], [rating]) VALUES (N'023370', CAST(N'2019-08-03' AS Date), CAST(N'2019-08-05' AS Date), N'Critical')
GO
I tried the following from @GMB which is close but does not seem to give me the correct results as there is negative numbers and the blank closed fields are coming back as 1900-01-01.
select
year(x.dt) yyyy,
month(x.dt) mm,
sum(sum(case when x.rating = 'low' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)) low,
sum(sum(case when x.rating = 'medium' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)) medium,
sum(sum(case when x.rating = 'high' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)) high
from [TestDB].[dbo].[taskDB] t
cross apply (values
(rating, created, 1),
(rating, closed, -1)
) as x(rating, dt, cnt)
where x.dt is not null
group by year(x.dt), month(x.dt)
order by year(x.dt), month(x.dt)
The results for this query:
UPDATE 8/14/2020
The revised query by @iceblade seems to be the most correct at this point. The only thing it does not account for is if a ticket was opened and closed in the same month, i believe it should be counted. Here is the query:
declare @FromDate datetime,
@ToDate datetime;
SET @FromDate = (Select min(created) From [dbo].[taskDB]);
SET @ToDate = (Select max(created) From [dbo].[taskDB]);
declare @openTicketsByMonth table (firstDayNextMonth datetime, year int, month int, Low int, Medium int, High int, Critical int, NA int)
Insert into @openTicketsByMonth(firstDayNextMonth, year, month)
Select top (datediff(month, @FromDate, @ToDate) + 1)
dateadd(month, number + 1, @FromDate),
year(dateadd(month, number, @FromDate)),
month(dateadd(month, number, @FromDate))
from [master].dbo.spt_values
where [type] = N'P' order by number;
update R
Set R.Low = (Select count(1) from [dbo].[taskDB] where rating = 'Low' and created < R.firstDayNextMonth and (closed >= R.firstDayNextMonth or closed = '' or closed is null)),
R.Medium = (Select count(1) from [dbo].[taskDB] where rating = 'Medium' and created < R.firstDayNextMonth and (closed >= R.firstDayNextMonth or closed = '' or closed is null)),
R.High = (Select count(1) from [dbo].[taskDB] where rating = 'High' and created < R.firstDayNextMonth and (closed >= R.firstDayNextMonth or closed = '' or closed is null)),
R.Critical = (Select count(1) from [dbo].[taskDB] where rating = 'Critical' and created < R.firstDayNextMonth and (closed >= R.firstDayNextMonth or closed = '' or closed is null)),
R.NA = (Select count(1) from [dbo].[taskDB] where rating = 'N/A' and created < R.firstDayNextMonth and (closed >= R.firstDayNextMonth or closed = '' or closed is null))
From @openTicketsByMonth R
select year,
month,
Low,
Medium,
High,
Critical,
NA
from @openTicketsByMonth
and the output from the query based on the data above:
If you look at 2019/8 there are 2 tickets that are critical that were opened and stayed open past that month but there are 3 critical tickets that were opened and closed in the same month. I believe those should be counted.
UPDATE 8/17/2020
The query @iceblade posted has been edited and is confirmed to produce the proper results. Answer has been marked accordingly.
You need a calendar table/view with the date ranges for the last twelve months, eg
"year/month" month_begin month_end
2019/08 2019-08-01 2019-08-31
and then a join checking for overlapping date ranges:
-- based on @iceblade's answer
declare @FromDate date,
@ToDate date;
SET @FromDate = (Select min(created) From [dbo].[taskDB]);
SET @ToDate = (Select max(created) From [dbo].[taskDB]);
declare @calendar table (Month_begin date, month_end date, year int, month int)
Insert into @calendar(Month_begin, month_end, year, month)
Select top (datediff(month, @FromDate, @ToDate) + 1)
dateadd(month, number, @FromDate),
dateadd(d,-1,dateadd(month, number + 1, @FromDate)),
year(dateadd(month, number, @FromDate)),
month(dateadd(month, number, @FromDate))
from [master].dbo.spt_values
where [type] = N'P' order by number;
select c.year,c.month,
COUNT(CASE WHEN rating = 'Low' THEN 1 ELSE NULL END) as low,
COUNT(CASE WHEN rating = 'Medium' THEN 1 ELSE NULL END) as med,
COUNT(CASE WHEN rating = 'High' THEN 1 ELSE NULL END) as high,
COUNT(CASE WHEN rating = 'Critical' THEN 1 ELSE NULL END) as critical,
COUNT(CASE WHEN rating = 'N/A' THEN 1 ELSE NULL END) as na
FROM taskDB as t join @calendar as c
-- overlapping periods
on t.created <= c.month_end
and (t.closed >= c.month_begin or t.closed is null)
GROUP BY c.year,c.month
ORDER BY c.year,c.month
Adding a varition based on GMB's, no join to a calendar and probably more efficient for your actual data. This just modifies the closing date to next month:
select
year(x.dt) yyyy,
month(x.dt) mm,
sum(sum(case when x.rating = 'Low' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)
rows unbounded preceding) low,
sum(sum(case when x.rating = 'Medium' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)
rows unbounded preceding) medium,
sum(sum(case when x.rating = 'High' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)
rows unbounded preceding) high,
sum(sum(case when x.rating = 'Critical' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)
rows unbounded preceding) critical,
sum(sum(case when x.rating = 'N/A' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)
rows unbounded preceding) na
from taskDB t
cross apply (values
(rating, created, 1),
-- closed in next month
(rating, dateadd(m,1,closed), -1)
) as x(rating, dt, cnt)
where dt <= getdate() -- no rows past today
group by year(x.dt), month(x.dt)
order by year(x.dt), month(x.dt)
There's a minor difference, #2 will skip months with no tickets, but I doubt this exists.
This seems to be what you want, see fiddle
One option uses a lateral join and conditional aggregation:
select
year(x.dt) yyyy,
month(x.dt) mm,
sum(sum(case when x.rating = 'low' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)) low,
sum(sum(case when x.rating = 'medium' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)) medium,
sum(sum(case when x.rating = 'high' then cnt else 0 end))
over(order by year(x.dt), month(x.dt)) high,
from taskDB t
cross apply (values
(rating, created, 1),
(rating, closed, -1)
) as x(rating, dt, cnt)
where x.dt is not null
group by year(x.dt), month(x.dt)
order by year(x.dt), month(x.dt)
You can filter as needed on a given period by turning this to a subquery and using a where
clause in the outer query.
Based on your new input, I created a table variable with all the year/month between the first and last ticket, I used this article for that: better way to generate months/year table Then I update each category, counting the tickets that have a closed date > first day of each month. This should give you the desired result.
Updated 8/17/2020 - Modified query to include tickets that were closed before the end of the month.
declare @FromDate datetime,
@ToDate datetime;
SET @FromDate = (Select min(created) From [dbo].[taskDB]);
SET @ToDate = (Select max(created) From [dbo].[taskDB]);
declare @openTicketsByMonth table (firstDayOfMonth datetime, firstDayNextMonth datetime, year int, month int, Low int, Medium int, High int, Critical int, NA int)
Insert into @openTicketsByMonth(firstDayOfMonth, firstDayNextMonth, year, month)
Select top (datediff(month, @FromDate, @ToDate) + 1)
dateadd(month, number, @FromDate),
dateadd(month, number + 1, @FromDate),
year(dateadd(month, number, @FromDate)),
month(dateadd(month, number, @FromDate))
from [master].dbo.spt_values
where [type] = N'P' order by number;
update R
Set R.Low = (Select count(1) from [dbo].[taskDB] where rating = 'Low' and created < R.firstDayNextMonth and (closed >= R.firstDayOfMonth or closed = '' or closed is null)),
R.Medium = (Select count(1) from [dbo].[taskDB] where rating = 'Medium' and created < R.firstDayNextMonth and (closed >= R.firstDayOfMonth or closed = '' or closed is null)),
R.High = (Select count(1) from [dbo].[taskDB] where rating = 'High' and created < R.firstDayNextMonth and (closed >= R.firstDayOfMonth or closed = '' or closed is null)),
R.Critical = (Select count(1) from [dbo].[taskDB] where rating = 'Critical' and created < R.firstDayNextMonth and (closed >= R.firstDayOfMonth or closed = '' or closed is null)),
R.NA = (Select count(1) from [dbo].[taskDB] where rating = 'N/A' and created < R.firstDayNextMonth and (closed >= R.firstDayOfMonth or closed = '' or closed is null))
From @openTicketsByMonth R
select year,
month,
Low,
Medium,
High,
Critical,
NA
from @openTicketsByMonth
Group by year and month instead of the full date.
select convert(varchar(max), year(created)) + '/' + right('0' + convert(varchar(max), month(created)),2) as Created
, COUNT(CASE WHEN rating = ‘high’ THEN 1 ELSE NULL END) as high,
COUNT(CASE WHEN rating = ‘med’ THEN 1 ELSE NULL END) as med,
COUNT(CASE WHEN rating = ‘low’ THEN 1 ELSE NULL END) as low FROM taskDB
GROUP BY convert(varchar(max), year(created)) + '/' + right('0' + convert(varchar(max), month(created)),2)
ORDER BY convert(varchar(max), year(created)) + '/' + right('0' + convert(varchar(max), month(created)),2) ASC
Make use of WIth clause in which convert the date in the required format and then group on that formatted date.
With TempTaskDB As (
SELECT convert(varchar(20), datepart(year, created)) + '/' + convert(varchar(20), datepart(month, created)) as CreatedDate,rating
from taskDB)
Select CreatedDate,
COUNT(CASE WHEN rating = ‘high’ THEN 1 ELSE NULL END) AS high,
COUNT(CASE WHEN rating = ‘med’ THEN 1 ELSE NULL END) AS med,
COUNT(CASE WHEN rating = ‘low’ THEN 1 ELSE NULL END) AS low
from TempTaskDB
group by CreatedDate
Order by CreatedDate Asc
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.