Oracle query to get data between specific time - sql

We are trying to find out the transactions made in between 00 to 03 AM for WHOLE YEAR.
Tried extract and to_date functions with many modification but unable to get the exact result.
Below Gives data just for current month even the date is not specified
and TXNDATE >= to_date('00:00:00', 'HH24:MI:SS')
and TXNDATE <= to_date('03:30:00','HH24:MI:SS')
Please assist.

How about just extracting the hour?
and extract(hour from TXNDATE) < 3
If you want to handle the time directly, strings might be the best approach:
to_char(txndate, 'HH24:MI:SS') >= '00:00:00' and
to_char(txndate, 'HH24:MI:SS') < '03:00:00'
If performance is an issue and you want to use an index, you can create an index incorporating to_char(txndate, 'HH24:MI:SS').

Try this:
WHERE to_char(tnxdate, 'HH24:MI:SS') between '00:00:00' and '03:30:00'

You can do this using 'EXTRACT' but you have to be careful about coding the edge conditions correctly. Here's an example:
WITH cteTxndates AS (SELECT TO_DATE('01-JAN-2018 00:00:00', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('01-JAN-2018 00:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('05-FEB-2018 00:01:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('10-MAR-2018 00:10:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('15-MAR-2018 01:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('20-APR-2018 10:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL --
SELECT TO_DATE('25-MAY-2018 02:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('30-JUN-2018 03:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('02-JUL-2018 10:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL --
SELECT TO_DATE('20-AUG-2018 01:00:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('03-SEP-2018 02:30:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('30-OCT-2018 03:30:00', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('30-OCT-2018 03:30:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL --
SELECT TO_DATE('04-NOV-2018 02:22:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL UNION ALL -- *
SELECT TO_DATE('14-DEC-2018 01:11:01', 'DD-MON-YYYY HH24:MI:SS') AS TXNDATE FROM DUAL), -- *
cteDatefields AS (SELECT TXNDATE,
EXTRACT(HOUR FROM CAST(TXNDATE AS TIMESTAMP)) AS XHOUR,
EXTRACT(MINUTE FROM CAST(TXNDATE AS TIMESTAMP)) AS XMINUTE,
EXTRACT(SECOND FROM CAST(TXNDATE AS TIMESTAMP)) AS XSECOND
FROM cteTxndates)
SELECT *
FROM cteDatefields
WHERE XHOUR >= 0 AND
( XHOUR < 3 OR
(XHOUR = 3 AND XMINUTE < 30) OR
(XHOUR = 3 AND XMINUTE = 30 AND XSECOND = 0));
dbfiddle here

Related

Column based on time range in Oracle

I have a sales table with created datetime, my business hours are from 9 AM to 2 AM in the night on the following day. I am trying to convert the dates into my business date.
01/08/22 09:39:12.000000000 AM +04:00
Lets say I have a sale at 1 AM, this sale has to be considered in the previous day.
Any function that can help me solve this issue would be appreciated
It might be a bit of an overkill, but you could just use EXTRACT:
WITH dat AS
(
SELECT to_date('01/08/22 09:39:12','DD/MM/YY HH24:MI:SS') AS t_stmp FROM dual UNION ALL
SELECT to_date('02/08/22 01:03:15','DD/MM/YY HH24:MI:SS') FROM dual UNION ALL
SELECT to_date('02/08/22 08:27:33','DD/MM/YY HH24:MI:SS') FROM dual UNION ALL
SELECT to_date('02/08/22 14:11:51','DD/MM/YY HH24:MI:SS') FROM dual UNION ALL
SELECT to_date('02/08/22 02:01:15','DD/MM/YY HH24:MI:SS') FROM dual
)
SELECT CASE WHEN EXTRACT(HOUR FROM CAST(t_stmp AS TIMESTAMP)) BETWEEN 2 AND 8 THEN -1
ELSE 0
END + TRUNC(t_stmp,'DD') AS business_date
FROM dat;
business_date
01.08.2022
02.08.2022
01.08.2022
02.08.2022
01.08.2022
It looks like you just need to make a 2 hour shift to get your sales in the right date. You can add or substract hours from DATE/DATETIME/TIMESTAMP data type. If your column is TIMESTAMP then it would be like this:
-- when selecting data for date of sales
SELECT TRUNC(your_column_name - INTERVAL '2' HOUR, 'dd') "SALE_DATE"
-- And/Or
WHERE TRUNC(your_column_name - INTERVAL '2' HOUR, 'dd') = :DATE_OF_SALES
-- TRUNC function always returns DATE datatype
--
-- The opposite conversion would be
CAST(your_datetime_column + INTERVAL '2' HOUR as TIMESTAMP) ...
Here is the small sample with result:
SELECT
to_char(SYSDATE, 'dd.mm.yyyy hh24:mi:ss') "DATETIME",
to_char(SYSDATE - INTERVAL '2' HOUR, 'dd.mm.yyyy hh24:mi:ss') "DATETIME_MINUS_2H",
to_char(SYSDATE + INTERVAL '2' HOUR, 'dd.mm.yyyy hh24:mi:ss') "DATETIME_PLUS_2H",
to_char(SYSDATE - INTERVAL '10' HOUR, 'dd.mm.yyyy hh24:mi:ss') "DATETIME_MINUS_10H"
FROM
DUAL
--
-- R e s u l t
--
-- DATETIME DATETIME_MINUS_2H DATETIME_PLUS_2H DATETIME_MINUS_10H
-- ------------------- ------------------- ------------------- -------------------
-- 07.08.2022 09:58:38 07.08.2022 07:58:38 07.08.2022 11:58:38 06.08.2022 23:58:38
The last column now has the date from day before.

Group by singles chute

How can I create an overview without duplicates in column "CHUTE"?
Eg in below result: AX002 = 129
select
COUNT(PPL_SDCC),
substr (PPL_DISCHARGEID,6,5) as CHUTE
from T1LOG.PPL_PIECELOG
where
PPL_DISCHARGEID like 'PS%X%'
and substr (PPL_SDCC,11,1)='1'
and PPL_DISCHARGETIME between TO_DATE ('01/08/2021 10:00:00' , 'DD/MM/YYYY hh24:mi:ss') and TO_DATE ('02/08/2021 09:00:00' , 'DD/MM/YYYY hh24:mi:ss')
group by PPL_DISCHARGEID
order by CHUTE
RESULT:
Rahther than grouping only column PPL_DISCHARGEID, You should actually include the exact calculation in group by clause. Please update your group by clause in your query to -
SELECT COUNT(PPL_SDCC),
SUBSTR(PPL_DISCHARGEID,6,5) as CHUTE
FROM T1LOG.PPL_PIECELOG
WHERE PPL_DISCHARGEID like 'PS%X%'
AND SUBSTR(PPL_SDCC,11,1)='1'
AND PPL_DISCHARGETIME BETWEEN TO_DATE('01/08/2021 10:00:00', 'DD/MM/YYYY hh24:mi:ss') and TO_DATE('02/08/2021 09:00:00', 'DD/MM/YYYY hh24:mi:ss')
GROUP BY SUBSTR(PPL_DISCHARGEID,6,5)
ORDER BY CHUTE;

find overlap between two sets of date ranges

I would like to find the overlap between two sets of date ranges for every order number using Oracle SQL or PL/SQL.
The inputs are "result set one" and "result set two". The output should be "overlap".
result set one
WITH T_RESULT_SET_ONE as(
select 21365 order_number,to_date('01/01/2021 09:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 10:30:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21365 order_number,to_date('02/01/2021 14:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('02/01/2021 18:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 08:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:43:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 16:34:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 18:15:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21367 order_number,to_date('04/01/2021 15:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('04/01/2021 16:15:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
)
or
result set two
T_RESULT_SET_TWO as(
select 21365 order_number,to_date('01/01/2021 09:30:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:45:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21365 order_number,to_date('02/01/2021 13:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('02/01/2021 17:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21367 order_number,to_date('01/01/2021 09:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 10:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 16:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 19:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('05/01/2021 19:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('04/01/2021 19:46:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
)
or
overlap
T_OVERLAP as
(
select 21365 order_number,to_date('01/01/2021 09:30:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:45:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21365 order_number,to_date('02/01/2021 14:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('02/01/2021 17:00:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
union
select 21367 order_number,to_date('01/01/2021 09:00:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 09:43:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
UNION
select 21367 order_number,to_date('01/01/2021 16:34:00', 'DD/MM/YYYY HH24:MI:SS') start_date_time, to_date('01/01/2021 18:15:00', 'DD/MM/YYYY HH24:MI:SS') finish_date_time FROM DUAL
)
or
The following image ilustrates the operation I am trying to execute (the date ranges are not the same as the ones I provided earlier)
Could anyone provide a SQL query or PL/SQL program that does that?
It seems what you are looking for is not INTERSECT but overlaps. In Oracle intersect generally refers to the common result of 2 queries:
Select <columns list> from table1
INTERSECT
Select <columns list> from table2;
Where the column lists have the same definition and the resulting values are the same. What you are looking for is where the values overlap one another each other not where the rows contain same values.
Lets consider 2 events call then 'A' and 'B', there are 4 possibilities for overlap:
A starts, B starts, B ends, A ends. A completely overlaps B.
A starts, B starts, A ends, B ends. A overlaps beginning of B
B starts, A starts, B ends, A ends. A overlaps ending of B
B starts, A starts, A ends, B ends. A is completely overlap by B.
Resolving is just determining is needs to determine the overlap we take the greatest start time and the least end time. With the data you provided this requires just one of the above:
select order_number
, greatest(t1start, t2start) start_date_time
, least(t1finish,t2finish) finish_date_time
from ( select t1.order_number
, t1.start_date_time t1start
, t1.finish_date_time t1finish
, t2.start_date_time t2start
, t2.finish_date_time t2finish
from t_result_set_one t1
join t_result_set_two t2
on t1.order_number = t2.order_number
where ( t1.finish_date_time >= t2.start_date_time
and t1.start_date_time <= t2.finish_date_time
)
);
See fiddle here. I leave the other 3 possibilities for you.

Union times in sql oracle - difficult problem

I have a big table with start times and end times.
It looks like this:
Start_time date-time (format: dd/mm/yyyy hh24:mi:ss),
End_time date-time (format: dd/mm/yyyy hh24:mi:ss)
I might have rows that represent time which is included in other rows
My desiarable result is a table that can solve this containing. I want take any firsy time and see next to him the last end time.
I tried to left join the table with itself on start time between start time and end time if the end of the second is greater than the ending of the first. Then to do a sliding window and take the max end time with sliding window or even with group by.
However, this idea does take in account then I may have, for example:
10:05-10:10
10:07-10:12
10:09-10:15
10:11-10:20
So when I am joined I allegedly get 10:05-10:15 and 10:11-10:20. The row of 10:11 is not joined to the first row because it is not included in that time.
I have here again the same problem I had in the begining.
My desiarable result is actually for the rows above:
10:05-10:20
Seem to be a difficult problem.
I dont know plsql but thought maybe about doing some function that repeat this query until it has nothing to join?
Hope to get ypur help!
Thanks.
I dont know how to format, but you can copy paste in your editor and than format.
Insted of my test data with operator "with" you may use your table.
I suppose you have some sort of ID so i include it:
with test_table as (
select 1 id, to_date('2019-04-07 10:05', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-07 10:08', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 2 id, to_date('2019-04-07 10:07', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-07 10:10', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 3 id, to_date('2019-04-07 10:11', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-07 10:15', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 4 id, to_date('2019-04-07 10:12', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-07 10:20', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 5 id, to_date('2019-04-08 10:05', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-08 10:10', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 6 id, to_date('2019-04-08 10:07', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-08 10:12', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 7 id, to_date('2019-04-08 10:09', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-08 10:15', 'yyyy-mm-dd hh24:mi') end_time from dual union all
select 8 id, to_date('2019-04-08 10:11', 'yyyy-mm-dd hh24:mi') start_time, to_date('2019-04-08 10:20', 'yyyy-mm-dd hh24:mi') end_time from dual
)
select id, to_char(start_time, 'yyyy-mm-dd hh24:mi') start_time, to_char(end_time, 'yyyy-mm-dd hh24:mi') end_time,
(SELECT MAX(to_char(end_time, 'yyyy-mm-dd hh24:mi'))
from test_table t2
connect by nocycle
prior t2.id != t2.id and
PRIOR end_time > start_time and
PRIOR start_time < end_time
start with t2.id = t1.id) max_date
from test_table t1;

Closest Date to a given date - SQL Oracle

Oracle (SQL) - I have 3 available dates in a month (1st, 10th and 25th). I need a query to find out the closest among the 3 dates based on the date of executing my query. For e.g, when i run the query on 4th, i should get 10th as my result, when i run on 12th, the result should be 25th and when i run on 27th, the result should be the 01st of next month.
I am struggling with the logic. Please help..
with
inputs ( dt ) as (
select to_date( '03/24/2015 11:30:00', 'mm/dd/yyyy hh24:mi:ss') from dual union all
select to_date( '08/03/2016 07:15:00', 'mm/dd/yyyy hh24:mi:ss') from dual union all
select to_date( '02/29/2016 22:30:00', 'mm/dd/yyyy hh24:mi:ss') from dual
)
-- End of simulated inputs (for testing only, not part of the solution).
-- SQL query begins BELOW THIS LINE. Use your actual table and column names.
select dt,
case when extract(day from dt) < 10 then trunc(dt, 'mm') + interval '9' day
when extract(day from dt) < 25 then trunc(dt, 'mm') + interval '24' day
else add_months(trunc(dt, 'mm'), 1)
end as next_std_dt
from inputs;
DT NEXT_STD_DT
------------------- -------------------
03/24/2015 11:30:00 03/25/2015 00:00:00
08/03/2016 07:15:00 08/10/2016 00:00:00
02/29/2016 22:30:00 03/01/2016 00:00:00
I believe this is much more efficient and simpler than the other solutions.
WITH
possible_dates
AS
-- generate the three available dates for the current month
(SELECT TRUNC (SYSDATE, 'MM') available_date
FROM DUAL
UNION ALL
SELECT TRUNC (SYSDATE, 'MM') + 9
FROM DUAL
UNION ALL
SELECT TRUNC (SYSDATE, 'MM') + 24
FROM DUAL
UNION ALL
SELECT ADD_MONTHS (TRUNC (SYSDATE, 'MM'), 1)
FROM DUAL),
delta
AS
-- calculate the distance of those available dates
(SELECT (available_date - SYSDATE) diff, available_date
FROM possible_dates)
SELECT *
FROM delta
WHERE diff = (SELECT MIN (diff)
FROM delta
WHERE diff >= 0);
If using PL SQL is an option, then use the query as following:
`DECLARE
curr_month CHAR(2);
curr_year CHAR(4);
future_date DATE;
BEGIN
select to_char(sysdate, 'MM') INTO curr_month from dual;
select to_char(sysdate, 'YYYY') INTO curr_year from dual;
future_date := TO_DATE('12' || curr_month || curr_year, 'DD/MM/YYYY');
IF (SYSDATE > future_date) THEN
{..whatever you want to do...}
ELSIF (SYSDATE > future_date2) THEN
{..whatever you want to do...}
END IF;
END;`
WITH mytable(dt) AS
(SELECT '01'
FROM dual
UNION ALL SELECT '10'
FROM dual
UNION ALL SELECT '25'
FROM dual),
given_dates AS
(SELECT Trunc (To_date (dt || To_char(sysdate, 'MMYYYY'), 'DDMMYYYY')) dt,
Trunc(sysdate) cdate
FROM mytable),
comp AS
(SELECT cdate,
CASE
WHEN ABS (cdate - dt) < ABS (cdate - Add_months (dt, 1)) THEN dt
ELSE Add_months (dt, 1)
END dt_comp
FROM given_dates)
SELECT dt_comp closest_date
FROM
(SELECT dt_comp,
rank() OVER (
ORDER BY ABS (cdate - dt_comp)) rn
FROM comp)
WHERE rn = 1;