Query date and specific time in the where clause 3 - sql

How can I get 3rd shift data from the where clause. I'm using current date, but even with what I don't get any resutls. I'm looking to see 3rd shift sale from time range 9pm to 5am ( Next Day ).
SELECT WORK_CENTER, SUM(SALES) AS SALES
FROM ZME_SALES
WHERE DATE_TIME_START >=sys_extract_utc(cast(trunc(current_date) + 21/24 as time stamp with time zone))
AND DATE_TIME_START < sys_extract_utc(cast(trunc(current_date)+1 + 5/24 as timestamp with time zone))
Here's my expected results.
SHIFT WORK_CENTER SALES
SHIFT-3 8001 1524
SHIFT-3 8002 1347
SHIFT-3 8003 1201
SHIFT-3 8004 1146

Related

Generating Time slots between two time for today

I am editing my question little bit
I have Office Timing Table As follows.
TIME_FROM TIME_TO TIME_FROM1 TIME_TO1 TIME_FROM2 TIME_TO2
07:00 AM 14:00 PM 700 1400 06/08/2020 07:00:00 AM 06/08/2020 02:00:00 PM
16:00 PM 18:00 PM 1600 1800 06/08/2020 04:00:00 PM 06/08/2020 06:00:00 PM
Office Starting time is 7.00 AM and ending time is 6.00 PM with break time in between.This Times can vary based on selected Office.
Input parameters are
1.Travel Time in minutes to reach Office
2.Time slot duration in minutes
After taking Travel Time in minutes into consideration,I want to generate time slots of 15 minute(variable) interval between these time ranges like
7.00 AM
7.15 AM
7.30 AM
7.45 AM
8.00 AM
.
.
.
.
1.30 PM
1.45 PM
2.00 PM
Second Shift starts here
4.00 PM
4.15 PM
4.30 PM
.
.
.
.
.
5.30 PM
5.45 PM
Scenario 1 :
Travel time needed :31 minutes
Booking attempt time 6.15 AM
Office Opening Time 7.00 AM
Required result
7.00
7.15
.
.
1.45 PM( Dont include shift ending time 2.00PM)
4.00 PM
4.15 PM
.
.
5.45 PM
Scenario 2 :
Travel time needed :31 minutes
Booking attempt time 6.45 AM
Office Opening Time 7.00 AM
Required result
7.16
7.31
.
.
1.46 PM( Dont include shift ending time 2.00PM)
4.00 PM
4.15 PM
.
.
5.45 PM
Scenario 3 :
Travel time needed :31 minutes
Booking attempt time 9.45 AM
Office Opening Time 7.00 AM
Required result
10.16
10.31
.
.
1.46 PM( Dont include shift ending time 2.00PM)
4.00 PM
4.15 PM
.
.
5.45 PM
Scenario 4 :
Travel time needed :31 minutes
Booking attempt time 3 PM
Office Second shift opening Time 4.00 PM
Required result
04.00 PM
04.15 PM
.
.
5.45 PM( Dont include shift ending time 18.00PM)
Scenario 5 :
Travel time needed :31 minutes
Booking attempt time 3.45 PM
Office Second shift opening Time 4.00 PM
Required result
04.16 PM
04.31 PM
.
.
5.46 PM( Dont include shift ending time 18.00PM)
WITH
--cte to determine office hours, this is probably a table irl
office_timing (id, time_from2, time_to2) AS
(
SELECT 1, TO_DATE('09/08/2020 07:00:00 AM','DD/MM/YYYY HH:MI:SS AM'), TO_DATE('09/08/2020 02:00:00 PM','DD/MM/YYYY HH:MI:SS AM') FROM dual UNION ALL
SELECT 2, TO_DATE('09/08/2020 04:00:00 PM','DD/MM/YYYY HH:MI:SS AM'), TO_DATE('09/08/2020 06:00:00 PM','DD/MM/YYYY HH:MI:SS AM') FROM dual
)
--cte to determine when travel time to office, replace with other values to test. Make this a variable if it is an input parameter
,travel_time (travel_mins) AS
(
SELECT 31 FROM DUAL
)
--cte to determine slot length, replace with other values to test. Make this a variable if it is an input parameter
,
slot_minutes (mins) AS
(
SELECT 15 FROM DUAL
)
--cte to determine when query is run, replace with other values to test. Make this a variable if it is an input parameter
,run_date_tab (run_date) AS
(
SELECT
TO_DATE('09/08/2020 03:45:00 PM','DD/MM/YYYY HH:MI:SS AM') + travel_mins/1440
FROM travel_time
)
--cte to determine start time based on the query run date
-- if run date is in a time slot then take run date
-- if run date is outside time slot then take closest future start date
,
start_time_tab (qry_start_time) AS
(
SELECT MIN(CASE
WHEN t.time_from2 <= r.run_date AND t.time_to2 > r.run_date
THEN r.run_date
WHEN t.time_from2 > r.run_date
THEN t.time_from2
ELSE
NULL
END)
FROM run_date_tab r
CROSS JOIN office_timing t
)
,slots (slot_start_time) AS
(
SELECT
s.qry_start_time +(level - 1) / ((60/m.mins)*24)
FROM start_time_tab s CROSS JOIN slot_minutes m CONNECT BY
level < 100
)
SELECT TO_CHAR(s.slot_start_time,'DD/MM/YYYY HH:MI:SS AM')
FROM slots s
JOIN office_timing t ON t.time_from2 < s.slot_start_time AND t.time_to2 > s.slot_start_time;
Oracle provides a fairly comprehensive date (including timestamp) handling functions. Generally I live by the axiom that once a column is converted to date the only reason to convert to a string it to create a display column. There are however exceptions. One of those is (often times) when the date needs to be sliced and diced and reassembled. That is the case here to properly calculate the end time by your 'match the minute run' requirement .
You are not going to get shift break line from an SQL solution, at least I'm going to do it. You could with PL/SQL by iterating over the results. However, that is an easy task for your presentation layer. I have added a column to the final result indicating the shift. With that in mind:
with time_range (sts, ets) as
( select case when extract(hour from systimestamp) <= 07
then trunc(systimestamp) + interval '07:00' hour to minute
else trunc(systimestamp, 'mi')
end sot
, case when extract(hour from systimestamp) <= 07
then trunc(systimestamp) + interval '18:00' hour to minute
else to_timestamp(to_char(systimestamp,'yyyymmdd') || '18' || to_char(systimestamp,'mi'), 'yyyymmddhh24mi')
end eot
from dual
)
, office_hours (start_time, end_time) as
( select * from time_range
union all
select start_time+interval '15' minute, end_time
from office_hours
where start_time < end_time
)
select to_char(start_time, 'hh.mi am')
, case when 60 * extract(hour from cast( start_time as timestamp))
+ extract(minute from cast( start_time as timestamp)) <= 14*60
then 'first shift'
else 'second shift'
end shift
from office_hours;
What it does:
The work of the query is done in 2 CTEs and a select using them:
time_range: determines the start and end times necessary given the
current time (on the server).
office_hours: a recursive CTE that computes the 15 minute intervals
for start to end times.
Main: Select the start of the range and determines shift.
See fiddle for example. Note: In its current form the query will always return at least 1 row. If run after the end time, it returns a row indication that time. There are 2 additional queries with a slight modification allowing actual specification of run time instead of getting that from the system. for these I have "set the runtime" in them to 07:00 and 09:02 respectively.

