Group by Time Interval - sql

I need to Group my Table into 15 minutes Intervals. I can do that with:
select dateadd(minute, datediff(minute, 0, ts) / 15 * 15, 0), sum (goodpieces)
from StationCount
Group by dateadd(minute, datediff(minute, 0, ts) / 15 * 15, 0)
But to display the returned data in a chart i need to insert also the intervals which don't have any data and aren't currently appearing in my select statement. How do i insert these?

Create a table with every possible timestamp in 15 minute increments, and do a LEFT JOIN from it to your query above.
SELECT * FROM timestamps LEFT JOIN (SELECT dateadd......) ON timestamps.timestamp = StationCount.ts
If you know your chart always covers a 24 hour period, you only need to create a table with the numbers 0-95, then for each entry add it to the start time of your chart.
SELECT *
FROM (SELECT dateadd(minute, <starttime>, number*15) timestamp FROM numbers) timestamps LEFT JOIN
(SELECT dateadd......) ON timestamps.timestamp = StationCount.ts

Something like this might help you.
declare #startDate datetime
,#endDate datetime
set #startDate = '2011-10-25'
set #endDate = '2011-10-26'
;with fifteenMinutes
as
(
select dateadd(minute, datediff(minute, 0, #startDate) / 15 * 15, 0) as q
UNION ALL
select dateadd(minute, 15, q)
from fifteenMinutes
where q < #endDate
)
select * from fifteenMinutes

Related

Calculate the number of records for each date between 2 dates

I have to create a query to return results for a multi-axis chart. I need to count the number of Ids created for each date between 2 dates. I tried this:
DECLARE #StartDate datetime2(7) = '11/1/2020',
#EndDate datetime2(7) = '2/22/2021'
;WITH Date_Range_T(d_range) AS
(
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, #EndDate) - #StartDate, 0)
UNION ALL SELECT DATEADD(DAY, 1, d_range)
FROM Date_Range_T
WHERE DATEADD(DAY, 1, d_range) < #EndDate
)
SELECT d_range, COUNT(Id) as Total
FROM Date_Range_T
LEFT JOIN [tbl_Support_Requests] on ([tbl_Support_Requests].CreatedDate Between #StartDate AND #EndDate)
GROUP BY d_range ORDER BY d_range ASC
Of course, the problem is with the ;WITH which returns the error
Operand type clash: datetime2 is incompatible with int.
The above works if I give it a specific number of days from the current date like:
;WITH Date_Range_T(d_range) AS
(
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()) - 6, 0)
UNION ALL SELECT DATEADD(DAY, 1, d_range)
FROM Date_Range_T
WHERE DATEADD(DAY, 1, d_range) < GETDATE()
)
Which returns:
The problem is that I cannot figure out how to substitute the date range.
Improving on Dale K's answer, I suggest you use a tally table or function, as this is usually more performant.
I have used Itzik Ben-Gan's well-known one below:
DECLARE #StartDate date = '2020-11-01', #EndDate date = '2021-02-22';
WITH
L0 AS ( SELECT 1 AS c
FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
FROM L2 )
Date_Range_T (d_range) AS (
SELECT TOP(DATEDIFF(day, #StartDate, #EndDate) + 1)
DATEADD(day, rownum - 1, #StartDate) AS d_range,
DATEADD(day, rownum, #StartDate) AS d_rangeNext
FROM Nums
)
SELECT d_range, COUNT(Id) AS Total
FROM Date_Range_T
LEFT JOIN tbl_Support_Requests R
ON R.CreatedDate >= T.d_range AND R.CreatedDate < T.d_rangeNext
GROUP BY d_range
ORDER BY d_range ASC
No need to reinvent the wheel - there are many examples of recursive CTE calendar tables out there, similar to below.
DECLARE #StartDate date = '01-Nov-2020', #EndDate date = '22-Feb-2021';
WITH Date_Range_T (d_range) AS (
SELECT #StartDate AS d_range
UNION ALL
SELECT DATEADD(DAY, 1, d_range)
FROM Date_Range_T
WHERE DATEADD(DAY, 1, d_range) < #EndDate
)
SELECT d_range, COUNT(Id) AS Total
FROM Date_Range_T
LEFT JOIN tbl_Support_Requests R ON R.CreatedDate = d_range
GROUP BY d_range
ORDER BY d_range ASC
-- Set to the max number of days you require
OPTION (MAXRECURSION 366);
Comments:
Why use a datetime2 for a date?
Do you definitely want < the end date or <=?
Are you familiar with how between works - its not always intuitive.
Alias all tables for better readability.
Semi-colon terminate all statements.
Consistent casing makes the query easier to read.
Use an unambiguous date format for date strings.

How to subtract an offset to dates generated through a recursive CTE?

The query below uses a recursive CTE to generate days if there is no data for that specific day. I want to group the daily downtime total starting at 7:15 the previous day until 7:15 the next day and do it over a month. This query works fine but I need to subtract DATEADD(minute, -(7 * 60 + 15) from each day.
WITH dates as (
SELECT CONVERT(date, 'Anydate') as dte
UNION ALL
SELECT DATEADD(day, 1, dte)
FROM dates
WHERE dte < 'Anydate + 1 month later'
)
SELECT CONVERT(datetime,d.dte), ISNULL(SUM(long_stop_minutes), 0) AS downtime
FROM dates d LEFT JOIN
long_stops_table b
ON CAST(t_stamp as DATE) = d.dte AND Type = 'downtime'
GROUP BY CONVERT(datetime, d.dte)
ORDER BY CONVERT(datetime, d.dte) ASC;
Just subtract the appropriate time units. Here is one way:
SELECT d.dte,
COALESCE(SUM(lst.long_stop_minutes), 0) AS downtime
FROM dates d LEFT JOIN
long_stops_table lst
ON CONVERT(date, DATEADD(minute, -(7 * 60 + 15), lst.t_stamp) = d.dte AND
lst.Type = 'downtime'
GROUP BY d.dte
ORDER BY d.dte ASC;
I see no reason to convert dates.dte to a datetime, so I just removed the conversion.

MSSQL query range for a day over a month

I am trying to retrieve the range for the value field over each 24hr period over a predefined time period. The issue is that the time period is between 7am and 7am the next day. (not a daily figure)
For example, I would like the range for day 1, then range for day 2, etc. I've tried using the below query but the production field keeps coming back with the same data, could anyone please shed some light on how I can make this work?
Thank you very much.
select tagname, convert(date,datetime),
(
select (max(Value)-min(Value)) as Range
from Runtime.dbo.AnalogHistory
where (TagName = 'LS_CV004_WX1_PROD_DATA.Actual_Input')
and DateTime BETWEEN dateadd(hh,7,convert(datetime,convert(date,datetime))) AND dateadd(hh,31,convert(datetime,convert(date,datetime)))
) as Production
from runtime.dbo.analoghistory
where (TagName = 'LS_CV004_WX1_PROD_DATA.Actual_Input')
and datetime between '20151101' and '20151201'
group by tagname, convert(date,DateTime)
I would like to result to be as per below
tagname | date | production
Below is one method that uses CTEs to generate a row for each date in the specified range, and then joins to the table on the date plus a 7 hour offset. Consider creating a utility calendar table for this type of task.
DECLARE
#StartDate date = '20151101'
, #EndDate date = '20151201';
WITH
t8 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
, t512 AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS num FROM t8 AS a CROSS JOIN t8 AS b CROSS JOIN t8 AS c)
, dates AS (SELECT DATEADD(day, num, #StartDate) AS Date FROM t512 WHERE num <= DATEDIFF(day, #StartDate, #EndDate))
SELECT TagName, Date, MAX(Value)-MIN(Value) as Production
FROM dates
JOIN dbo.AnalogHistory ON
AnalogHistory.DateTime >= DATEADD(hour, 7, Date)
AND AnalogHistory.DateTime < DATEADD(hour, 31, Date)
GROUP BY TagName, Date
ORDER BY TagName, Date;
Your subquery is not correlated to the outer query, so it is no surprise that it returns the same value. I think you want something like this:
select tagname, convert(date, datetime),
(select (max(ah2.Value) - min(ah2.Value)) as Range
from Runtime.dbo.AnalogHistory ah2
where ah2.TagName = 'LS_CV004_WX1_PROD_DATA.Actual_Input' and
ah2.DateTime BETWEEN dateadd(hour, 7, convert(datetime, convert(date, ah.datetime))) AND
dateadd(hour, 31, convert(datetime, convert(date, ah.datetime)))
) as Production
from runtime.dbo.analoghistory ah
where TagName = 'LS_CV004_WX1_PROD_DATA.Actual_Input' and
datetime between '20151101' and '20151201'
group by tagname, convert(date, DateTime);
Note the use of ah2 and ah in the subquery.

SQL Query by time interval

So I have used this post as a reference, however I would like to count all the rows based on a 15 minute time period.
Here is what I have so far:
SELECT DateAdd(minute, DateDiff(minute, 0, [datetime]), 0) as Timestamp,
Count(*) as Tasks
FROM [table]
GROUP BY DateAdd(minute, DateDiff(minute, 0, [datetime]), 0)
ORDER BY Timestamp
This is great for getting rows per minute, however I need 15 minutes...
So I change:
DateAdd(minute, DateDiff(minute, 0, [datetime]), 0)
to
DateAdd(minute, DateDiff(minute, 0, [datetime]), 15)
however that is just pushing the date 15 days ahead.
Any help is appreciated!
To get 15 minutes, divide by 15 (and then multiply again):
SELECT DateAdd(minute, 15*(DateDiff(minute, 0, [datetime]) / 15), 0
) as Timestamp,
Count(*) as Tasks
FROM [table]
GROUP BY (DateDiff(minute, 0, [datetime]) / 15)
ORDER BY Timestamp;
SQL Server does integer division. If you want to be unambiguous about your intentions, use FLOOR().
SELECT ROUND(DATEDIFF(SECOND,{d '1970-01-01'},[datetime])/(15 * 60),0) as Timestamp,
Count(*) as Tasks
FROM [table]
GROUP BY ROUND(DATEDIFF(SECOND,{d '1970-01-01'},[datetime])/(15 * 60),0)
ORDER BY Timestamp
Here is an alternative in case integer division causes an issue for you. It casts the datetime as a float and then uses floor().
SELECT convert(varchar,cast(round(floor(cast([datetime] as float(53))*24*4)/(24*4),5) as smalldatetime),108) as Timestamp,
Count(*) as Tasks
FROM [table]
GROUP BY convert(varchar,cast(round(floor(cast([datetime] as float(53))*24*4)/(24*4),5) as smalldatetime),108)
ORDER BY Timestamp
I normally change the (24*4) to 96 (the number of 15 minute intervals in a day), but thought I'd leave it so people can see how to adapt it for other time periods.

SQL statement to select all rows from previous day

I am looking for a good SQL Statement to select all rows from the previous day from one table. The table holds one datetime column. I am using SQL Server 2005.
get today no time:
SELECT dateadd(day,datediff(day,0,GETDATE()),0)
get yestersday no time:
SELECT dateadd(day,datediff(day,1,GETDATE()),0)
query for all of rows from only yesterday:
select
*
from yourTable
WHERE YourDate >= dateadd(day,datediff(day,1,GETDATE()),0)
AND YourDate < dateadd(day,datediff(day,0,GETDATE()),0)
To get the "today" value in SQL:
convert(date, GETDATE())
To get "yesterday":
DATEADD(day, -1, convert(date, GETDATE()))
To get "today minus X days": change the -1 into -X.
So for all yesterday's rows, you get:
select * from tablename
where date >= DATEADD(day, -1, convert(date, GETDATE()))
and date < convert(date, GETDATE())
It's seems the obvious answer was missing. To get all data from a table (Ttable) where the column (DatetimeColumn) is a datetime with a timestamp the following query can be used:
SELECT * FROM Ttable
WHERE DATEDIFF(day,Ttable.DatetimeColumn ,GETDATE()) = 1 -- yesterday
This can easily be changed to today, last month, last year, etc.
SELECT * from table_name where date_field = DATE_SUB(CURRENT_DATE(),INTERVAL 1 DAY);
Its a really old thread, but here is my take on it.
Rather than 2 different clauses, one greater than and less than. I use this below syntax for selecting records from A date. If you want a date range then previous answers are the way to go.
SELECT * FROM TABLE_NAME WHERE
DATEDIFF(DAY, DATEADD(DAY, X , CURRENT_TIMESTAMP), <column_name>) = 0
In the above case X will be -1 for yesterday's records
This should do it:
WHERE `date` = CURDATE() - INTERVAL 1 DAY
Can't test it right now, but:
select * from tablename where date >= dateadd(day, datediff(day, 1, getdate()), 0) and date < dateadd(day, datediff(day, 0, getdate()), 0)
In SQL Server do like this:
where cast(columnName as date) = cast(getdate() -1 as date)
You should cast both sides of the expression to date to avoid issues with time formatting.
If you need to control interval in more detail, then you should try something like:
declare #start datetime = cast(getdate() - 1 as date)
declare #end datetime = cast(getdate() - 1 as date)
set #end = dateadd(second, 86399, #end)
Another way to tell it "Yesterday"...
Select * from TABLE
where Day(DateField) = (Day(GetDate())-1)
and Month(DateField) = (Month(GetDate()))
and Year(DateField) = (Year(getdate()))
This conceivably won't work well on January 1, as well as the first day of every month. But on the fly it's effective.
Well, its easier to cast the datetime column to date and than compare.
SELECT * FROM TABLE_NAME WHERE cast(COLUMN_NAME as date) =
dateadd(day,0, convert(date, getdate(), 105))
A simple alternative
Select GETDATE() - 1
Change 1 to go back that many number of days
PS : This gives you timestamp accuracy.
This worked a charm:
SELECT * FROM mytable WHERE date(mydate) = DATE_SUB(CURDATE(), INTERVAL 1 DAY);
subdate(now(),1) will return yesterdays timestamp
The below code will select all rows with yesterday's timestamp
Select * FROM `login` WHERE `dattime` <= subdate(now(),1) AND `dattime` > subdate(now(),2)