split night shift timings and split the working hours in 2 parts in mysql - mysql-workbench

I would like to split the shift timings.
night shift timings (22:01:00(day1) - 06:00:00(day 2))
I have 2 columns start_date and end_Date,
suppose a person started at 23:55:00 and finish 1:20:00
again started at 1:30:00 and finish at 05:50:00
like this there are multiple records.
example 1
May I know how to split the working hours in 2 parts
day-1 - (23:55:00) - (23:59:59) (4/5mins) (between 23:55:00 - 1:20:00)
day-2 - (12:00:00) - (06:00:00) (rest of the working hours)
and one more example(2)
a person started work at 9pm
shifts should be split like this
9pm - 10pm (afternoon)
10pm - 11.59pm (day 1)
12am - 6am (day 2)
I tried this for example 1
select
start_dt as start_date,
end_dt as end_date,
ucase(rep) as assignee,
timediff(end_dt,start_dt) as total_time_spent,
(case
when
CONVERT(start_dt,TIME) > convert('22:01:00',TIME)
and convert(end_dt,TIME) <= CONVERT('11:59:59',TIME)
then
timediff(end_dt,start_dt)
end ) as time_spent_day_1 ,
(case
when
CONVERT(start_dt,TIME) > convert('12:00:00',TIME)
-- and convert(end_dt,TIME) <= CONVERT('05:59:59',TIME)
then
timediff(end_dt,start_dt)
end) as time_spent_day_2
from table_1
WHERE CONVERT(start_dt,TIME) > convert('22:01:00',TIME)
and CONVERT(DATE_ADD(end_dt, INTERVAL 1 DAY),TIME) <= convert('05:59:59',TIME)
and ucase(rep) like '%xyz%' ```

Related

Exclude weekend between two timestamps in a query in Db2

I have two columns with a timestamp in each
column_a column_b
2021-08-03 13:22:29 2021-08-09 15:51:59
I want to calculate the difference in hours, but exclude the weekend (if the dates fall on or between the two timestamps).
I have tried TIMESTAMPDIFF and HOURS_BETWEEN - but these would still include the weekend.
UPDATE:
my solution was to ...
create a function to calculate the number of days between the two days, excluding weekends taken from here
How to get the WORKING day diff in db2 without saturdays and sundays?
Then in my SELECT used Db2s native DATEDIFF(8,xxx,yyy) to get the total number of hours, and subtracted from this DATEDIFF, the value returned from the function * 24 (for hours)
WITH
MYTAB (A, B) AS
(
VALUES ('2021-08-03 13:22:29'::TIMESTAMP, '2021-08-09 15:51:59'::TIMESTAMP)
)
, MYTAB2 (A, B) AS
(
SELECT
CASE WHEN DAYOFWEEK(A) IN (1, 7) THEN DATE (A) + 1 DAY ELSE A END
, CASE WHEN DAYOFWEEK(B) IN (1, 7) THEN B - (MIDNIGHT_SECONDS (B) + 1) SECOND ELSE B END
FROM MYTAB
)
, R (TS) AS
(
SELECT V.TS
FROM MYTAB2 T, TABLE (VALUES T.A, T.B) V (TS)
WHERE T.A <= T.B
UNION ALL
SELECT DATE (R.TS) + 1 DAY
FROM R, MYTAB2 T
WHERE DATE (R.TS) + 1 DAY < DATE (T.B)
)
SELECT
COALESCE
(
-- Seconds between start and end
(DAYS (MAX (TS)) - DAYS (MIN (TS))) * 86400
+ MIDNIGHT_SECONDS (MAX (TS)) - MIDNIGHT_SECONDS (MIN (TS))
-- SUM of seconds for weekend days is subtracted
- SUM (CASE WHEN DAYOFWEEK (TS) IN (1, 7) THEN 86400 ELSE 0 END)
, 0
) / 3600 AS HRS
FROM R
The idea is to construct the following table with Recursive Common Table expression first:
2021-08-03 13:22:29 --> 2021-08-04 00:00:00 (if start is a weekend)
2021-08-04 00:00:00 --> 2021-08-05 00:00:00 (if start is a weekend)
...
2021-08-08 00:00:00 --> 2021-08-07 00:00:00 (if end is a weekend)
2021-08-09 15:51:59 --> 2021-08-08 23:59:59 (if end is a weekend)
That is: one timestamp for each day between the start and the end timestamps. These start and end timestamps are adjusted:
If start is a weekend - change it to the start of the next day
If end is a weekend - change it to the end of the previous day
The final calculation is simple: we subtract sum of seconds for all weekends in the list from the difference in seconds between start and end.

Postgresql - query to get difference in data count

I have two tables, today's_table and yeterday's_table.
I need to compare the data for an interval of 15 mins at exact same times for today and yesterday.
For example, for below data let's I need to check from 00:00:00 and 00:15:00 on 20201202 and 20201202. So difference should come out as '3' since the yesterday's_table has 8 records and today's_table has 5 records.
today's_table:
Yesterday's table:
I tried something like; (consider now() is 00:15:00)
select count(*) from yeterday's_table where time between now() - interval "24 hours" and now() - interval "23 hours 45 mins"
minus
select count(*) from today's_table where time = now() - interval "15 minutes";
is there any other way to do this?
You can easily do this with subqueries:
SELECT b.c - a.c
FROM (select count(*) as c from yeterdays_table where time between now() - interval '24 hours' and now() - interval '23 hours 45 mins') a,
(select count(*) as c from todays_table where time = now() - interval '15 minutes') b;
Bear in mind you need to single-quote your intervals, and your table names cannot have quotes in them.

DB2: Bi-monthly query for a DB2 report

I am currently writing a Crystal Report that has a DB2 query as its backend. I have finished the query but am stuck on the date portion of it. I am going to be running it twice a month - once on the 16th, and once on the 1st of the next month. Here's how it should work:
If I run it on the 16th of the month, it will give me results from the 1st of that same month to the 15th of that month.
If I run it on the 1st of the next month, it will give me results from the 16th of the previous month to the last day of the previous month.
This comes down a basic bi-monthly report. I've found plenty of hints to do this in T-SQL, but no efficient ways on how to accomplish this in DB2. I'm having a hard time wrapping my head around the logic to get this to consistently work, taking into account differences in month lengths and such.
There are 2 expressions for start and end date of an interval depending on the report date passed, which you may use in your where clause.
The logic is as follows:
1) If the report date is the 1-st day of a month, then:
DATE_START is 16-th of the previous month
DATE_END is the last day of the previous month
2) Otherwise:
DATE_START is 1-st of the current month
DATE_END is 15-th of the current month
SELECT
REPORT_DATE
, CASE DAY(REPORT_DATE) WHEN 1 THEN REPORT_DATE - 1 MONTH + 15 ELSE REPORT_DATE - DAY(REPORT_DATE) + 1 END AS DATE_START
, CASE DAY(REPORT_DATE) WHEN 1 THEN REPORT_DATE - 1 ELSE REPORT_DATE - DAY(REPORT_DATE) + 15 END AS DATE_END
FROM
(
VALUES
DATE('2020-02-01')
, DATE('2020-02-05')
, DATE('2020-02-16')
) T (REPORT_DATE);
The result is:
|REPORT_DATE|DATE_START|DATE_END |
|-----------|----------|----------|
|2020-02-01 |2020-01-16|2020-01-31|
|2020-02-05 |2020-02-01|2020-02-15|
|2020-02-16 |2020-02-01|2020-02-15|
In Db2 (for Unix, Linux and Windows) it could be a WHERE Condition like
WHERE
(CASE WHEN date_part('days', CURRENT date) > 15 THEN yourdatecolum >= this_month(CURRENT date) AND yourdatecolum < this_month(CURRENT date) + 15 days
ELSE yourdatecolum > this_month(CURRENT date) - 1 month + 15 DAYS AND yourdatecolum < this_month(CURRENT date)
END)
Check out the THIS_MONTH function - there are multiple ways to do it. Also DAYS_TO_END_OF_MONTH might be helpful

Grouping data by quarter intervals (or any time interval) with a defined starting basis in postgresql

Let's say I have a table orders with columns amount and order_date.
I want to be able to group this data by quarters and aggregate the amount, the catch however is that the quarters do not start on January 1st but on any given arbitrary date, say July 12th. These quarters are also split in 13 week intervals. From what I see using something like date_trunc such as:
SELECT SUM(orders.amount), DATE_TRUNC('quarter', orders.order_date) AS interval FROM orders WHERE orders.order_date BETWEEN [date_start] AND [date_end] GROUP BY interval
is out of the question as this forces quarters to start on Jan 1st and it has 'hardcoded' quarter starting dates (Apr 1st, Jul 1st, etc).
I have tried using something like:
SELECT SUM(orders.amount),
to_timestamp(floor((extract('epoch' from orders.order_date / 7862400 )) * 7862400 ) AT TIME ZONE 'UTC' AS interval
FROM orders
WHERE orders.order_date BETWEEN [date_start] AND [date_end]
GROUP BY interval
(where 7862400 is the time interval that I want)
But with this method I cannot figure out how to set the offset for the initial grouping date, in my example I would like it to start from July 12th of each year (then count 13 weeks and start the next quarter, and so on). Hope I was clear and I would appreciate any help!
You can use generate_series() to create the first day of each quarter, join it and group by it.
SELECT quarters.first_day,
quarters.first_day + '13 weeks'::interval last_day,
sum(orders.amount) amount
FROM orders
LEFT JOIN generate_series('2019-07-12'::timestamp,
'2020-07-10'::timestamp,
'13 weeks'::interval) quarters (first_day)
ON quarters.first_day <= orders.order_date
AND quarters.first_day + '13 weeks'::interval > orders.order_date
WHERE orders.order_date BETWEEN [date_start]
AND [date_end]
GROUP BY quarters.first_day,
quarters.first_day + '13 weeks'::interval;
You just need to make sure, that the boundary days you give the generate_series() cover the whole period you want to query, so that depends on your [date_start] and [date_end].
You can generate your own 'quarterly calendar' and use that in place of the Postgers 'quarter' date extraction.
create or replace function quarterly_calendar(annual_date text default extract('YEAR' from current_date)::text)
returns table( quarter integer
, quarter_start_date date
, quarter_end_date date
)
language sql immutable strict leakproof
as $$
with RECURSIVE quarters as
(select 1 qtr, qdt::date q_start_dt, (qdt + interval '90 day' )::date q_end_dt, (qdt+interval '1 year' - interval '1 day')::date last_dt
from ( select date_trunc('year',current_date) + interval '6 month 11 day' qdt) q
union all
select qtr+1, (q_end_dt + interval '1 day')::date, least ((q_end_dt + interval '91 day')::date,last_dt), last_dt
from quarters
where qtr+1 <=5
)
select qtr, q_start_dt, q_end_dt
from quarters;
$$;
-- test
select * from quarterly_calender();
It does actually create 5 quarters. But that is because a year is not a multiple of 13 weeks (or 91 days or 7862400 seconds). In your given year from 12-July-2019 through 11-July-2020 is 2 days (366 days total) over 4 times that interval. You'll have to decide how to handle that 5th quarter. It occurs every year, having either 1 or 2 days. Hope this helps .

PostgreSQL - Insert all weekend dates from year xxxx to year yyyy

I have already built a holidays table containing all public holidays from 2000 to 2050. But I have should have put also all weekend dates and now I am trying to find an approach to it. Can somebody suggest something? I checked and there are functions for calculating the number of the business days, but I need insertion of all weekends between these two years.
If 8.4+:
select
a::date as Sunday,
a::date - 1 as Saturday
from generate_series('2000-01-02'::date, '2050-12-31', '7 days') s(a)
;
Else:
select
'2000-01-02'::date + s.a as Sunday,
'2000-01-02'::date + s.a - 1 as Saturday
from generate_series(0, '2050-12-31'::date - '2000-01-02'::date, 7) s(a)
;