Like when I do
SELECT [Date]
FROM [FRIIB].[dbo].[ArchiveAnalog]
GROUP BY [Date]
How can I specify the group period? I'm using MS SQL 2008.
I've tried this, both with % 10 and / 10.
SELECT MIN([Date]) AS RecT, AVG(Value)
FROM [FRIIB].[dbo].[ArchiveAnalog]
GROUP BY (DATEPART(MINUTE, [Date]) / 10)
ORDER BY RecT
Is it possible to make Date output without milliseconds?
finally done with
GROUP BY
DATEPART(YEAR, DT.[Date]),
DATEPART(MONTH, DT.[Date]),
DATEPART(DAY, DT.[Date]),
DATEPART(HOUR, DT.[Date]),
(DATEPART(MINUTE, DT.[Date]) / 10)
Short and sweet
GROUP BY DATEDIFF(MINUTE, '2000', date_column) / 10
With heavy acknowledgements to Derek's answer, which forms the core of this one.
Practical usage
SELECT DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
AS [date_truncated],
COUNT(*) AS [records_in_interval],
AVG(aa.[value]) AS [average_value]
FROM [friib].[dbo].[archive_analog] AS aa
-- WHERE aa.[date] > '1900-01-01'
GROUP BY DATEDIFF(MINUTE, '2000', aa.[date]) / 10
-- HAVING SUM(aa.[value]) > 1000
ORDER BY [date_truncated]
Details and commentary
The MINUTE and 10 terms can be changed to any DATEPART and integer,1 respectively, to group into different time intervals.
e.g. 10 with MINUTE is ten minute intervals; 6 with HOUR is
six hour intervals.
If you change the interval a lot, you might benefit from declaring it as a variable.
DECLARE #interval int = 10;
SELECT DATEADD(MINUTE, DATEDIFF(…) / #interval * #interval, '2000')
…
GROUP BY DATEDIFF(…) / #interval
Wrapping it with a DATEADD invocation with a multiplier will give you a DATETIME value, which means:
Data sources over long time intervals are fine. Some other answers have collision between years.
Including it in the SELECT statement will give your output a single column with the truncated timestamp.
In the SELECT, the division (/) operation after DATEDIFF truncates values to integers (a FLOOR shortcut), which yields the beginning of time intervals for each row.
If you want to label each row with the middle or end of its interval, you can tweak the division in the second term of DATEADD with the bold part below:
End of interval: …) / 10 * 10 + 10 , '2000'), credit to Daniel Elkington.
Middle of interval: …) / 10 * 10 + (10 / 2.0) , '2000').
Trivia
'2000' is an "anchor date" around which SQL will perform the date math. Most sample code uses 0 for the anchor, but JereonH discovered that you encounter an integer overflow when grouping more-recent dates by seconds or milliseconds.2
If your data spans centuries,3 using a single anchor date in the GROUP BY for seconds or milliseconds will still encounter the overflow. For those queries, you can ask each row to anchor the binning comparison to its own date's midnight:
Use DATEADD(DAY, DATEDIFF(DAY, 0, aa.[date]), 0) instead of '2000' wherever it appears above. Your query will be totally unreadable, but it will work.
An alternative might be CONVERT(DATETIME, CONVERT(DATE, aa.[date])) as the replacement.
1 If you want all :00 timestamps to be eligible for binning, use an integer that your DATEPART's maximum can evenly divide into.4 As a counterexample, grouping results into 13-minute or 37-hour bins will skip some :00s, but it should still work fine.
2 The math says 232 ≈ 4.29E+9. This means for a DATEPART of SECOND, you get 4.3 billion seconds on either side, which works out to "anchor date ± 136 years." Similarly, 232 milliseconds is ≈ 49.7 days.
3 If your data actually spans centuries or millenia and is still accurate to the second or millisecond… congratulations! Whatever you're doing, keep doing it.
4 If you ever wondered why our clocks have a 12 at the top, reflect on how 5 is the only integer from 6 (half of 12) or below that is not a factor of 12. Then note that 5 × 12 = 60. You have lots of choices for bin sizes with hours, minutes, and seconds.
In T-SQL you can:
SELECT [Date]
FROM [FRIIB].[dbo].[ArchiveAnalog]
GROUP BY [Date], DATEPART(hh, [Date])
or
by minute use DATEPART(mi, [Date])
or
by 10 minutes use DATEPART(mi, [Date]) / 10 (like Timothy suggested)
For a 10 minute interval, you would
GROUP BY (DATEPART(MINUTE, [Date]) / 10)
As was already mentioned by tzup and Pieter888... to do an hour interval, just
GROUP BY DATEPART(HOUR, [Date])
Should be something like
select timeslot, count(*)
from
(
select datepart('hh', date) timeslot
FROM [FRIIB].[dbo].[ArchiveAnalog]
)
group by timeslot
(Not 100% sure about the syntax - I'm more an Oracle kind of guy)
In Oracle:
SELECT timeslot, COUNT(*)
FROM
(
SELECT to_char(l_time, 'YYYY-MM-DD hh24') timeslot
FROM
(
SELECT l_time FROM mytab
)
) GROUP BY timeslot
The original answer the author gave works pretty well. Just to extend this idea, you can do something like
group by datediff(minute, 0, [Date])/10
which will allow you to group by a longer period then 60 minutes, say 720, which is half a day etc.
For MySql:
GROUP BY
DATE(`your_date_field`),
HOUR(`your_date_field`),
FLOOR( MINUTE(`your_date_field`) / 10);
If you want to actually display the date, have a variable grouping, and be able to specify larger time frames than 60 minutes:
DECLARE #minutes int
SET #minutes = 90
SELECT
DATEADD(MINUTE, DATEDIFF(MINUTE, 0, [Date]) / #minutes * #minutes, 0) as [Date],
AVG([Value]) as [Value]
FROM [FRIIB].[dbo].[ArchiveAnalog]
GROUP BY
DATEDIFF(MINUTE, 0, [Date]) / #minutes
declare #interval tinyint
set #interval = 30
select dateadd(minute,(datediff(minute,0,[DateInsert])/#interval)*#interval,0), sum(Value_Transaction)
from Transactions
group by dateadd(minute,(datediff(minute,0,[DateInsert])/#interval)*#interval,0)
In SQLite, in order to group by hour, you can do:
GROUP BY strftime('%H', [FRIIB].[dbo].[ArchiveAnalog].[Date]);
and to group by each 10 minutes:
GROUP BY strftime('%M', [FRIIB].[dbo].[ArchiveAnalog].[Date]) / 10;
My solution is to use a function to create a table with the date intervals and then join this table to the data I want to group using the date interval in the table.
The date interval can then be easily selected when presenting the data.
CREATE FUNCTION [dbo].[fn_MinuteIntervals]
(
#startDate SMALLDATETIME ,
#endDate SMALLDATETIME ,
#interval INT = 1
)
RETURNS #returnDates TABLE
(
[date] SMALLDATETIME PRIMARY KEY NOT NULL
)
AS
BEGIN
DECLARE #counter SMALLDATETIME
SET #counter = #startDate
WHILE #counter <= #endDate
BEGIN
INSERT INTO #returnDates VALUES ( #counter )
SET #counter = DATEADD(n, #interval, #counter)
END
RETURN
END
For SQL Server 2012, though I believe it would work in SQL Server 2008R2, I use the following approach to get time slicing down to the millisecond:
DATEADD(MILLISECOND, -DATEDIFF(MILLISECOND, CAST(time AS DATE), time) % #msPerSlice, time)
This works by:
Getting the number of milliseconds between a fixed point and target time:#ms = DATEDIFF(MILLISECOND, CAST(time AS DATE), time)
Taking the remainder of dividing those milliseconds into time slices:#rms = #ms % #msPerSlice
Adding the negative of that remainder to the target time to get the slice time:DATEADD(MILLISECOND, -#rms, time)
Unfortunately, as is this overflows with microseconds and smaller units, so larger, finer data sets would need to use a less convenient fixed point.
I have not rigorously benchmarked this and I am not in big data, so your mileage may vary, but performance was not noticeably worse than the other methods tried on our equipment and data sets, and the payout in developer convenience for arbitrary slicing makes it worthwhile for us.
select dateadd(minute, datediff(minute, 0, Date), 0),
sum(SnapShotValue)
FROM [FRIIB].[dbo].[ArchiveAnalog]
group by dateadd(minute, datediff(minute, 0, Date), 0)
select from_unixtime( 600 * ( unix_timestamp( [Date] ) % 600 ) ) AS RecT, avg(Value)
from [FRIIB].[dbo].[ArchiveAnalog]
group by RecT
order by RecT;
replace the two 600 by any number of seconds you want to group.
If you need this often and the table doesn't change, as the name Archive suggests, it would probably be a bit faster to convert and store the date (& time) as a unixtime in the table.
I know I am late to the show with this one, but I used this - pretty simple approach. This allows you to get the 60 minute slices without any rounding issues.
Select
CONCAT(
Format(endtime,'yyyy-MM-dd_HH:'),
LEFT(Format(endtime,'mm'),1),
'0'
) as [Time-Slice]
Try this query. It makes one column. (references #nobilist answer)
GROUP BY CAST(DATE(`your_date_field`) as varchar) || ' ' || CAST(HOUR(`your_date_field`) as varchar) || ':' || CAST(FLOOR(minute(`your_date_field`) / 10) AS varchar) || '0' AS date_format
Here is an option that provides a human readable start time of that interval (7:30, 7:40, etc).
In a temp table, it truncates seconds and milliseconds by using SMALLDATETIME, and then the main query subtracts any amount over the desired minute interval.
SELECT DATEADD(MINUTE, -(DATEDIFF(MINUTE, '2000', tmp.dt) % 10), tmp.dt)
FROM (
SELECT CAST(DateField AS SMALLDATETIME) AS dt
FROM MyDataTable
) tmp
It can also be done in a single line of code, but it is not as readable.
SELECT DATEADD(MINUTE, -(DATEDIFF(MINUTE, '2000', CAST(DateField AS SMALLDATETIME)) % 10), CAST(DateField AS SMALLDATETIME)) AS [interval] FROM MyDataTable
Related
I need to create a VIEW/Select statement that will take a start date, and create 3 different rows for each date. One row calculates 30 days, from the start date, another 60 days, and another 90 days. Also each row needs to have an identifier that states whether the date is 30 days, 60 days or 90 days from the start date. So say that the start date is 09/01/2020. Then the View will return this for each start date:
Row Header : Start Date, AdditionalDate, AdditionalDays
Row 1 : 01/01/2020, 02/01/2020, 30
Row 2 : 01/02/2020, 03/01/2020, 60
Row 3 : 01/01/2020, 04/01/2020, 90
Sorry, forgot to mention, but start date is from a table. Like (Select startDate from Appointment)
I am using Microsoft SQL Server and a new SQL user. Really appreciate any help and advice.
Thank you!
I am unsure why what do you expect from a view for that - views don't take parameters.
Here is, however, a query that, from a given date parameter, generates three rows, at 30, 60 and 90 days later:
declare #start_date date = '2020-01-01';
select
#start_date,
dateadd(day, additional_days, #start_date) additional_date,
additional_days
from (values (30), (60), (90)) x(additional_days)
I am unsure whether you really mean 30 days or a month. If you want months, then:
declare #start_date date = '2020-01-01';
select
#start_date,
dateadd(month, additional_months, #start_date) additional_date,
additional_months
from (values (1), (2), (3)) x(additional_months)
On the other hand, if you are starting from an existing table, then that's a cross join:
select
t.*,
dateadd(day, x.additional_days, t.start_date) additional_date,
x.additional_days
from mytable t
cross join (values (30), (60), (90)) x(additional_days
You cannot use a view for this purpose, but you can use an inline table-valued function:
create function dates (
#date date,
#period int,
#num int
)
returns table
as return
with dates as (
select #date as start_date,
dateadd(day, #period, #date) as additional_date,
#period as additional_days, 1 as n
union all
select start_date,
dateadd(day, #period, additional_date),
additional_days + #period, n + 1
from dates
where n < #num
)
select start_date, additional_date, additional_days
from dates;
Here is a db<>fiddle.
You can utilize a recursive cte:
with cte as
( Select 1 as Header
,Start
,dateadd(day, 30, Start) as AdditionalDate
,30 as AdditionalDays
from Appointment
union all
select Header+1
,Start
,dateadd(day, 30, AdditionalDate)
,AdditionalDays + 30
from cte
where Header <= 2
)
Select * from cte
Or for adding months instead of days:
with cte as
( Select 1 as Header
,Start
,dateadd(month, 1, Start) as AdditionalDate
,datediff(day, Start, dateadd(month, 1, Start)) as AdditionalDays
from Appointment
union all
select Header+1
,Start
,dateadd(month, 1, AdditionalDate)
,datediff(day, Start, dateadd(month, 1, AdditionalDate))
from cte
where Header <= 2
)
Select * from cte
See fiddle
I have two dates: CREATION_DATE and START_DATE. START_DATE will always be later than CREATION_DATE. I need to calculate the number of minutes between them, except for minutes which happen on a weekend.
Every solution I can find assumes one of those dates occurs on a weekend, but alas, if CREATION_DATE is on a Friday, and START_DATE is a Monday, all of Saturday and Sunday is counted.
I've even tried calculating minutes from CREATION_DATE to the next 12am occurs plus minutes from first 12am Monday to START_DATE, but that doesn't work either.
I have found a solution if I only wanted to count days. I need to know down to minutes.
Our DB is hosted an I am not able to create VB functions so my solution must be all SQL.
The basic idea is to generate a record for all minutes between the start and finish, including those on weekends. Then use the WHERE clause to filter out those you don't want. In many cases, this is done by joining to a Calendar table, so you can also look at holidays or other special events, but for this purpose we can just use the DATEPART() function.
One this is done, we use a GROUP BY to roll things back up to the original date values and the COUNT() function to know how much work we did.
This basic concept works whether you're counting days, minutes, months, whatever.
It's not clear in the question, but I'm gonna assume your start and end values are columns in a table, rather than variable names (no #).
WITH Numbers(Number) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
)
SELECT t.CREATION_DATE, t.START_DATE, COUNT(*) AS Num_Minutes
FROM [MyTable] t
INNER JOIN Numbers n on n.Number <= DATEDIFF(minute, t.CREATION_DATE, t.START_DATE)
WHERE DATEPART(dw, DATEADD(minute, n.Number, t.CREATION_DATE)) NOT IN (7,1)
GROUP BY t.CREATION_DATE, t.START_DATE
But this has the potential to be very slow, depending on how far apart the dates are. You can improve this by using various other ways to generate the Numbers table to get a starting point that better approximates the needs of your actual data.
If you aren't worried about accounting for holidays, you can do this as a simple math problem without having to monkey around with a tally table or doing any counting.
The following works by dropping the time portion off the begin and end date parameters and calculates the number of working days from that and multiples that figure by 3660. From there, if the begin date is a week day the begin date mins are subtracted... if end date is a weekday, those mins are added.
DECLARE
#BegDate DATETIME = '2018-09-13 03:30:30',
#EndDate DATETIME = '2018-09-18 03:35:27';
SELECT
working_mins = bm.base_mins
- ((1 - (x.is_beg_sat + x.is_beg_sun)) * x.beg_mins) -- if the begin date is a week day, subtract the mins from midnight.
+ ((1 - (x.is_end_sat + x.is_end_sun)) * x.end_mins) -- if the end date is a week day add the mins from midnight.
--,*
FROM
( VALUES (
DATEADD(DAY, DATEDIFF(DAY, 0, #BegDate), 0),
DATEADD(DAY, DATEDIFF(DAY, 0, #EndDate), 0)
) ) d (beg_date, end_date)
CROSS APPLY ( VALUES (
DATEDIFF(MINUTE, d.beg_date, #BegDate),
DATEDIFF(MINUTE, d.end_date, #EndDate),
DATEDIFF(DAY, d.beg_date, d.end_date),
DATEDIFF(WEEK, d.beg_date, d.end_date) * 2,
1 - SIGN(DATEPART(WEEKDAY, d.beg_date) % 7),
1 - SIGN(DATEPART(WEEKDAY, d.end_date) % 7),
1 - SIGN((DATEPART(WEEKDAY, d.beg_date) + 7) % 8),
1 - SIGN((DATEPART(WEEKDAY, d.end_date) + 7) % 8)
) ) x (beg_mins, end_mins, total_days, weekend_days, is_beg_sat, is_end_sat, is_beg_sun, is_end_sun)
CROSS APPLY ( VALUES (1440 * (x.total_days - x.weekend_days + x.is_beg_sat - x.is_end_sat)) ) bm (base_mins);
You can take a look at this solution, and see if meets your needs. Basically, I did the following:
Take the number of whole days betwen StartDate and EndDate that aren't weekend days, and multiply by 2:
SELECT COUNT(*) * 24 * 60 FROM WholeDaysBetween WHERE wkday <= 5
Take the minutes from StartDate (hours*60 + minutes)
(24 * 60) - (DATEPART(HOUR, #StartDate) * 60) - (DATEPART(MINUTE, #StartDate))
Take the minutes from EndDate (hours*60 + minutes)
(DATEPART(HOUR, #EndDate) * 60) + (DATEPART(MINUTE, #EndDate))
To get the number of whole days between, I used a recursive CTE:
WITH
WholeDaysBetween(dt, wkday) AS
(
SELECT DATEADD(DAY, 1, #StartDate), DATEPART(WEEKDAY, DATEADD(DAY, 1, #StartDate))
UNION ALL
SELECT DATEADD(DAY, 1, dt), DATEPART(WEEKDAY, DATEADD(DAY, 1, dt))
FROM WholeDaysBetween
WHERE dt < DATEADD(DAY, -1, #EndDate)
)
Of course, for this to work, you have to adjust your datefirst settings.
The final query is as follows (I used the same sample data as in your comment):
set datefirst 1; -- day starts on Monday
declare #StartDate datetime = '2018-09-21 23:59:00';
declare #EndDate datetime = '2018-09-24 00:01:00';
WITH
WholeDaysBetween(dt, wkday) AS
(
SELECT DATEADD(DAY, 1, #StartDate), DATEPART(WEEKDAY, DATEADD(DAY, 1, #StartDate))
UNION ALL
SELECT DATEADD(DAY, 1, dt), DATEPART(WEEKDAY, DATEADD(DAY, 1, dt))
FROM WholeDaysBetween
WHERE dt < DATEADD(DAY, -1, #EndDate)
)
SELECT
-- whole weekdays between #StartDate and #EndDate,
-- multiplied by minutes per day
(
SELECT COUNT(*) * 24 * 60
FROM WholeDaysBetween
WHERE wkday <= 5
)
+
-- minutes from #StartDate date to end of #StartDate
-- as long as #StartDate isn't on weekend
(
SELECT
CASE
WHEN DATEPART(WEEKDAY, #StartDate) <= 5
THEN
(24 * 60) -
(DATEPART(HOUR, #StartDate) * 60) -
(DATEPART(MINUTE, #StartDate))
ELSE 0
END
)
+
-- minutes from start of #EndDate's date to #EndDate
-- as long as #EndDate isn't on weekend
(
SELECT
CASE
WHEN DATEPART(WEEKDAY, #EndDate) <= 5
THEN
(DATEPART(HOUR, #EndDate) * 60) +
(DATEPART(MINUTE, #EndDate))
ELSE 0
END
)
I am using SQL 2008/2012.
Query to calculate Minute Difference between two dates.
select DATEDIFF(mi, '9999-08-03 04:20:00.000', '2005-05-22 03:45:09.530')
Error:
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.
Note : DateDiff_Big not support this version.
Is there any other way to get result. without using DateDiff_Big
So you use a smaller unit and do some arithmetic. But I presume you mean:
select datediff(minute, '2005-05-22 03:45:09.530', '9999-08-03 04:20:00.000')
Normally, one wants the difference to be positive (although that is not related to the answer).
select (convert(bigint, datediff(day, '2100-08-03 04:20:00', '9999-08-03 04:20:00.000')) * 60 * 24) +
datediff(minute, '2005-05-22 03:45:09.530', '2100-08-03 04:20:00')
Does this fit your needs
DECLARE #DT1 datetime = '9999-08-03 04:20:00.000'
DECLARE #DT2 datetime = '2005-05-22 03:45:09.530'
select --DATEDIFF_BIG(mi, #DT1, #DT2),
CONVERT(BIGINT, DATEDIFF(DAY, #DT1, #DT2)) * 24 * 60
+ CONVERT(BIGINT, DATEDIFF(mi, CONVERT(TIME(7), #DT1), CONVERT(TIME(7), #DT2)))
GO
try this type to get Minutes Different
SELECT CONVERT(BIGINT, DATEDIFF(HOUR, '2005-05-22 03:45:09.530', '9999-08-03 04:20:00.000')) * 60
Check This.
SELECT
CAST(DATEDIFF(hour, '9999-08-03 04:20:00.000', '2009-05-22 03:45:09.530') AS BIGINT)* 60+
DATEDIFF(mi, CONVERT(TIME, '9999-08-03 04:20:00.000'),
CONVERT(TIME, '2009-05-22 03:45:09.530'))+60;
DATEDIFF has a limit that depends on what time element is used.
Because it can only return a number that fits in an INT.
For a return value out of range for int (-2,147,483,648 to
+2,147,483,647), DATEDIFF returns an error. For millisecond, the maximum difference between startdate and enddate is 24 days, 20 hours,
31 minutes and 23.647 seconds. For second, the maximum difference is
68 years.
For minutes that appears to be 4083 years.
So then limit for minutes would be in the -4083 years to 4083 years range.
Then you could wrap the DATEDIFF in a CASE WHEN that checks if it's in that range.
And let it default to the DATEDIFF in hours * 60.
It'll loose some minutes for the default, but that might still be better than returning a NULL.
SELECT dt1, dt2,
DATEDIFF(year, dt1, dt2) AS diff_years,
DATEDIFF(hour, dt1, dt2) AS diff_hours,
CONVERT(BIGINT,
CASE
WHEN DATEDIFF(year, dt1, dt2) BETWEEN -4083 AND 4083
THEN DATEDIFF(minute, dt1, dt2)
ELSE CONVERT(BIGINT,DATEDIFF(hour, dt1, dt2)) * 60
END) AS diff_minutes
FROM (VALUES
('9999-08-03 04:20:00.000', '2005-05-22 03:45:09.530'),
('6083-01-01 00:00:00.000', '2000-01-01 00:00:00.000'),
('2000-01-01 00:00:00.000','6083-01-01 00:00:00.000'),
('0001-01-01 00:00:00.000','9999-12-31 00:00:00.000')
) q(dt1, dt2)
I have below View Query. While calculating time interval it only display time interval if check Out is for same date. For example if i check in on 11/12/2017 then it i have to check out with same date 11/12/201 n order to calculate time Interval.
But i want to modify it like if i checkout on next day (after 12:00 AM midnight) it should also calculate the time interval.can some help me to modify query to get the desired results?
Query:
ALTER VIEW [dbo].[TimeAttendanceQuery]
AS
SELECT TOP (100) PERCENT
dbo.AxPerson.Name,
dbo.AxPerson.IdNumber AS EmployeeID,
dbo.TimeAttendance.Badge,
dbo.AxPerson.Id,
MIN(dbo.TimeAttendance.EventTime) AS EntryTime,
MAX(dbo.TimeAttendance.EventTime) AS ExitTime,
CAST(DATEDIFF(second, MIN(dbo.TimeAttendance.EventTime), MAX(dbo.TimeAttendance.EventTime)) / 60 / 60 / 24 AS NVARCHAR(50)) +
':' + CAST(DATEDIFF(second, MIN(dbo.TimeAttendance.EventTime), MAX(dbo.TimeAttendance.EventTime)) / 60 / 60 % 24 AS NVARCHAR(50)) + ':' + CAST(DATEDIFF(second, MIN(dbo.TimeAttendance.EventTime), MAX(dbo.TimeAttendance.EventTime)) / 60 % 60 AS NVARCHAR(50))
AS TimeInterval,
dbo.TimeAttendance.Event,
dbo.AxPerson.Type AS ShitType,
dbo.AxPerson.ShiftDesc,
CONVERT(Varchar,dbo.TimeAttendance.EventTime, 101) AS EventTIME
FROM
dbo.AxPerson
INNER JOIN dbo.TimeAttendance ON dbo.AxPerson.Name = dbo.TimeAttendance.Name
GROUP BY dbo.AxPerson.Name, dbo.AxPerson.IdNumber, dbo.TimeAttendance.Badge, CONVERT(Varchar, dbo.TimeAttendance.EventTime, 101), dbo. AxPerson.ShiftDesc, dbo.AxPerson.Id, dbo.TimeAttendance.Event,dbo.AxPerson.Type
ORDER BY dbo.AxPerson.Name, EventTime DESC
GO
I'm not sure about the algorithm you posted, but if you want to get the time difference from two datetimes you can cast the subtraction as time. This works for less than 24 hours. If you also want the number of days (I think this is only good for less than a year), then you can do the datepart-day of the difference.
For example:
DECLARE #starttime datetime = '2017-11-12 010:20:00'
DECLARE #endtime datetime = '2017-11-13 08:00:00'
SELECT DATEPART(DAY, #endtime - #starttime) - 1 [Days Passed]
,CAST(#endtime - #starttime as time(0)) [Time Passed]
--WHERE the (0) in time(0) is for the milliseconds to return.
Gives output:
Days Passed Time Passed
0 21:40:00
If you don't care about the days, then your code could be modified like this:
ALTER VIEW [dbo].[TimeAttendanceQuery]
AS
SELECT Name, EmployeeID, Badge, Id, EntryTime,
CAST(ExitTime - EntryTime as time(0)) [TimeInterval],
Event, ShiftType, ShiftDesc
,CONVERT(Varchar, EventTime, 101) AS EventTIME
FROM (
SELECT
dbo.AxPerson.Name,
dbo.AxPerson.IdNumber AS EmployeeID,
dbo.TimeAttendance.Badge,
dbo.AxPerson.Id,
MIN(dbo.TimeAttendance.EventTime) AS EntryTime,
MAX(dbo.TimeAttendance.EventTime) AS ExitTime,
dbo.TimeAttendance.Event,
dbo.AxPerson.Type AS ShiftType,
dbo.AxPerson.ShiftDesc,
dbo.TimeAttendance.EventTime
FROM dbo.AxPerson INNER JOIN dbo.TimeAttendance
ON dbo.AxPerson.Name = dbo.TimeAttendance.Name
GROUP BY
dbo.AxPerson.Name,
dbo.AxPerson.IdNumber,
dbo.TimeAttendance.Badge,
dbo.AxPerson.Id,
dbo.TimeAttendance.Event,
dbo.AxPerson.Type
dbo.AxPerson.ShiftDesc,
dbo.TimeAttendance.EventTime,
) AS dT
Given a specified time value and an interval value:
Specified Time: 13:25:00
Interval Value: 00:20:00
How can I filter the following table of values to return times that are the specified Interval either side of the Specified Time.
12:45:24
13:05:00
13:50:30
14:50:32
15:15:10
I want a function or query to check if '13:25:00' has '00:20:00' difference with any of the times in table.
The output should return:
13:05:00
Based on the information you have provided, I assume you want to get values from the list that are the specified period either side of your "special time".
Here's one way to do it using DATEADD:
-- temp table for your sample data
CREATE TABLE #times ( val TIME )
INSERT INTO #times
( val )
VALUES ( '12:45:24' ),
( '13:05:00' ),
( '13:50:30' ),
( '14:50:32' ),
( '15:15:10' )
DECLARE #special_time TIME = '13:25:00'
DECLARE #diff_value TIME = '00:20:00'
-- variable will hold the total number of seconds for your interval
DECLARE #diff_in_seconds INT
-- gets the total number of seconds of your interval -> #diff_value
SELECT #diff_in_seconds = DATEPART(SECOND, #diff_value) + 60
* DATEPART(MINUTE, #diff_value) + 3600 * DATEPART(HOUR, #diff_value)
-- get the values that match the criteria
SELECT *
FROM #times
WHERE val = DATEADD(SECOND, #diff_in_seconds, #special_time)
OR val = DATEADD(SECOND, -( #diff_in_seconds ), #special_time)
DROP TABLE #times
Note that the WHERE clause filters the results by adding and subtracting the difference. The subtraction is achieved by making the #diff_in_seconds negative.
If we are understanding your question correctly, you want all the times that are bigger than 20 minutes from your given (special) time.
To achieve this, just do a select with a where clause that contains a clause looking like this: abs(datediff(minute, tableDate, #specialdate)) > 20
SQLFiddle sample and code example:
declare #specialDate datetime = '1900-01-01 13:25:00'
select *
from SampleData
where abs(datediff(minute, SomeDate, #specialDate)) > 20
Note that I set the dates of the Datetime columns to 1900-01-01 as an obscure reference, adjust according to your settings.
You will need the ABS in the line to make sure that both variants of the resulting datediff are checked (It can either bring back 0, > 0 or < 0)
References:
MSDN: DATEDIFF
MSDN: ABS
Here is a solution:
create table t(t time);
insert into t
values
('12:45:24'),
('13:05:00'),
('13:50:30'),
('14:50:32'),
('15:15:10')
declare #st time = '13:25:00'
declare #dt time = '00:20:00'
select * from t
where abs(datediff(ss, t, #st)) - datediff(ss, '00:00:00', #dt) = 0
abs(datediff(ss, t, #st) will hold difference in seconds between times in table and special time. You compare this difference to difference between 00:00:00 and interval datediff(ss, '00:00:00', #dt)
Output:
t
13:05:00.0000000
Fiddle http://sqlfiddle.com/#!6/05df4/1