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

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.

Related

(bigquery) how number of hours event is happening within multiple dates

So my data looks like this:
DATE TEMPERATURE
2012-01-13 23:15:00 UTC 0
2012-01-14 01:35:00 UTC 5
2012-01-14 02:15:00 UTC 6
2012-01-14 03:15:00 UTC 8
2012-01-14 04:15:00 UTC 0
2012-01-14 04:55:00 UTC 0
2012-01-14 05:15:00 UTC -2
2012-01-14 05:35:00 UTC 0
I am trying to calculate the amount of time a zip code temperature will drop to 0 or below on any given day. On the 13th, it only happens for a very short amount of time so we don't really care. I want to know how to calculate the number of minutes this happens on the 14th, since it looks like a significantly (and consistently) cold day.
I want the query to add two more columns.
The first column added would be the time difference between the rows on a given date. So row 3- row 2=40 mins and row 4-row3=60 mins.
The second column would total the amount of minutes for a whole day the minutes the temperature has dropped to 0 or below. Here row 2-4 would be ignored. From row 5-8, total time that the temperature was 0 or below would be about 90 mins
It should end up looking like this:
DATE TEMPERATURE MINUTES_DIFFERENCE TOTAL_MINUTES
2012-01-13 23:15:00 UTC 0 0 0
2012-01-14 01:35:00 UTC 5 140 0
2012-01-14 02:15:00 UTC 6 40 0
2012-01-14 03:15:00 UTC 8 60 0
2012-01-14 04:15:00 UTC 0 60 60
2012-01-14 04:55:00 UTC 0 30 90
2012-01-14 05:15:00 UTC-2 20 110
2012-01-14 05:35:00 UTC 0 20 130
Use below
select *,
sum(minutes_difference) over(order by date) total_minutes
from (
select *,
ifnull(timestamp_diff(timestamp(date), lag(timestamp(date)) over(order by date), minute), 0) as minutes_difference
from your_table
)
if applied to sample data in your question - output is
Update to answer updated question
select * except(new_grp, grp),
sum(if(temperature > 0, 0, minutes_difference)) over(partition by grp order by date) total_minutes
from (
select *, countif(new_grp) over(order by date) as grp
from (
select *,
ifnull(timestamp_diff(timestamp(date), lag(timestamp(date)) over(order by date), minute), 0) as minutes_difference,
ifnull(((temperature <= 0) and (lag(temperature) over(order by date) > 0)) or
((temperature > 0) and (lag(temperature) over(order by date) <= 0)), true) as new_grp
from your_table
)
)
with output

Take value of some date that fall in specific hours in weekday but take different date in specific hours in weekend

