SQL Query | Summarize TimeSeries - sql

Wondering if anyone can help me out. I have a simple table with the following fields.
ID (int),
TimeStamp (DateTime),
Status (NvarChar)
I need to produce a table with a Count of all Status' for the last 2 hours in 10 minute slots like the example provided. The idea is to produce a Google Line Chart in a dashboard where it will refresh every 10 minutes.
Example:
Table
any help would be appreciated.
José

you could create your time slots with a recursive cte and join to that.
with cte as (
select DATETIMEFROMPARTS(datepart(year,getdate()), datepart(month,getdate()), datepart(day,getdate()), datepart(hour, getdate()), floor((datepart(minute, getdate()) - 9) / 10) * 10, 0, 0) as startDT,
DATETIMEFROMPARTS(datepart(year,getdate()), datepart(month,getdate()), datepart(day,getdate()), datepart(hour, getdate()), floor((datepart(minute, getdate()) + 9) / 10) * 10, 0, 0) as endDT
union all
select DATEADD(minute, -10, startDT),
DATEADD(minute, -10, endDt)
from cte
where DATEADD(minute, -130, getdate()) < DATEADD(minute, -10, startDT)
)
select endDt as [Period],
count(case when [Status] = 'OK' then 1 end) as Status_OK,
count(case when [Status] <> 'OK' then 1 end) as Status_NOK
from cte
left join myTable on [TimeStamp] >= startDT and [TimeStamp] < endDT
group by endDT
if you prefer to use dateadd then
;with cte as (
select dateadd(minute, datediff(minute, 0, getdate()) / 10 * 10, 0) as startDT,
dateadd(minute, datediff(minute, 0, getdate()) / 10 * 10 + 10, 0) as endDT
union all
select dateadd(minute, -10, startDT),
dateadd(minute, -10, endDt)
from cte
where dateadd(minute, -130, getdate()) < dateadd(minute, -10, startDT)
)
select endDt as [Period],
count(case when [Status] = 'OK' then 1 end) as Status_OK,
count(case when [Status] <> 'OK' then 1 end) as Status_NOK
from cte
left join myTable on [TimeStamp] >= startDT and [TimeStamp] < endDT
group by endDT

Related

Create table with each customer and each hour of the month

