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

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

Related

How to get last N week data in different year

I need to get last 6 weeks data from some table, right now the logic that I use is this
WEEK([date column]) BETWEEN WEEK(NOW()) - 6 AND WEEK(NOW())
It run as I want, but January is near and I realize that this query will not working as it is. I try to run my query on 15th January 2022, I only get data from 1st January to 15th January when I use my logic.
TGL MINGGU_KE
2022-01-01 | 1
2022-01-02 | 2
2022-01-03 | 2
2022-01-04 | 2
2022-01-05 | 2
2022-01-06 | 2
2022-01-07 | 2
2022-01-08 | 2
2022-01-09 | 3
2022-01-10 | 3
2022-01-11 | 3
2022-01-12 | 3
2022-01-13 | 3
2022-01-14 | 3
2022-01-15 | 3
Can I get the last 6 weeks data including last year?
This is my dbfiddle: https://dbfiddle.uk/o9BeAFJF
You can round the dates to the first day of the week using ROUND, TRUNC or THIS_WEEK
WITH
SEARCH_WEEK (TGL) AS (
VALUES date '2020-12-01'
UNION ALL
SELECT tgl + 1 DAY FROM SEARCH_WEEK WHERE tgl < CURRENT date
),
BASE_DATE (base_date) AS (
VALUES date '2022-01-15'
),
OPTIONS (OPTION, OPTION_BASE_DATE) AS (
SELECT OPTION, option_base_date FROM base_date CROSS JOIN LATERAL (
VALUES
('ROUND D', ROUND(base_date, 'D')),
('ROUND IW', ROUND(base_date, 'IW')),
('ROUND W', ROUND(base_date, 'W')),
('ROUND WW', ROUND(base_date, 'WW')),
('TRUNC D', TRUNC(base_date, 'D')),
('TRUNC IW', TRUNC(base_date, 'IW')),
('TRUNC W', TRUNC(base_date, 'W')),
('TRUNC WW', TRUNC(base_date, 'WW')),
('THIS_WEEK', THIS_WEEK(base_date)),
('THIS_WEEK + 1 DAY', THIS_WEEK(base_date) + 1 DAY)
) a (OPTION, OPTION_BASE_DATE)
)
SELECT
OPTION,
MIN(TGL) BEGIN,
max(tgl) END,
dayname(MIN(TGL)) day_BEGIN,
dayname(max(tgl)) day_end,
days_between(max(tgl), min(tgl)) + 1 duration_in_days
FROM
SEARCH_WEEK
CROSS JOIN options
WHERE
TGL BETWEEN option_base_date - 35 DAYS AND option_base_date + 6 DAYS
GROUP BY OPTION
OPTION
BEGIN
END
DAY_BEGIN
DAY_END
DURATION_IN_DAYS
ROUND D
2021-12-12
2022-01-22
Sunday
Saturday
42
ROUND IW
2021-12-13
2022-01-23
Monday
Sunday
42
ROUND W
2021-12-11
2022-01-21
Saturday
Friday
42
ROUND WW
2021-12-11
2022-01-21
Saturday
Friday
42
THIS_WEEK
2021-12-05
2022-01-15
Sunday
Saturday
42
THIS_WEEK + 1 DAY
2021-12-06
2022-01-16
Monday
Sunday
42
TRUNC D
2021-12-05
2022-01-15
Sunday
Saturday
42
TRUNC IW
2021-12-06
2022-01-16
Monday
Sunday
42
TRUNC W
2021-12-11
2022-01-21
Saturday
Friday
42
TRUNC WW
2021-12-11
2022-01-21
Saturday
Friday
42
fiddle
you can use dateadd to get first day of week six weeks ago like this:
Select * from tableName
where [dateColumn] between dateadd(WEEK,-6,getdate()) and getdate()
You can use DATEADD to get last 6 weeks of data as follows:
Select * from [TableName] where [DateColumn] between
DATEADD(WEEK,-6,GETDATE()) and GETDATE();

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.

Big query- get last date of every month in a year

Iam trying to extract only the last dates of every month in a year.
SELECT
*
FROM
UNNEST(GENERATE_DATE_ARRAY('2018-04-30', '2027-03-31', INTERVAL 1 MONTH)) AS example
ORDER BY 1 ASC
I am getting
1 2018-04-30
2 2018-05-30
3 2018-06-30
4 2018-07-30
5 2018-08-30
6 2018-09-30
7 2018-10-30
8 2018-11-30
9 2018-12-30
10 2019-01-30
EXPECTATION:
31/01/2013
28/02/2013
31/03/2013
30/04/2013
31/05/2013
30/06/2013
31/07/2013
31/08/2013
30/09/2013
31/10/2013
30/11/2013
31/12/2013
31/01/2014
28/02/2014
31/03/2014
30/04/2014
31/05/2014
30/06/2014
31/07/2014
31/08/2014
30/09/2014
31/10/2014
30/11/2014
31/12/2014
31/01/2015
28/02/2015
You could generate an array of the first day of each month and then subtract one day to get the last day of the previous month:
SELECT DATE_SUB(example, INTERVAL 1 DAY)
FROM UNNEST(GENERATE_DATE_ARRAY('2018-05-1', '2027-04-01', INTERVAL 1 MONTH)) AS example
Consider below approach
SELECT LAST_DAY(example, MONTH) last_day_of_month
FROM UNNEST(GENERATE_DATE_ARRAY('2018-04-30', '2027-03-31', INTERVAL 1 MONTH)) AS example

Trying to get an MS Access query the hours in one day

I have three 5 columns in my query:
Count(ID)
Min(Date)
Max(Date)
Hours: DateDiff("h",Min([Date]),Max([Date]))
UPH: Count([ID])/[Hours]
I want the query to return the count per day, for example:
Count(ID) | Min(Date) | Max(Date) | Hours | UPH
30 | 9/29/2020 11:00:00 AM | 9/29/2020 11:00:00 PM | 12 | 2.5
I have tried the following code but had no luck
Between #9/29/2020 12:00:01 AM# And #9/29/2020 11:59:59 PM#
Seems like I can only get it to run within a day range, not an hourly range.

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 |
+-------------------------+-------------------------+