I want to take the value that falls between the current day (today) and yesterday but only when after 9 am yesterday and before 9 am today. The current day must be only on Tuesday - Friday. But, if the current day is Monday, it will take value from Friday after 9 am to Monday before 9 am.
Samples
+---------+------------------------------+
| ID | registration_started_at |
+---------+------------------------------+
| 1 | 2021-05-13 07:00:00.000 |
| 2 | 2021-05-13 11:00:00.000 |
| 3 | 2021-05-14 08:00:00.000 |
| 4 | 2021-05-14 10:00:00.000 |
| 5 | 2021-05-15 12:00:00.000 |
| 6 | 2021-05-16 13:00:00.000 |
| 7 | 2021-05-17 08:00:00.000 |
| 8 | 2021-05-17 10:00:00.000 |
+---------+------------------------------+
So let say when the current_day (today) is Friday (14 May 2021),
When I run the query it must return
Desired Result 1
+---------+------------------------------+
| ID | registration_started_at |
+---------+------------------------------+
| 2 | 2021-05-13 11:00:00.000 |
| 3 | 2021-05-14 08:00:00.000 |
+---------+------------------------------+
But when current_day (today) is Monday (17 May 2021) it should return
Desired Result 2
+---------+------------------------------+
| ID | registration_started_at |
+---------+------------------------------+
| 4 | 2021-05-14 10:00:00.000 |
| 5 | 2021-05-15 12:00:00.000 |
| 6 | 2021-05-16 13:00:00.000 |
| 7 | 2021-05-17 08:00:00.000 |
+---------+------------------------------+
I only manage to get the desired result 1 with this query and I think this still not correct tho for desired result 2
SELECT ID,
DATETIME(registration_started_at, 'Asia/Jakarta') as registration_started_at
FROM `table`
WHERE
DATETIME_DIFF(CURRENT_DATETIME('Asia/Jakarta'), DATETIME(registration_started_at, 'Asia/Jakarta'), week) = 0
AND DATE(DATETIME_ADD(DATETIME(registration_started_at, 'Asia/Jakarta'), INTERVAL -9 HOUR)) = CURRENT_DATE('Asia/Jakarta') - 1
---------edit 1
Using the Mr. Caius Jard answer
WHERE
(DATETIME(registration_started_at, 'Asia/Jakarta') BETWEEN (
(CASE
WHEN EXTRACT(
DAYOFWEEK
FROM CURRENT_DATE('Asia/Jakarta')
) = 2 -- if Monday
THEN DATETIME_ADD(
CURRENT_DATETIME('Asia/Jakarta'),
INTERVAL -63 HOUR
) -- then 63 hours back from midnight today
ELSE DATETIME_ADD(
CURRENT_DATETIME('Asia/Jakarta'),
INTERVAL -15 HOUR
)
END)
) -- else 15 hours back from midnight today
AND DATETIME_ADD(
CURRENT_DATETIME('Asia/Jakarta'),
INTERVAL 9 HOUR
)) -- 9am today
It returns the 63 hours before today's time for Monday or 15 hours before today's time if not Monday, which is incorrect because if I run the query on 15.00 it only returns value from 00.00 today
I think this captures the logic you want:
WHERE DATETIME(registration_started_at, 'Asia/Jakarta') < DATETIME_ADD(DATETIME(DATE(CURRENT_DATETIME('Asia/Jakarta'))), INTERVAL 9 HOUR) AND
(EXTRACT(DAYOFWEEK, DATE(CURRENT_DATETIME('Asia/Jakarta'))) = 2 AND
DATETIME(registration_started_at, 'Asia/Jakarta') > DATETIME_ADD(DATETIME_ADD(DATETIME(DATE(CURRENT_DATETIME('Asia/Jakarta'))), INTERVAL 9 HOUR), INTERVAL -3 DAY) OR
DATETIME(registration_started_at, 'Asia/Jakarta') > DATETIME_ADD(DATETIME_ADD(DATETIME(DATE(CURRENT_DATETIME('Asia/Jakarta'))), INTERVAL 9 HOUR), INTERVAL -1 DAY)
)
What are the important components of this?
This expressoin:
DATETIME_ADD(DATETIME(DATE(CURRENT_DATETIME('Asia/Jakarta'))), INTERVAL 9 HOUR)
Returns 9:00 on the current date in Jakarata. No matter what, you want registration_started_at before that date/time.
This expression
EXTRACT(DAYOFWEEK, DATE(CURRENT_DATETIME('Asia/Jakarta')))
Returns the current day of the week, with 2 for Monday.
These expressions:
DATETIME_ADD(DATETIME_ADD(DATETIME(DATE(CURRENT_DATETIME('Asia/Jakarta'))), INTERVAL 9 HOUR), INTERVAL -3 DAY)
DATETIME_ADD(DATETIME_ADD(DATETIME(DATE(CURRENT_DATETIME('Asia/Jakarta'))), INTERVAL 9 HOUR), INTERVAL -1 DAY)
Just subtract 1 or 3 days from the current 9:00 datetime.
Perhaps something like:
WHERE
registration_started_at
BETWEEN
CASE WHEN EXTRACT(DAYOFWEEK FROM CURRENT_DATE()) = 2 -- if Monday
THEN DATETIME_ADD(CURRENT_DATE(), INTERVAL -63 HOUR)) -- then 63 hours back from midnight today
ELSE DATETIME_ADD(CURRENT_DATE(), INTERVAL -15 HOUR)) END -- else 15 hours back from midnight today
AND
DATETIME_ADD(CURRENT_DATE(), INTERVAL 9 HOUR)) -- 9am today
Never used bigquery, so it might need some fiddling, but the basic idea is that we ask via case when what the current day is and use it to change how much we go back in time
Using Mr. Caius Jard idea, I am able to make it works. I just need to cast CURRENT_DATE to DATETIME() to take the today's 00:00
WHERE
(DATETIME(registration_started_at, 'Asia/Jakarta') BETWEEN (
(CASE
WHEN EXTRACT(
DAYOFWEEK
FROM CURRENT_DATE('Asia/Jakarta')
) = 2 -- if Monday
THEN DATETIME_ADD(
DATETIME(CURRENT_DATE('Asia/Jakarta')),
INTERVAL -63 HOUR
) -- then 63 hours back from midnight today
ELSE DATETIME_ADD(
DATETIME(CURRENT_DATE('Asia/Jakarta')),
INTERVAL -15 HOUR
)
END)
) -- else 15 hours back from midnight today
AND DATETIME_ADD(
DATETIME(CURRENT_DATE('Asia/Jakarta')),
INTERVAL 9 HOUR
)) -- 9am today