I have a list of customers that we can receive calls for each month. I generate a list with a simple
SELECT DISTINCT(Campaign)
FROM [Reporting].[dbo].[New_Five9_CallLog]
WHERE DATEDIFF(MM,TimeStamp,GETDATE()) <= 12
I also have short little recursion query I can use to create an entry for each hour of the current month
WITH time_CTE
AS (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0) AS [DateTime]
UNION ALL
SELECT dateadd(hour, 1, [DateTime])
FROM time_CTE
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(GETDATE())
)
SELECT *
FROM time_CTE
OPTION (maxrecursion 0);
I also have a query that will show me the total calls received for each client, for each hour of the month
SELECT
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0) as RoundedToHour,
Campaign,
COUNT(*) AS Actual
FROM [Reporting].[dbo].[New_Five9_CallLog] WITH (NOLOCK)
WHERE DATEDIFF(mm, Timestamp, GETDATE()) = 1
AND Call_Type = 'Inbound'
GROUP BY Campaign,
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0)
My ultimate goal is to have a procedure I can run at the beginning of each month that will update a table and add a 4 week average for each client, for each hour of the month. I have the 3 basic parts above I believe, but for the life of me I can't figure out how to add it all together properly.
My first attempt was to create a table that simply had a row for each hour of the month, for each client that we can get calls for.
DECLARE #date DateTime SELECT DATEADD(DAY, 1, EOMONTH(getdate(), -1))
DECLARE #comp varchar SELECT DISTINCT(Campaign) FROM [Reporting].[dbo].[New_Five9_CallLog] WHERE
DATEDIFF(MM,TimeStamp,GETDATE()) <= 12
WHILE #date < EOMONTH(GETDATE())
BEGIN
WITH time_CTE ([DateTime],Comp)
AS
(
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, #date), 0) AS [DateTime],
#comp
UNION ALL
SELECT dateadd(hour, 1, [DateTime]),
#comp
FROM time_CTE
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(#date)
)
SELECT *
FROM time_CTE
OPTION (maxrecursion 0);
SET #date = #date +1;
END;
However this did practically nothing. I understand SQL pretty well, but I have never had to do a For Loop in SQL before, and I just can't wrap my head around how to structure this to make it work.
The end table would hopefully look like this;
DateTime Campaign Avg
1/1/2021 00:00 Client 1 4
1/1/2021 00:00 Client 2 0
1/1/2021 01:00 Client 1 2
1/1/2021 01:00 Client 2 1
etc....
I was able to make this work with the following, which will create a row for each hour of the month, for each client that has calls.
DECLARE #CompTable TABLE (comp varchar(max))
INSERT INTO #CompTable
SELECT DISTINCT Campaign
FROM [Reporting].[dbo].[New_Five9_CallLog]
WHERE DATEDIFF(MM,TimeStamp,GETDATE()) <= 12
DECLARE #comp varchar(max)
WHILE EXISTS (SELECT 1 FROM #CompTable)
BEGIN
SELECT TOP 1 #comp = comp FROM #CompTable;
WITH Time_CTE ([DateTime])
AS (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0) AS [DateTime]
UNION ALL
SELECT dateadd(hour, 1, [DateTime])
FROM time_CTE
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(GETDATE())
)
SELECT *,
(Select TOP 1 #comp
FROM #CompTable) AS Campaign
FROM time_CTE
OPTION (maxrecursion 0)
DELETE FROM #CompTable WHERE comp = #comp
END

Combine Queries where one is recursive

I have a recursive query that creates a row for each hour of the previous month as follows;
WITH a AS (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0) AS [DateTime]
UNION ALL
SELECT dateadd(hour, 1, [DateTime])
FROM a
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(GETDATE(), -1)
)
SELECT
a.*
FROM a
OPTION (maxrecursion 0);
This produces these results;
DateTime
2020-11-01 00:00:00.000
2020-11-01 01:00:00.000
2020-11-01 02:00:00.000
etc.....
Next I have a query that calculates the number of calls per customer, per hour, for the previous month, as a 4 week average as follows;
SELECT
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0) as RoundedToHour,
Campaign,
COUNT(*)/4 AS Average
FROM [Reporting].[dbo].[New_Five9_CallLog] WITH (NOLOCK)
WHERE DATEDIFF(mm, Timestamp, GETDATE()) = 1
AND Call_Type = 'Inbound'
GROUP BY Campaign,
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0)
This produces the following results;
RoundedToHour Campaign Average
2020-11-01 02:00:00.000 Client1 0
2020-11-01 04:00:00.000 Client2 2
etc....
What I am having trouble doing is combining these two. My initial thoughts were to use a CTE of the recursive query as basically a where clause in my second query, but since you have to use a WITH for CTE's, and I have to use a WITH for my recursive query, that won't directly work, because you can't have nested WITH's.
My final result I am looking for is a single query that produces the 4 week average of calls for each hour of the previous month, for each client. I am open to changing how I am doing any of this if someone has a better suggestion on how to reach my ultimate goal.
You can't declare a CTE in a sub-query but you can declare multiple CTEs together. So declare both and then LEFT JOIN them.
WITH a AS (
SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-1, 0) AS [DateTime]
UNION ALL
SELECT dateadd(hour, 1, [DateTime])
FROM a
WHERE dateadd(hour, 1, [DateTime]) < EOMONTH(GETDATE(), -1)
), b AS (
SELECT
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0) as RoundedToHour,
Campaign,
COUNT(*)/4 AS Average
FROM [Reporting].[dbo].[New_Five9_CallLog] WITH (NOLOCK)
WHERE DATEDIFF(mm, Timestamp, GETDATE()) = 1
AND Call_Type = 'Inbound'
GROUP BY Campaign,
DATEADD(hour, DATEDIFF(hour, 0,
DATEADD(minute, 30 - DATEPART(minute, Timestamp + '00:30:00.000'),
Timestamp)), 0)
)
SELECT a.*, b.*
FROM a
LEFT JOIN b on b.RoundedToHour = a.DateTime
OPTION (maxrecursion 0);

Add numbers from three different queries together

