difference in time is giving incorrect results - sql

I have a table as test
shiftend | out |
---------------------------------------------
15:00:00.0000000 | 2016-07-22 14:42:00 |
16:00:00.0000000 | 2016-07-22 16:06:00 |
Shiftend is having a datatype as time
out is having a datatype as smalldatetime
I am expecting the output as
shiftend | out | Output
-----------------------------------------------------------------------------
15:00:00.0000000 | 2016-07-22 14:42:00 | -00:18:00
16:00:00.0000000 | 2016-07-22 16:06:00 | 00:06:00
I am trying this query:
select shiftend,out,CAST((out-Shiftend) as time(0)) as Output from test
where
CAST(CONVERT(NVARCHAR(10), out, 101) AS SMALLDATETIME) = CAST(CONVERT(NVARCHAR(10),'2016-07-22', 101) AS SMALLDATETIME)
But i am getting the output as
shiftend | out | Output
-----------------------------------------------------------------------------
15:00:00.0000000 | 2016-07-22 14:42:00 | 23:42:00
16:00:00.0000000 | 2016-07-22 16:06:00 | 00:06:00
23:42:00 is incorrect. How to calcualte the time.

Try the following query:
select
case when (cast(out as time) < shiftend) then '-' else '' end +
convert(varchar(8),
dateadd(minute,
abs(
DATEDIFF(minute,
cast(out as time)
, shiftend)
)
,0)
,108) as Output
Explanation:
You're getting the difference between the two dates with DATEDIFF(minute, cast(out as time), shiftend).
You need just the time component to avoid going to the previous day, so you use cast(out as time). shiftend as you mentioned is already of datatype time
abs returns the absolute value, so -18 becomes 18.
Then generate a date by adding the above value as minutes to 00:00:00 using dateadd(minute, [above value], 0)
The final convert(varchar(8),____,108) is since you required the output as a time.
iif(cast(out as time) < shiftend,'-','') adds negative sign or not to the beginning of the word.
Unfortunately, you can't have negative values in the time datatype. It's turning -18 into 00:00 - 18 which is 23:42. You could:
Use datediff and save the difference as the number of seconds for example.
Save only the modular difference and have a separate column for deciding whether it's a positive or negative difference.
Write your own SQL function for computing this as a varchar as above

Related

SUM of production counts for "overnight work shift" in MS SQL (2019)

I need some help regarding sum of production count for overnight shifts.
The table just contains a timestamp (that is automaticaly generated by SQL server during INSERT), the number of OK produced pieces and the number of NOT OK produced pieces in that given timestamp.
CREATE TABLE [machine1](
[timestamp] [datetime] NOT NULL,
[OK] [int] NOT NULL,
[NOK] [int] NOT NULL
)
ALTER TABLE [machine1] ADD DEFAULT (getdate()) FOR [timestamp]
The table holds values like these (just an example, there are hundreds of lines each day and the time stamps are not fixed like each hour or each 30mins):
timestamp
OK
NOK
2022-08-01 05:30:00.000
15
1
2022-08-01 06:30:00.000
18
3
...
...
...
2022-08-01 21:30:00.000
10
12
2022-08-01 22:30:00.000
0
3
...
...
...
2022-08-01 23:59:00.000
1
2
2022-08-02 00:01:00.000
7
0
...
...
...
2022-08-02 05:30:00.000
12
4
2022-08-02 06:30:00.000
9
3
The production works in shifts like so:
morning shift: 6:00 -> 14:00
afternoon shift: 14:00 -> 22:00
night shift: 22:00 -> 6:00 the next day
I have managed to get sums for the morning and afternoon shifts without issues but I can't figure out how to do the sum for the night shift (I have these SELECTs for each shift stored as a VIEW for easy access).
For the morning shift:
SELECT CAST(timestamp AS date) AS Morning,
SUM(OK) AS SUM_OK,
SUM(NOK) AS SUM_NOK
FROM [machine1]
WHERE DATEPART(hh,timestamp) >= 6 AND DATEPART(hh,timestamp) < 14
GROUP BY CAST(timestamp AS date)
ORDER BY Morning ASC
For the afternoon shift:
SELECT CAST(timestamp AS date) AS Afternoon,
SUM(OK) AS SUM_OK,
SUM(NOK) AS SUM_NOK
FROM [machine1]
WHERE DATEPART(hh,timestamp) >= 14 AND DATEPART(hh,timestamp) < 22
GROUP BY CAST(timestamp AS date)
ORDER BY Afternoon ASC
Since we identify the date of each shift by its start, my idea would be that the result for such SUM of night shift would be
Night
SUM_OK
SUM_NOK
2022-08-01
xxx
xxx
for interval 2022-08-01 22:00:00.000 -> 2022-08-02 05:59:59.999
2022-08-02
xxx
xxx
for interval 2022-08-02 22:00:00.000 -> 2022-08-03 05:59:59.999
2022-08-03
xxx
xxx
for interval 2022-08-03 22:00:00.000 -> 2022-08-04 05:59:59.999
2022-08-04
xxx
xxx
for interval 2022-08-04 22:00:00.000 -> 2022-08-05 05:59:59.999
...
...
...
After few days of trial and error I have probably managed to find the needed solution. Using a subquery I shift all the times in range 00:00:00 -> 05:59:59 to the previous day and then I use that result in same approach as for morning and afternon shift (because now all the production data from night shift are in the same date between 22:00:00 and 23:59:59).
In case anyone needs it in future:
SELECT
CAST(nightShift.shiftedTime AS date) AS Night,
SUM(nightShift.OK) AS SUM_OK,
SUM(nightShift.NOK) AS SUM_NOK
FROM
(SELECT
CASE WHEN (DATEPART(hh, timestamp) < 6 AND DATEPART(hh, timestamp) >= 4) THEN DATEADD(HOUR, -6, timestamp)
WHEN (DATEPART(hh, timestamp) < 4 AND DATEPART(hh, timestamp) >= 2) THEN DATEADD(HOUR, -4, timestamp)
WHEN (DATEPART(hh, timestamp) < 2 AND DATEPART(hh, timestamp) >= 0) THEN DATEADD(HOUR, -2, timestamp)
END AS shiftedTime,
[OK],
[NOK]
FROM [machine1]
WHERE (DATEPART(hh, cas) >= 0 AND DATEPART(hh, cas) < 6)) nightShift
WHERE DATEPART(hh,nightShift.shiftedTime) >= 22
GROUP BY CAST(nightShift.shiftedTime AS date)
ORDER BY Night ASC
PS: If there is anything wrong with this approach, please feel free to correct me as I'm just newbie in SQL. So far this seems to do exactly what I needed.