How do I retrieve data in Monday to Friday Hourly Format

I have a table that is currently in the following format
ID
Title
CreatedOn
1
Test 1
2021-04-26 08:00:00
2
Test 2
2021-04-26 10:00:00
3
Test 3
2021-04-27 09:00:00
4
Test 4
2021-04-28 14:00:00
5
Test 5
2021-04-28 16:00:00
6
Test 6
2021-04-28 12:00:00
7
Test 7
2021-04-29 13:00:00
8
Test 8
2021-04-30 06:00:00
9
Test 9
2021-05-17 10:00:00
10
Test 10
2021-05-18 19:00:00
11
Test 11
2021-05-18 23:00:00
12
Test 12
2021-05-19 16:00:00
13
Test 13
2021-05-20 07:00:00
14
Test 14
2021-05-21 14:00:00
15
Test 15
2021-05-21 10:00:00
16
Test 16
2021-04-30 10:00:00
What I would like to do is a query that would tell me how many requests have been Monday to Friday per hour. So aggregate all the data into just rows of Monday to Friday.
So the query should return
Day
Hour
Count
Monday
08:00
1
Monday
10:00
2
Tuesday
10:00
1
Tuesday
19:00
1
Tuesday
23:00
1
Wednesday
14:00
1
Wednesday
16:00
2
Wednesday
12:00
1
etc.. How do I achieve this?
So far I have the following
SELECT
DATENAME(WEEK, CreatedOn) AS Week,
DATEPART(Hour, CreatedOn) AS Hour,
COUNT(*) AS Requests
FROM [Enterprise32].[dbo].[nav_EmailEstimateRequests]
where CreatedOn > '2021-01-01'
GROUP BY DATENAME(WK, CreatedOn),DATEPART(Hour, CreatedOn)
ORDER BY DATENAME(WK, CreatedOn);
But the above query returns each week so Week 1 up until Week 21. Please guide me in the right direction.
Thank you!
You want weekday for the date part:
SELECT DATENAME(WEEKDAY, CreatedOn) AS Weekday,
DATEPART(Hour, CreatedOn) AS Hour,
COUNT(*) AS Requests
FROM [Enterprise32].[dbo].[nav_EmailEstimateRequests]
WHERE CreatedOn > '2021-01-01'
GROUP BY DATENAME(WEEKDAY, CreatedOn), DATEPART(Hour, CreatedOn), DATEPART(WEEKDAY, CreatedOn)
ORDER BY DATEPART(WEEKDAY, CreatedOn), Hour;
Note: I included DATEPART(weekday, ) in the GROUP BY, so you could use it in the ORDER BY.

Get the total sum hours in a column SQL SERVER

Sql Fiddle Example
I have this result table
Id Hours
----- -----
1 09:00
2 09:30
3 10:00
4 10:30
5 11:00
6 11:30
7 12:00
8 12:30
9 13:00
10 13:30
11 14:00
12 14:30
13 15:00
14 15:30
15 16:00
16 16:30
17 17:00
18 17:30
19 18:00
I need to get the total sum hours, for example from 09:00 to 18:00 there is a total of :
9
hours, I need to get this sum of hours
Your table schema hour is varchar, you need to cast as time, then do the calculation
SELECT datediff(hour,min(cast(hour as time)),max(cast(hour as time)))
FROM Timetable
sqlfiddle
NOTE
I would suggest your hour column as datetime or time instead of varchar. because hour column intention is time.
EDIT
If your time is 9:00 to 17:30, you can try to use datediff minute to get the total diff minutes then divide 60 to get hours.
SELECT datediff(minute,min(cast(hour as time)),max(cast(hour as time))) / CAST(60 as float)
FROM Timetable
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=6e005cdfad4eca3ff7c4c92ef14cc9c7
use datediff function
select datediff(hour,min(h),max(h)) from
(
select CAST(hour AS TIME) as h from Timetable
) as t
strongly disagreed to put time value in varchar ,so it is better change your data type from varchar to time
declare #a time = '13:00',#b time = '17:30' --- Here you can give time, what you need.
select distinct convert(varchar(20)
, datediff(MINUTE,#a,#b) / 60)
+ ':' +
convert(varchar(20), datediff(MINUTE,#a,#b) % 60)
from #Timetable
where hour in (#a,#b)
For your SQL Fiddle Sample Data.
Obviously, you need to use datediff(). However, you should be doing the datediff() in minutes or seconds and then converting to hours:
SELECT datediff(minute, min(cast(hour as time)), max(cast(hour as time))) / 60.0
FROM Timetable;
This will handle the case where the number of hours is not an exact number of hours.

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)