I am getting average values for different past 3, 6 and 10 months.From following queries
select SUM(GrossAmount)/10 as Averageten
FROM Table
WHERE CreatedDate >= DATEADD(MONTH, -10, GETDATE())
select SUM(GrossAmount)/6 as Averagesix
FROM Table
WHERE CreatedDate >= DATEADD(MONTH, -6, GETDATE())
select SUM(GrossAmount)/3 as Averagethree
FROM Table
WHERE CreatedDate >= DATEADD(MONTH, -3, GETDATE())
I get three different values for example:
1200.22
2300.22
4500
I want to get the average of those three values like this
(1200.22 + 2300.22 + 4500) / 3
How can I add values from these separate queries.
Use union to merge three queries and then do average
select avg(Averageten)
from
(select SUM(GrossAmount)/10 as Averageten
FROM Table
WHERE CreatedDate >= DATEADD(MONTH, -10, GETDATE())
union
select SUM(GrossAmount)/6
FROM Table
WHERE CreatedDate >= DATEADD(MONTH, -6, GETDATE())
union
select SUM(GrossAmount)/3
FROM Table
WHERE CreatedDate >= DATEADD(MONTH, -3, GETDATE())
)a
OR you can use conditional aggregation to find the values as you query is from same table with different condition
select ((sum(case when CreatedDate >= DATEADD(MONTH, -10, GETDATE()) then GrossAmount end)/10)
+(sum(case when CreatedDate >= DATEADD(MONTH, -6, GETDATE()) then GrossAmount end)/6)
+(sum(case when CreatedDate >= DATEADD(MONTH, -3, GETDATE()) then GrossAmount end)/3))/3
from tablename
Use conditional aggregation.
SELECT
(
SUM(CASE WHEN CreatedDate >= DATEADD(MONTH, -10, GETDATE()) THEN GrossAmount END) / 10 +
SUM(CASE WHEN CreatedDate >= DATEADD(MONTH, -6, GETDATE()) THEN GrossAmount END) / 6 +
SUM(CASE WHEN CreatedDate >= DATEADD(MONTH, -3, GETDATE()) THEN GrossAmount END) / 3
) / 3 AS [10, 6, 3 Average]
FROM Table
WHERE CreatedDate >= DATEADD(MONTH, -10, GETDATE()) -- this covers all three cases

SQL Server Group BY weekly clause blank if no data

