If you are familiar with Microsoft Log Parser you probably recognize the Quantize function which will truncate a value to the nearest multiple of another value. It is quite handy for grouping date-time fields into increments.
Date-Time Count
1/1/2010 00:00 100
1/1/2010 00:15 134
1/1/2010 00:30 56
....
I'm trying to find a similar function in Transaction-SQL (specifically SQL Server 2005 or 2008) that will allow me to do a similar grouping on date-time.
You can round to any given number of minutes like so:
DateAdd(Minute, (DateDiff(minute, 0, getutcdate() )/15) * 15, 0)
Instead of using getutcdate() you can use your date column, variable or expression. In addition the number of minutes can be a variable.
declare #minutesQuantize int set #minutesQuantize = 15
DateAdd(Minute, (DateDiff(minute, 0, getutcdate() )/#minutesQuantize) * #minutesQuantize, 0)
The only rule is that the date difference must fit into an integer, I.e. be less than 2 billion. That means you can't do seconds or milliseconds without a more complicated expression.
If you need seconds or milliseconds do this:
dateadd(ms, (datediff(ms, dateadd(day, datediff(day, 0, #date), 0), #date)/#msInterval)*#msInterval, dateadd(day, datediff(day, 0, #date), 0))
Or, if you want to wrap this into a function:
create function dbo.DateRoundMinutes(#dt datetime, #minutes int)
returns datetime
as begin
return DateAdd(Minute, (DateDiff(minute, 0, #dt )/#minutes) * #minutes, 0)
end
go
create function dbo.DateRoundMilliseconds(#dt datetime, #ms int)
returns datetime
as begin
return dateadd(ms, (datediff(ms, dateadd(day, datediff(day, 0, #dt), 0), #dt)/#ms)*#ms, dateadd(day, datediff(day, 0, #dt), 0))
end
Which you can use like this:
select t.dt,
dbo.DateRoundMilliseconds(dt, 500) dt0_5Second, -- Half second
dbo.DateRoundMilliseconds(dt, 5000) dt5second, -- 5 second
dbo.DateRoundMilliseconds(dt, 15000) dt15Second,
dbo.DateRoundMilliseconds(dt, 90000) dt90Second,
dbo.DateRoundMinutes(dt, 2) dt2Minute,
dbo.DateRoundMinutes(dt, 5) dt5Minute,
dbo.DateRoundMinutes(dt, 15) dt15Minute,
dbo.DateRoundMinutes(dt, 90) dt90Minute
from
/* some table having a column dt */
Not directly, it doesn't. But you can group by a function (that you write) that rounds the datetime column to its nearest quarter-hour (or whatever Quantize does).
SELECT
dbo.QuarterHour(DateColumn) AS Date-Time
, COUNT(*) AS Count
FROM MyTable
GROUP BY dbo.QuarterHour(DateColumn)
Related
I would like into a stored procedure, truncate timestamp input values at the top hour or at the lower hour.
For example, if my input values are 2020-02-12 06:56:00 and 2020-02-12 07:14:00, I would like to transforme it in 2020-02-12 06:00:00 and 2020-02-12 08:00:00
Is a cast function can work?
You can construct the new datetimes from the parts that you want of your original datetimes.
declare #start datetime = '2020-02-12 06:56:00'
declare #end datetime = '2020-02-12 07:14:00'
select #start as OriginalStart,
#end as OriginalEnd,
datetimefromparts(year(#start), month(#start), day(#start), datepart(hour, #start), 0, 0, 0) as TruncatedStart,
dateadd(hour, 1, datetimefromparts(year(#end), month(#end), day(#end), datepart(hour, #end), 0, 0, 0)) as TruncatedEnd
The first truncation of the interval is the lower hour, and the second one adds an additional hour so it returns the higher hour.
PS: If what you want is to round to the nearest hour, then you can add 30 minutes and truncate :
declare #date datetime = '2020-02-12 06:56:00'
set #date = dateadd(minute, 30, #date)
select datetimefromparts(year(#date), month(#date), day(#date), datepart(hour, #date), 0, 0, 0) as NearestHour
or in a single step (using Lepetit's shortcut for truncation) :
declare #date datetime = '2020-02-12 06:56:00'
select dateadd(hour, datediff(hour, 0, dateadd(minute, 30, #date)), 0) AS NearestHour
This is a simpler solution:
declare #start datetime = '2020-02-12 06:56:00'
declare #end datetime = '2020-02-12 07:14:00'
select #start as OriginalStart,
#end as OriginalEnd,
dateadd(hour, datediff(hour, 0, #start), 0) as TruncatedStart,
dateadd(hour, datediff(hour, 0, dateadd(hour, 1, #end)), 0) as TruncatedEnd
In both cases the function substracts the hour part from the original timestamp. For the TruncatedEnd, one hour is added, so that the result is the subsequent hour.
Using a bit of arithmetic calculation, convert to hours with decimal and use floor() and ceiling() to perform the round up / down
first it find the time different with 00:00:00 in terms of second. convert(date, date_col) convert the datetime to date, so effectively it is 00:00:00
datediff(second, convert(date, date_col), date_col)
then you divide by 60 x 60 = 3600 seconds. Gives you fraction of hours
then you use floor() or ceiling() to perform the rounding
and lastly you add that back to the date (convert(date, date_col))
Final query
select *,
RoundDown = convert(datetime, convert(date, date_col))
+ dateadd(hour, floor(datediff(second, convert(date, date_col), date_col) / (3600.0)), 0),
RoundUp = convert(datetime, convert(date, date_col))
+ dateadd(hour, ceiling(datediff(second, convert(date, date_col), date_col) / (3600.0)), 0)
from (
values
('2020-02-12 06:56:00'),
('2020-02-12 07:14:00')
) d (date_col)
/*
2020-02-12 06:56:00 2020-02-12 06:00:00 2020-02-12 07:00:00
2020-02-12 07:14:00 2020-02-12 07:00:00 2020-02-12 08:00:00
*/
EDIT : a much simpler query below
find the different in minute divide by 60.0 minutes to get different in terms of hour (with decimal places) and then apply floor or ceiling. Finally add that result back
select getdate() as Now,
dateadd(hour, floor(datediff(minute, 0, getdate()) / 60.0), 0) as RoundDown,
dateadd(hour, ceiling(datediff(minute, 0, getdate()) / 60.0), 0) as RoundUp
SELECT
CONVERT(BIGINT, DATEDIFF(ss, '1970-01-01 00:00:00', replogindatetime)) AS login,
CONVERT(BIGINT, DATEDIFF(ss, '1970-01-01 00:00:00', replogoutdatetime)) AS logout
FROM
#Table
The RepLoginDateTime & RepLogoutDateTime are 2 columns in SQL Server datetime datatype, which I have to convert to epoch timestamp, but I get this error:
Msg 535, Level 16, State 0, Line 23
The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.
I'm using SQL Server 2008
DATEDIFF in SQL Server returns an int. From SQL Server 2016 you also have DATEDIFF_BIG, which returns a bigint. This means, if you have a value that is going to be too large for an int you'll get an overflow error when using DATEDIFF.
It, however, surprises me your getting an overflow here, as 1970-01-01 plus 2,147,483,647 seconds is 2038-01-19 03:14:07.
Anyway, instead, why not get the days, and then the seconds, and add:
SELECT CONVERT(BIGINT,DATEDIFF(DAY, '19700101','2038-10-12 12:30:49')) * 86400 +
DATEDIFF(ss, DATEADD(DAY, DATEDIFF(DAY, 0, '2038-10-12 12:30:49'),0), '2038-10-12 12:30:49')
You have to go around the conversion functions and use a BIGINT instead.
DECLARE #Epoch DATETIME = CAST(0 as DATETIME)
DECLARE #TestDate DATETIME = GETDATE()
DECLARE #DaysPast BIGINT = (SELECT DATEDIFF(DAY, #Epoch,#TestDate))
DECLARE #TimeOfDayInMilliseconds BIGINT = (SELECT DATEDIFF(ms, 0, DATEADD(Day, 0 - DATEDIFF(Day, 0, #TestDate), #TestDate)))
SELECT (#DaysPast * 86400000) + #TimeOfDayInMilliseconds
I am trying to get a smalldatetime value of "9pm today" in a query. I thought I could use
DATEADD(HOUR, 21, CONVERT(date, GETDATE()))
but SQL Server doesn't like that - I get the error
The datepart hour is not supported by date function dateadd for data
type date.
Suggestions for a workaround?
Pretty simple, just cast date back to datetime after casting to date.
Thus you'll get current_date 00:00:00 and then add 21 hours:
select dateadd(hh, 21, cast(cast(getdate() as date) as datetime))
it is because dateadd's 3rd parameter should be datetime type, not date.
SELECT DATEADD(HOUR, 21, CONVERT(datetime,CONVERT(date, GETDATE())))
just add 21 / 24.0 to todays date
Select dateadd(day, datediff(day, 1, getDate()), 1) + (21 / 24.0)
First part, dateadd(day, datediff(day, 1, getDate()), 1), strips time from getdate(),
second part, + (21 / 24.0), adds fractional part of day equal to 9 am
This works because internally, SQL Server represents datetimes as two integers, one for the date, (number of days since 1 Jan 1900), and a second integer for the time, (number of ticks since midnight), which it combines into a decimal value where the integer part is the date integer, and the decimal part is the fraction of one day, so if you add 0.5 to a date, you get noon on that day, etc.
or, for comparison, using dateadd for hours,
Select dateadd(hour, 21, dateadd(day, datediff(day, 1, getDate()), 1))
Could you help me with this please.
I would like to get the records only if the DATEDIFF(day, due_date, GETDATE()) is more than 60 but is less than 90 days (or between 60 days and 90 days). If it is lesser than 60 or greater than 90 days, then leave it out).
Thank you!
a sargable alternative =>> it does not use functions on the data
-- DECLARE #today datetime = CAST(GETDATE() AS date) -- an option
DECLARE #today datetime = DATEADD(DAY, DATEDIFF(DAY,0, GETDATE()), 0)
SELECT
due_date
FROM some_table
WHERE (
due_date >= DATEDIFF(DAY, -90, #today)
AND due_date < DATEDIFF(DAY, -60, #today)
)
GETDATE() returns both date and the current time, so to get "today" at 00:00:00 you need to either cast getdate to a date, or use the dateadd function as shown above.
Then instead of calculating the days different for each row of data (2 functions for each row) and filtering on that calculated column (this could require hundreds, thousands or millions of calculations), why not compare the existing data to 2 calculated dates?
See: Sargable
Try this:
declare #today smalldatetime = getdate()
select
due_date
from
some_table
where
datediff(day, due_date, #today) > 60 and
datediff(day, due_date, #today) < 90
I have a console app that will choose records based on if the user has chosen to be notified daily at a specific hour of the day as well as all records where the user has chosen hourly.
The job will run every 15 minutes.
How do I find a threshold of time between 5 minutes before and 5 minutes after the top of the hour?
This is my code
Declare #Now datetime
Declare #NowHour int
Declare #NowMinute int
Declare #NewNow nvarchar(50)
Set #Now = {fn Now()}
Set #NowHour = (SELECT DATENAME(hh, #Now))
Set #NowMinute = (SELECT DATENAME(mi, #Now))
Set #NewNow = Cast(Cast(#NowHour As nvarchar(2)) + ':' + Cast(#NowMinute As nvarchar(2)) as Time)
Select #Now, #NowHour, #NowMinute, #NewNow
Select * From vw_consumerAlerts
Where casexid not in
(Select casexid from alerthistory)
And Name = 'Always'
Or ([Hour] Between DateAdd(minute, -5, #NewNow) And DateAdd(minute, -5, #NewNow))
This is the error I'm getting
The data types time and datetime are incompatible in the greater than or equal to operator.
I got it, I had [Hour] and #NewNow swapped, now I get the correct results with:
(#NewNow Between DateAdd(minute, -5, [Hour]) And DateAdd(minute, 5, [Hour]))
Assuming you have defined #TheHour as being the hour in question with 00 minutes/seconds:
WHERE mydatecol BETWEEN DATEADD(minute, -5, #TheHour) AND DATEADD(minute, 5, #TheHour)
UPDATE
See this:
select 1
where cast('1/1/2010 1:04pm' as time)
between cast('1/1/2010 12:55pm' AS time)
and cast ('1/1/2010 1:05pm' as time)
time and datetime are different, so you need to cast your datetime to time as I've done in the crude example here.
So in your case:
Select * From vw_consumerAlerts
Where casexid not in
(Select casexid from alerthistory)
And Name = 'Always'
Or ([Hour] Between cast(DateAdd(minute, -5, #NewNow) as time)
And cast(DateAdd(minute, -5, #NewNow) as time))