How to break datetime in 12 hour chunks and use it for aggregation in Presto SQL?

I have been trying to break the datetime in 12 hour chunk in Presto SQL but was unsuccessful.
Raw data table:
datetime
Login
2022-05-08 07:10:00.000
1234
2022-05-09 23:20:00.000
5678
2022-05-09 06:20:00.000
5674
2022-05-08 09:20:00.000
8971
The output table should look like below. I have to get count of login in 12 hour chunks. So, first should be from 00:00:00.000 to 11:59:00:000 and the next chunk from 12:00:00.000 to 23:59:00:000
Output:
datetime
count
2022-05-08 00:00:00.000
2
2022-05-08 12:00:00.000
0
2022-05-09 00:00:00.000
1
2022-05-09 12:20:00.000
1
This should work:
Extract the hour from the timestamp, then integer divide it by 12. That will make it 0 till 11:59, and 1 till 23:59. Then, multiply that back by 12.
Use that resulting integer to DATE_ADD() it with unit 'HOUR' to the timestamp of the row truncated to the day.
SELECT
DATE_ADD('HOUR',(HOUR(ts) / 12) * 12, TRUNC(ts,'DAY')) AS halfday
, SUM(login) AS count_login
FROM indata
GROUP BY
halfday
;
-- out halfday | count_login
-- out ---------------------+-------------
-- out 2022-05-08 00:00:00 | 15879
-- out 2022-05-08 12:00:00 | 5678
This query worked for me.
SELECT
DATE_ADD('HOUR',(HOUR(ts) / 12) * 12, date_trunc('DAY',ts)) AS halfday
, SUM(login) AS count_login
FROM indata
GROUP BY
halfday
;

Create table with 15 minutes interval on date time in Snowflake

I am trying to create a table in Snowflake with 15 mins interval. I have tried with generator, but that's not give in the 15 minutes interval. Are there any function which I can use to generate and build this table for couple of years worth data.
Such as
Date
Hour
202-03-29
02:00 AM
202-03-29
02:15 AM
202-03-29
02:30 AM
202-03-29
02:45 AM
202-03-29
03:00 AM
202-03-29
03:15 AM
.........
........
.........
........
Thanks
Use following as time generator with 15min interval and then use other date time functions as needed to extract date part or time part in separate columns.
with CTE as
(select timestampadd(min,seq4()*15 ,date_trunc(hour, current_timestamp())) as time_count
from table(generator(rowcount=>4*24)))
select time_count from cte;
+-------------------------------+
| TIME_COUNT |
|-------------------------------|
| 2022-03-29 14:00:00.000 -0700 |
| 2022-03-29 14:15:00.000 -0700 |
| 2022-03-29 14:30:00.000 -0700 |
| 2022-03-29 14:45:00.000 -0700 |
| 2022-03-29 15:00:00.000 -0700 |
| 2022-03-29 15:15:00.000 -0700 |
.
.
.
....truncated output
| 2022-03-30 13:15:00.000 -0700 |
| 2022-03-30 13:30:00.000 -0700 |
| 2022-03-30 13:45:00.000 -0700 |
+-------------------------------+
There are many answers to this question h e r e already (those 4 are all this month).
But major point to note is you MUST NOT use SEQx() as the number generator (you can use it in the ORDER BY, but that is not needed). As noted in the doc's
Important
This function uses sequences to produce a unique set of increasing integers, but does not necessarily produce a gap-free sequence. When operating on a large quantity of data, gaps can appear in a sequence. If a fully ordered, gap-free sequence is required, consider using the ROW_NUMBER window function.
CREATE TABLE table_of_2_years_date_times AS
SELECT
date_time::date as date,
date_time::time as time
FROM (
SELECT
row_number() over (order by null)-1 as rn
,dateadd('minute', 15 * rn, '2022-03-01'::date) as date_time
from table(generator(rowcount=>4*24*365*2))
)
ORDER BY rn;
then selecting the top/bottom:
(SELECT * FROM table_of_2_years_date_times ORDER BY date,time LIMIT 5)
UNION ALL
(SELECT * FROM table_of_2_years_date_times ORDER BY date desc,time desc LIMIT 5)
ORDER BY 1,2;
DATE
TIME
2022-03-01
00:00:00
2022-03-01
00:15:00
2022-03-01
00:30:00
2022-03-01
00:45:00
2022-03-01
01:00:00
2024-02-28
22:45:00
2024-02-28
23:00:00
2024-02-28
23:15:00
2024-02-28
23:30:00
2024-02-28
23:45:00