Get count of matching time ranges for every minute of the day in Postgres

Problem
I have a table of records each containing id, in_datetime, and out_datetime. A record is considered "open" during the time between the in_datetime and out_datetime. I want to know how many time records were "open" for each minute of the day (regardless of date). For example, for the last 90 days I want to know how many records were "open" at 3:14 am, then 3:15 am, then 3:16 am, then... If no records were "open" at 2:00 am the query should return 0 or null instead of excluding the row, thus 1440 rows should always be returned (the number of minutes in a day). Datetimes are stored in UTC and need to be cast to a time zone.
Simplified example graphic
record_id | time_range
| 0123456789 (these are minutes past midnight)
1 | =========
2 | ===
3 | =======
4 | ===
5 | ==
______________________
result 3323343210
Desired output
time | count of open records at this time
00:00 120
00:01 135
00:02 132
...
23:57 57
23:58 62
23:59 60
No more than 1440 records would ever be returned as there are only 1440 minutes in the day.
What I've tried
1.) In a subquery, I currently generate a minutely series of times for the entire range of each time record. I then group those by time and get a count of the records per minute.
Here is a db-fiddle using my current query:
select
trs.minutes,
count(trs.minutes)
from (
select
generate_series(
DATE_TRUNC('minute', (time_records.in_datetime::timestamptz AT TIME ZONE 'America/Denver')),
DATE_TRUNC('minute', (time_records.out_datetime::timestamptz AT TIME ZONE 'America/Denver')),
interval '1 min'
)::time as minutes
from
time_records
) trs
group by
trs.minutes
This works but is quite inefficient and takes several seconds to run due to the size of my table. Additionally, it excludes times when no records were open. I think somehow I could use window functions to count the number of overlapping time records for each minute of the day, but I don't quite understand how to do that.
2.) Modifying Gordon Linoff's query in his answer below, I came to this (db-fiddle link):
with tr as (
select
date_trunc('minute', (tr.in_datetime::timestamptz AT TIME ZONE 'America/Denver'))::time as m,
1 as inc
from
time_records tr
union all
select
(date_trunc('minute', (tr.out_datetime::timestamptz AT TIME ZONE 'America/Denver')) + interval '1 minute')::time as m,
-1 as inc
from
time_records tr
union all
select
minutes::time,
0
from
generate_series(timestamp '2000-01-01 00:00', timestamp '2000-01-01 23:59', interval '1 min') as minutes
)
select
m,
sum(inc) as changes_at_inc,
sum(sum(inc)) over (order by m) as running_count
from
tr
where
m is not null
group by
m
order by
m;
This runs reasonably quickly, but towards the end of the day (about 22:00 onwards in the linked example) the values turn negative for some reason. Additionally, this query doesn't seem to work correctly with records with time ranges that cross over midnight. It's a step in the right direction, but I unfortunately don't understand it enough to improve on it further.
Here is a faster method. Generate "in" and "out" records for when something gets counted. Then aggregate and use a running sum.
To get all minutes, throw in a generate_series() for the time period in question:
with tr as (
select date_trunc('minute', (tr.in_datetime::timestamptz AT TIME ZONE 'America/Denver')) as m,
1 as inc
from time_records tr
union all
select date_trunc('minute', (tr.out_datetime::timestamptz AT TIME ZONE 'America/Denver')) + interval '1 minute' as m,
-1 as inc
from time_records tr
union all
select generate_series(date_trunc('minute',
min(tr.in_datetime::timestamptz AT TIME ZONE 'America/Denver')),
date_trunc('minute',
max(tr.out_datetime::timestamptz AT TIME ZONE 'America/Denver')),
interval '1 minute'
), 0
from time_records tr
)
select m,
sum(inc) as changes_at_inc,
sum(sum(inc)) over (order by m) as running_count
from tr
group by m
order by m;

