I have a column of store opening hours in STRING format where one column*row entry looks like this:
Monday: 10:00 - 20:00, Tuesday: 10:00 - 20:00, Wednesday: 10:00 - 20:00, Thursday: 10:00 - 20:00, Friday: 10:00 - 20:00, Saturday: 10:00 - 20:00, Sunday: 11:00 - 18:00
. I would like to transform this entry into several column*row entries such like this:
Weekday
Opening
Closing
0
00:10:00
00:20:00
1
00:10:00
00:20:00
The timestamp format I need in order to obtain foottraffic for stores at certain hours of the day.
Consider below option as well,
weekday in your expected output is not clear to me. If it's just an order of appearance, I thinks below query is enough for your purpose.
WITH sample_data AS (
SELECT 'Monday: 10:00 - 20:00, Tuesday: 10:00 - 20:00, Wednesday: 10:00 - 20:00, Thursday: 10:00 - 20:00, Friday: 10:00 - 20:00, Saturday: 10:00 - 20:00, Sunday: 11:00 - 18:00' str
)
SELECT offset AS weekday,
PARSE_TIME('%H:%M', SPLIT(RIGHT(day, 13), ' - ')[OFFSET(0)]) opening,
PARSE_TIME('%H:%M', SPLIT(RIGHT(day, 13), ' - ')[OFFSET(1)]) closing,
FROM sample_data, UNNEST(SPLIT(str, ', ')) day WITH offset;
but if you want an explicit order of weekday, you can use this instead.
SELECT offset AS weekday,
PARSE_TIME('%H:%M', SPLIT(RIGHT(day, 13), ' - ')[OFFSET(0)]) opening,
PARSE_TIME('%H:%M', SPLIT(RIGHT(day, 13), ' - ')[OFFSET(1)]) closing,
FROM sample_data, UNNEST(SPLIT(str, ', ')) day
JOIN UNNEST(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']) w WITH offset
ON SPLIT(day, ':')[OFFSET(0)] = w;
10:00 - 20:00 seems to be between 10 to 20 o'clock, not 10 minute to 20 minute, so I've used '%H:%M' format string. If you really mean it as minutes, you can use '%M:%S' instead, then output will be like below.
For your requirement, split can be used. You can check out this below sample query.
declare value String DEFAULT 'Monday: 10:00 - 20:00, Tuesday: 10:00 - 20:00, Wednesday: 10:00 - 20:00, Thursday: 10:00 - 20:00, Friday: 10:00 - 20:00, Saturday: 10:00 - 20:00, Sunday: 11:00 - 18:00';
select weekday,
concat('00:',trim(opening))opening,
concat('00:',trim(closing))closing
from(
select *,
SPLIT(trim(c), ':')[OFFSET(0)] weekDay,
SPLIT(trim(c), '-')[OFFSET(0)] t1,
Split(SPLIT(trim(c), '-')[OFFSET(0)],':')[OFFSET(1)] t2,
Split(SPLIT(trim(c), '-')[OFFSET(0)],':')[OFFSET(2)] t3,
concat(Split(SPLIT(trim(c), '-')[OFFSET(0)],':')[OFFSET(1)],
':',Split(SPLIT(trim(c), '-')[OFFSET(0)],':')[OFFSET(2)])opening, # by concatinating t2 and t3
SPLIT(trim(c), '-')[OFFSET(1)] closing
from (
SELECT * from
UNNEST(SPLIT(value)) c )
)
Output :
Related
I am attempting to find all employees that worked during a 24 hour period. Their shift starts at 08:00 and ends the next day at 08:00. Most start at 08:00 and end the next day at 08:00. However, the start and end times may occur at any time during the 24 hour period. My goal would be to have all of those that worked during the 24 hour period to be marked as starting on the day the shift started.
For example, in the table below even though there are multiple start times over 2 days they all occurred within the 24 hours of the shift start time beginning on 7/26/22 at 08:00. I would like a new column showing a shift day of 7/26/22.
first
start_time
end_time
hours
Nicole
7/26/22 8:00
7/27/22 8:00
24
Callan
7/26/22 8:00
7/27/22 8:00
24
Bob
7/26/22 18:30
7/27/22 6:30
12
Kevin
7/27/22 7:00
7/27/22 8:00
1
Michael
7/27/22 7:00
7/27/22 8:00
1
If the start time is greater than or equal to 8 am you want the shift date to be the same date as the start time, otherwise it is the previous day:
create table #shifts
(
[first] varchar(32), -- this is a very weird column name. first_name, perhaps?
start_time datetime,
end_time datetime,
[hours] tinyint
);
set dateformat mdy; -- your sample data uses an ambiguous date format so I have to do this
insert #shifts values
('Nicole ', '7/26/22 08:00', '7/27/22 8:00', 24),
('Callan ', '7/26/22 08:00', '7/27/22 8:00', 24),
('Bob ', '7/26/22 18:30', '7/27/22 6:30', 12),
('Kevin ', '7/27/22 07:00', '7/27/22 8:00', 1),
('Michael', '7/27/22 07:00', '7/27/22 8:00', 1);
select [first],
start_time,
end_time,
[hours],
shift_date = iif
(
cast(start_time as time) >= '8:00',
cast(start_time as date),
cast(dateadd(day, -1, start_time) as date)
)
from #shifts;
Produces:
first
start_time
end_time
hours
shift_date
Nicole
2022-07-26 08:00:00.000
2022-07-27 08:00:00.000
24
2022-07-26
Callan
2022-07-26 08:00:00.000
2022-07-27 08:00:00.000
24
2022-07-26
Bob
2022-07-26 18:30:00.000
2022-07-27 06:30:00.000
12
2022-07-26
Kevin
2022-07-27 07:00:00.000
2022-07-27 08:00:00.000
1
2022-07-26
Michael
2022-07-27 07:00:00.000
2022-07-27 08:00:00.000
1
2022-07-26
I have a table with interval dates and times. Can i create a full list with this data?
Table example:
Start_Date, End_Date, Start_Time, End_Time, Interval
01-jun-2021 02-jun-2021 08:00 10:00 30
03-jun-2021 04-jun-2021 10:00 12:00 15
Result:
01-jun-2021 08:00
01-jun-2021 08:30
01-jun-2021 09:00
01-jun-2021 09:30
02-jun-2021 08:00
02-jun-2021 08:30
02-jun-2021 09:00
02-jun-2021 09:30
03-jun-2021 10:00
03-jun-2021 10:15
03-jun-2021 10:30
03-jun-2021 11:00
03-jun-2021 11:15
03-jun-2021 11:30
03-jun-2021 11:45
04-jun-2021 10:00
04-jun-2021 10:15
04-jun-2021 10:30
04-jun-2021 11:00
04-jun-2021 11:15
04-jun-2021 11:30
04-jun-2021 11:45
Thanks.
This is a handy place to use a recursive CTE:
with cte (start_date, end_date, interval) as (
select to_date(start_date||start_time, 'DD-Mon-YYYYHH24:MI'), to_date(end_date||end_time, 'DD-Mon-YYYYHH24:MI'), interval
from t
union all
select cte.start_date + cte.interval * interval '1' minute, end_date, interval
from cte
where cte.start_date < end_date
)
select cast(start_date as timestamp)
from cte
order by start_date;
Here is a db<>fiddle.
You can use a recursive CTE, but the logic has to skip to the next day when you reach the end time; so this works:
with rcte (date_time, end_date, start_int, end_int, step_int) as (
select
start_date + to_dsinterval('0 ' || start_time || ':00'),
end_date,
to_dsinterval('0 ' || start_time || ':00'),
to_dsinterval('0 ' || end_time || ':00'),
interval * interval '1' minute
from your_table
union all
select
case
when date_time + step_int < trunc(date_time) + end_int
then date_time + step_int
else trunc(date_time) + interval '1' day + start_int
end,
end_date,
start_int,
end_int,
step_int
from rcte
where date_time + step_int < end_date + end_int
)
select date_time
from rcte
order by date_time
DATE_TIME
-------------------
2021-06-01 08:00:00
2021-06-01 08:30:00
2021-06-01 09:00:00
2021-06-01 09:30:00
2021-06-02 08:00:00
2021-06-02 08:30:00
2021-06-02 09:00:00
2021-06-02 09:30:00
2021-06-03 10:00:00
2021-06-03 10:15:00
2021-06-03 10:30:00
2021-06-03 10:45:00
2021-06-03 11:00:00
2021-06-03 11:15:00
2021-06-03 11:30:00
2021-06-03 11:45:00
2021-06-04 10:00:00
2021-06-04 10:15:00
2021-06-04 10:30:00
2021-06-04 10:45:00
2021-06-04 11:00:00
2021-06-04 11:15:00
2021-06-04 11:30:00
2021-06-04 11:45:00
db<>fiddle showing the anchor member including converting the times and interval to real day to second intervals types for later use; the anchor and recursive members with all the intermediate columns; and finally just this version with a single column.
You can format the resulting date value however you want, of course.
I have JSON data as follows:
[{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":1},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":2},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":3},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":4},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":5},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":6},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":7}]
I need to extract values of dayOfWeek, start & end
Tried the solution suggested for Unable to convert varchar to array in Presto Athena but no luck.
Expected Result:
dayOfWeek start end
1 11:00 22:00
2 11:00 22:00
3 11:00 22:00
4 11:00 22:00
5 11:00 22:00
6 11:00 22:00
7 11:00 22:00
You can use the following query. Verified with version 0.172.
WITH data(value) AS (VALUES
'[{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":1},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":2},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":3},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":4},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":5},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":6},{"all":{"end":"22:00","start":"11:00"},"dayOfWeek":7}]'
),
parsed(c1) AS (
SELECT cast(json_parse(value) AS array(json))
FROM data
)
SELECT
json_extract_scalar(json1, '$.dayOfWeek') AS "dayOfWeek"
,json_extract_scalar(json1, '$.all.start') AS "start"
,json_extract_scalar(json1, '$.all.end') AS "end"
FROM parsed, unnest(c1) as t(json1)
I have a question and hopefully someone can help, because i have been stuck on this for a long time.
I have a column with remaining minutes for a task to expire and i want to calculate when this task will expire within the business days timeframe starting from the current sysdate day lets say weekdays from 09:00 to 17:00.
| Task No | Minutes Remaining | Expiration date |
| Task1 | 1800 | 27-10-16 9:45 AM |
| Task2 | 3400 | 28-10-16 9:45 AM |
| Task3 | 400 | 29-10-16 9:45 AM |
| Task4 | 180 | 30-10-16 9:45 AM |
| Task5 | 8400 | 31-10-16 9:45 AM |
| Task6 | 5000 | 1-11-16 9:45 AM |
OK, this was a fun problem. To summarize: You are given a date (which in Oracle always includes the time-of-day) from which you start measurement, and an initial duration in minutes. You need to find the expiration date (meaning date and time-of-day as always), which is calculated by adding the duration in minutes to the "clock-starting" date, but the clock should only run during business hours - 9 to 17, Monday to Friday only (not on weekends).
I assume if the "minutes remaining" is 0, then the expiration should be the same as the "clock-starting" date if it falls within work hours, or 9 am on the next work day otherwise.
To understand the solution, let's break it down in two parts. First let's consider a very special case: the "clock starts" on a Monday at 9 am. Then break down minutes remaining into an integer multiple of 2400 (5*8*60 = 2400 minutes in a full work week), plus an integer multiple of 480 from what's left (480 minutes to a work day), plus whatever is left, if anything. Then: the expiration date is the "clock-starting" date, plus however many weeks, plus however many whole days (between 0 and 4), plus the remaining minutes. One exceptional case here: if the "minutes remaining" is an exact multiple of 480 minutes, then expiration is at 5 pm on a certain work day, and not 9 am on the next work day. This requires special handling in the formula. All this is done in the outer query (at the bottom of the solution below).
Then we need to reduce the general case to this special case. This is done in the subquery prep in the solution. I simply increase the "minutes remaining" by the work minutes elapsed from 9 am on Monday at the beginning of the week. This is a relatively simple computation. Note that if the "clock starting" date is after 5 pm on a Friday (or any time on Saturday or Sunday), I must add exactly 2400 minutes, a full work week.
In the solution, I show a variety of "clock starting" dates, dt, and minutes remaining, rm. I tested a variety of situations, and I think the solution is correct, but you may want to test on more data (other situations I didn't include in the tests).
with
inputs ( task, min_rem, dt ) as (
select 'Task1', 1800, to_date('27-10-16 9:45 AM', 'dd-mm-yy hh:mi AM') from dual union all
select 'Task2', 3400, to_date('28-10-16 9:45 AM', 'dd-mm-yy hh:mi AM') from dual union all
select 'Task3', 400, to_date('29-10-16 3:45 AM', 'dd-mm-yy hh:mi AM') from dual union all
select 'Task4', 180, to_date('30-10-16 9:45 AM', 'dd-mm-yy hh:mi AM') from dual union all
select 'Task5', 8400, to_date('31-10-16 9:45 PM', 'dd-mm-yy hh:mi AM') from dual union all
select 'Task6', 5000, to_date('01-11-16 5:00 PM', 'dd-mm-yy hh:mi AM') from dual union all
select 'Task7', 0, to_date('01-12-16 5:00 PM', 'dd-mm-yy hh:mi PM') from dual
),
prep ( task, min_rem, dt, adj_min, adj_dt ) as (
select task, min_rem, dt,
min_rem + case when dt > trunc(dt, 'iw') + 5 + 17/24 then 2400
else (trunc(dt) - trunc(dt, 'iw')) * 480 +
least(480, greatest(0, 1440 * (dt - trunc(dt) - 9/24)))
end,
trunc(dt, 'iw') + 9/24
from inputs
)
select task, min_rem, dt,
adj_dt + 7 * trunc(adj_min / 2400)
+ case when adj_min/480 = trunc(adj_min/480)
then mod(adj_min, 2400) / 480 - 1 + 8/24
else trunc(mod(adj_min, 2400) / 480) + mod(adj_min, 480) / 1440
end as expiration
from prep
order by task
;
Output:
TASK MIN_REM DT EXPIRATION
----- ---------- ----------------- -----------------
Task1 1800 27-10-16 09:45 AM 01-11-16 03:45 PM
Task2 3400 28-10-16 09:45 AM 08-11-16 10:25 AM
Task3 400 29-10-16 03:45 AM 31-10-16 03:40 PM
Task4 180 30-10-16 09:45 AM 31-10-16 12:00 PM
Task5 8400 31-10-16 09:45 PM 24-11-16 01:00 PM
Task6 5000 01-11-16 05:00 PM 16-11-16 12:20 PM
Task7 0 01-12-16 05:00 PM 01-12-16 05:00 PM
7 rows selected
I would like to generate a list of half an hour interval. Any suggestion would be very helpful. I tried this and did not work. Thank you
WITH cte
AS (select convert(varchar, DATEADD(Day, 0, DATEDIFF(Day, 0, GetDate())), 108) AS Today
UNION ALL
SELECT dateadd(MINUTE, 30, Today) AS Today
FROM cte
WHERE dateadd(MINUTE, 30,Today) < (select convert(varchar, DATEADD(Day, 1, DATEDIFF(Day, 0, GetDate())), 108))
)
SELECT*
FROM cte
To get:
0:00
0:30
1:00
1:30
2:00
2:30
3:00
3:30
4:00
4:30
5:00
5:30
6:00
6:30
7:00
7:30
8:00
8:30
9:00
9:30
10:00
10:30
11:00
11:30
12:00
12:30
13:00
13:30
14:00
14:30
15:00
15:30
16:00
16:30
17:00
17:30
18:00
18:30
19:00
19:30
20:00
20:30
21:00
21:30
22:00
22:30
23:00
23:30
0:00
You can't convert the time to a VARCHAR and then use DATETIME functions on it, you need to keep it as a DATETIME throughout the recursive portion:
;WITH cte AS (SELECT DATEADD(day, 0, DATEDIFF(day, 0, GETDATE())) dt
UNION ALL
SELECT DATEADD(MINUTE, 30, dt) AS dt
FROM cte
WHERE dt < DATEADD(day,1,GETDATE())
)
SELECT CONVERT(VARCHAR(12),dt, 108)
FROM cte