No. 1 Query group by weekly
SELECT
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0) AS WeekStart,
SUM(Soil) AS TotalSoil,
SUM(Rock) AS TotalRock,
SUM(Concrete) AS TotalConcrete,
SUM(Steel) AS TotalSteel,
SUM(MovementPiles) AS TotalMovementPiles,
SUM(Total) AS OverallTotal
FROM
BplPjtRevenueStreams
WHERE
DailyDate > '2016-11-28 00:00:00.000'
AND DailyDate < '2016-12-05 00:00:00.000'
AND Project_Id = 5
GROUP BY
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0)
No. 2 query which have empty data in the table since table has no project_Id = 6 yet
SELECT
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0) AS WeekStart,
SUM(Soil) AS TotalSoil,
SUM(Rock) AS TotalRock,
SUM(Concrete) AS TotalConcrete,
SUM(Steel) AS TotalSteel,
SUM(MovementPiles) AS TotalMovementPiles,
SUM(Total) AS OverallTotal
FROM
BplPjtRevenueStreams
WHERE
DailyDate > '2016-11-28 00:00:00.000'
AND DailyDate < '2016-12-05 00:00:00.000'
AND Project_Id = 6
GROUP BY
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0)
May I know if I want to do something like this for the No. 2 query?
Week Start TotalSoil TotalRock TotalConcrete TotalSteel TotalMovementPiles OverallTotal
1 2016-11-28 00:00:00.000 NULL NULL NULL NULL NULL NULL
1 2016-12-05 00:00:00.000 NULL NULL NULL NULL NULL NULL
Is it possible to do like this if it's empty result in the table?
DECLARE
#Project_Id int = 6,
#RangeFrom datetime = '2016-11-28 00:00:00.000',
#RangeTo datetime = '2016-12-05 00:00:00.000'
;WITH Numbers AS
(
SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) t(Value)
), Weeks AS
(
-- All weeks start from year 2000, Monday
SELECT DATEADD(WEEK, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), '1999-12-27') AS DateValue
FROM Numbers a CROSS JOIN Numbers b CROSS JOIN Numbers c CROSS JOIN Numbers d
), Data AS
(
-- This is your query
SELECT
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0) AS WeekStart,
SUM(Soil) AS TotalSoil,
SUM(Rock) AS TotalRock,
SUM(Concrete) AS TotalConcrete,
SUM(Steel) AS TotalSteel,
SUM(MovementPiles) AS TotalMovementPiles,
SUM(Total) AS OverallTotal
FROM
BplPjtRevenueStreams
WHERE
DailyDate > #RangeFrom
AND DailyDate < #RangeTo
AND Project_Id = #Project_Id
GROUP BY
DATEADD(week, DATEDIFF(week, 0, DailyDate), 0)
)
SELECT
CAST(w.DateValue AS datetime) AS WeekStart,
d.TotalSoil,
d.TotalRock,
d.TotalConcrete,
d.TotalSteel,
d.TotalMovementPiles,
d.OverallTotal
FROM Weeks w LEFT OUTER JOIN Data d ON w.DateValue = CAST(d.WeekStart AS date)
WHERE
w.DateValue BETWEEN CAST(#RangeFrom AS date) AND CAST(#RangeTo AS date)

Show Dates that have no values for selected column

I have the following query which counts the number of items created on a particular date in the last 10 days
SELECT
CONVERT (DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) AS 'Logged Date',
Count (*) AS 'Total'
FROM
MTV_System$WorkItem$Incident
WHERE
CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688 >= DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)
GROUP BY
CONVERT(DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688)
How do I get this to show the dates which have no values present (i.e. get every date value for the last 10 days, return the count if there is data or 0 if none). Using SQL Server 2012.
You can write a recursive cte to get the date for the last 10 days into a table as follows:
WITH TableA (StartDate) AS (SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)),
q as (
SELECT StartDate
, Number = 0
FROM TableA
UNION ALL
SELECT DATEADD(d,1,StartDate)
, Number = Number + 1
FROM q
WHERE 10 > Number )
Then join q with your original query, to get a row for every date.
select q.StartDate, yourtable.Total from q
left join (
SELECT
CONVERT (DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) AS 'Logged Date',
Count (*) AS 'Total'
FROM
MTV_System$WorkItem$Incident
WHERE
CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688 >= DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)
GROUP BY
CONVERT(DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688)
) as yourtable on [Logged Date] = q.StartDate
Similar to BeanFrog's answer but a little shorter
-- sample data for testing
declare #MTV_System$WorkItemIncident table (
[CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688] DATE,
[Total] INT
);
INSERT INTO #MTV_System$WorkItemIncident VALUES ('2015-11-23', 23);
INSERT INTO #MTV_System$WorkItemIncident VALUES ('2015-11-21', 21);
INSERT INTO #MTV_System$WorkItemIncident VALUES ('2015-11-30', 30);
-- now the query
WITH TableA (LoggedDate) AS (
SELECT TOP 10 CONVERT (DATE, DATEADD(DAY, number * -1, GETDATE())) AS 'LoggedDate'
FROM master.dbo.spt_values
WHERE name IS NULL
)
SELECT TableA.[LoggedDate],
SUM(ISNULL(Data.Total, 0)) AS 'LoggedCount'
FROM TableA
LEFT JOIN #MTV_System$WorkItemIncident AS Data ON CONVERT (DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) = TableA.[LoggedDate]
GROUP BY TableA.[LoggedDate]
Not that there is anything wrong with BeanFrog's answer, but if you don't want to use a recursive cte you could do this:
CREATE TABLE MTV_System$WorkItem$Incident (id int PRIMARY KEY, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688 datetime)
INSERT INTO MTV_System$WorkItem$Incident VALUES (1, '20151201')
INSERT INTO MTV_System$WorkItem$Incident VALUES (2, '20151126')
INSERT INTO MTV_System$WorkItem$Incident VALUES (3, '20151127')
INSERT INTO MTV_System$WorkItem$Incident VALUES (4, '20151127')
SELECT
ReportDate AS 'Logged Date',
Count (CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) AS 'Total'
FROM (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 0, 0) AS ReportDate
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 1, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 2, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 3, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 4, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 5, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 6, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 7, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 8, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 9, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)
) AS Dates
LEFT JOIN MTV_System$WorkItem$Incident ON ReportDate = CONVERT(DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688)
GROUP BY ReportDate
BeanFrog's answer has the advantage of being able to easily change the number of days though.