Counting readmissions in postgresql

I have a table containing data for a prison facility, of the following format:
Prisoner_id admission date discharge date
---------------------------------------------------
1325 06/13/2014 09/13/2014
1266 05/01/2014 07/02/2014
1325 02/21/2015 07/23/2015
1471 02/26/2014 04/20/2014
1266 10/19/2014 12/22/2014
1325 10/09/2015 11/10/2015
I need to count the number of readmissions of each prisoner; that is, how many times each prisoner has been admitted again to the facility, such that the difference between his admission date (date he entered) the last time he entered the facility and his discharge date (date he was let go) the time before the last is less than 60 days.
This means that if the same prisoner has been admitted 2 times, we count this as 1 readmission if the difference between his admission date of the second time and his discharge date of the first time is less than 60 days.
Moreover, if a prisoner has been admitted 3 times, we count this as 2 readmissions if the difference between his discharge date the third time and his admission date the second time AND the difference between his discharge date the second time and his admission date the first time are both less than 60 days. If one of them is less than 60 days but the other is not, count as 1 readmission. If none of them is less than 60 days, count as zero readmissions.
How can I do this in SQL or PostgreSQL? Your help is really appreciated.
I think you just want lag() and some query logic:
The following gets the groups:
select t.prisoner_id,
sum( (prev_dd > admission_date - interval '60 day')::int ) as num_readmissions
from (select t.*,
lag(discharge_date) over (partition by prisoner_id) as prev_dd
from t
) t
group by prisoner_id;