T-SQL: Rounding Time in to 15 minutes but only after 5 minutes past the 15min

I have a table with a datetime field. I'm trying to round up/down the time in 15 minute intervals. But with non standard mathematical rounding rules where rounding up happens if its greater than 5 minutes past the 15 minute interval.
For example
IF 06:05 round down to 06:00
IF 06:06 round up to 06:15
IF 06:20 round down to 06:15
IF 06:21 round up to 06:30
and so on..
I've managed to find here T-SQL: Round to nearest 15 minute interval to round the nearest 15 minutes but this uses mathematical rounding meaning 06:07 would still round down to 06:00 instead of rounding up to 06:15.
Below code where i've got to:
cast(datepart(hour, getdate()) + datepart(minute, getdate()) / 60.00 as decimal(5, 2))
Just use a couple of date tricks.
This code will give you the top of the hour for the time you're evaluating (minutes effectively removed by adding up the hours since the 0 date in SQL):
select dateadd(hour, datediff(hour, 0, getdate()), 0)
From there, you need a CASE expression to evaluate which quartile of the hour the time in question falls into (just a snippet here):
case
when datepart(minute, dtm) > 50 then 60
when datepart(minute, dtm) > 35 then 45
when datepart(minute, dtm) > 20 then 30
when datepart(minute, dtm) > 5 then 15
else 0
end
Put those two pieces together with a DATEADD to decide how many minutes we're adding to that even hour mark:
declare #dtms table (dtm datetime);
insert #dtms (dtm)
values ('2019-07-16T12:05:00'),
('2019-07-16T12:06:00'),
('2019-07-16T12:21:00'),
('2019-07-16T12:29:00'),
('2019-07-16T12:35:00'),
('2019-07-16T12:38:00'),
('2019-07-16T12:56:00')
select
dtm,
dateadd(minute,
case
when datepart(minute, dtm) > 50 then 60
when datepart(minute, dtm) > 35 then 45
when datepart(minute, dtm) > 20 then 30
when datepart(minute, dtm) > 5 then 15
else 0
end, dateadd(hour, datediff(hour, 0, dtm), 0)) as rounded
from #dtms;
Results:
+-------------------------+-------------------------+
| dtm | rounded |
+-------------------------+-------------------------+
| 2019-07-16 12:05:00.000 | 2019-07-16 12:00:00.000 |
| 2019-07-16 12:06:00.000 | 2019-07-16 12:15:00.000 |
| 2019-07-16 12:21:00.000 | 2019-07-16 12:30:00.000 |
| 2019-07-16 12:29:00.000 | 2019-07-16 12:30:00.000 |
| 2019-07-16 12:35:00.000 | 2019-07-16 12:30:00.000 |
| 2019-07-16 12:38:00.000 | 2019-07-16 12:45:00.000 |
| 2019-07-16 12:56:00.000 | 2019-07-16 13:00:00.000 |
+-------------------------+-------------------------+

Get hourly data based on StartDate

For Example,
I had data something like this :-
batch MIN MAX TIME
X 10 20 2018-07-12 10:29:00.000
X 30 50 2018-07-12 10:30:00.000
X 50 30 2018-07-12 10:31:00.000
| | | |
X 40 20 2018-07-12 11:45:00.000
Now I want hourly data based on start time, For example :-
DURATION MIN
2018-07-12 10:29:00.000-2018-07-12 11:29:00.000 10
2018-07-12 11:30:00.000-2018-07-12 12:30:00.000 10
How can I get this?(Get Min Value For every hour based on Start Time)
dateadd function allows you to add or subtract days,hours, minutes to a date.
Consider The below query
select dateadd(HOUR, -1, getdate()) as time_added,
getdate() as curr_date
The -1 is for subtracting one hour (adding negative one hour)
The result of above query is :
timeadded curr_date
2018-07-12 13:25:31.603 2018-07-12 14:25:31.603
Instead of getdate() use your startdate
In your case it would be
select min from table where time<#starttime and time> dateadd(HOUR, -1, #starttime)