filter time periods in redshift

how to filter time period from datetime column in sql.
have a table with product, date time and quantity.
date time from 00 hrs to 24 hrs , but requirement is to filter give time range eg from 08:05 to 14:25 , Please suggest
if this is a sort key, then first you need to filter the date range and then the time range to ensure you get the benefit of the sort key, e.g.
WHERE purchase_time > '2017-10-01' AND DATE_PART('hour', purchase_time) BETWEEN 8 and 9
If you need to be more granular you could do something like:
WHERE purchase_time > '2017-10-01' AND (DATE_PART('hour', purchase_time) * 100 + DATE_PART('minute', purchase_time)) BETWEEN 805 and 1425

How to write SQL queries to calculate aggregate server up-time?

I have a requirement in SQL where I have data of a server start stop time on daily basis for one month. I need the result where it should calculate the first start time of the day, last stop time of the day, total time server was started in a day on daily basis and for servers.
Table is like below and expected output is also given.
Table:
Date & Time Reader ServerID
3/14/2016 6:36:20 AM ON 123
3/14/2016 6:58:45 AM OFF 123
3/14/2016 8:06:19 AM ON 123
3/14/2016 9:32:48 AM OFF 123
3/15/16 6:00:00 AM ON 123
3/15/16 6:01:00 AM OFF 123
3/14/2016 9:46 AM ON 124
3/14/2016 10:01 AM OFF 124
3/14/16 11:01 AM ON 124
3/14/16 12:01 PM OFF 124
Expected output
UserID FirstIN Last Out TotalInTime (min) Date
123 6:00 09:32 86 3/14
123 06:00 06:01 1 3/15
124 9:46 12:01 75 3/14
So, for each day & server, you want the minimum and maximum time and the sum of minutes "ON".
First you need rows of ON/OFF pairs (pairs on a row, not pairs of rows) whose minutes you can calculate. Then you sum the minutes and take the minimum and maximum times.
SQL Server has datepart. You can use that to compute days. To make On/Off pairs, join the table to itself along these lines:
select A.ServerID, datepart(day, A.time) as day,
A.time as ON, min(B.time) as OFF
from T as A join T as B on datepart(day, A.time) = datepart(day, B.time)
and A.Reader = 'ON' and B.Reader = 'OFF'
and A.time < B.time
group by A.ServerID, datepart(day, A.time), A.time
You can make a view like that, or a CTE, or insert the results in a temporary table. Let's call the result V.
select ServerID, day, min(ON), max(OFF)
, sum(datediff(minute, OFF, ON)) as minutes
from V
group by ServerID, day
(You can also nest the first query inside the second one.)
The trick is knowing how to find the "next" time for any pair (a question I've answered often), and how to use the server's date functions.