Oracle copying rows into same table with different dates - sql

I have a schedule where dates can cross midnight. In the example you can see dates for 08/23 & 08/24.
How can I copy these rows into the same table but with different dates.
For example the row with start_date 08/23 I want to be 08/28. The row with start_date 08/24 should be the next day 08/29.
Note the situation where the start_date=08/23 and end_date=08/24
Thanks in advance to all that answer.
Desired output
SCHEDULE_ID LOCATION_ID START_DATE END_DATE
22 1 08232021 23:50:00 08232021 23:54:00
22 1 08232021 23:56:00 08242021 00:02:00
22 2 08242021 00:05:00 08242021 00:09:00
22 1 08282021 23:50:00 08282021 23:54:00
22 1 08282021 23:56:00 08292021 00:02:00
22 2 08292021 00:05:00 08292021 00:09:00
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
create table schedule(
schedule_id NUMBER(4),
location_id number(4),
start_date DATE,
end_date DATE,
check (start_date=trunc(start_date,'MI')),
check (end_date=trunc(end_date,'MI'))
);
insert into schedule(
schedule_id,
location_id,
start_date,
end_date
)
VALUES (22,1,TO_DATE('2021/08/23 23:50:00', 'yyyy/mm/dd hh24:mi:ss'),TO_DATE('2021/08/23 23:54:00', 'yyyy/mm/dd hh24:mi:ss'));
insert into schedule(
schedule_id,
location_id,
start_date,
end_date
)
VALUES (22,1,TO_DATE('2021/08/23 23:56:00', 'yyyy/mm/dd hh24:mi:ss'),TO_DATE('2021/08/24 00:02:00', 'yyyy/mm/dd hh24:mi:ss'));
insert into schedule(
schedule_id,
location_id,
start_date,
end_date
)
VALUES (22,2,TO_DATE('2021/08/24 00:05:00', 'yyyy/mm/dd hh24:mi:ss'),
TO_DATE('2021/08/24 00:09:00', 'yyyy/mm/dd hh24:mi:ss'));

Are you doing this to generate more data, perhaps for testing?
It should be something like
insert into schedule
select schedule_id, location_id, start_date + 5, end_date + 5
from schedule
;
The fact that some date intervals straddle over midnight is irrelevant.

Related

List of dates between two tables in oracle

Problem
Input:
START_DATE END_DATE
01-FEB-16 03-FEB-16
01-FEB-16 02-FEB-16
10-FEB-16 11-FEB-16
I want to generate all the dates between the start_day and end_day as
Output
01-FEB-16
02-FEB-16
03-FEB-16
10-FEB-16
11-FEB-16
You could do it using Row Generator technique.
Setup
SQL> CREATE TABLE t
2 (START_DATE DATE, END_DATE DATE
3 );
Table created.
SQL> INSERT INTO t VALUES(DATE '2016-02-01', DATE '2016-02-03');
1 row created.
SQL> INSERT INTO t VALUES(DATE '2016-02-01', DATE '2016-02-02');
1 row created.
SQL> INSERT INTO t VALUES(DATE '2016-02-10', DATE '2016-02-11');
1 row created.
SQL> COMMIT;
Commit complete.
Query
SQL> SELECT DISTINCT TO_CHAR(START_DATE+LEVEL-1, 'DD-MON-YYYY') the_date
2 FROM t
3 CONNECT BY LEVEL <= END_DATE-START_DATE+1
4 ORDER BY the_date
5 /
THE_DATE
-----------
01-FEB-2016
02-FEB-2016
03-FEB-2016
10-FEB-2016
11-FEB-2016
SQL>

Connect by and level - hour interval

I have the following data:
ID SESSION START_DATE END_DATE
1 A 01/01/2016 22:35 02/01/2016 02:35
1 B 02/01/2016 02:35 02/01/2016 04:45
2 A 01/01/2016 00:00 01/01/2016 02:00
2 B 01/01/2016 02:00 01/01/2016 03:30
And I need to return like this:
ID SESSION START_DATE END_DATE
1 A 01/01/2016 22:35 01/01/2016 22:59
1 A 01/01/2016 23:00 01/01/2016 23:59
1 A 02/01/2016 00:00 02/01/2016 00:59
1 A 02/01/2016 01:00 02/01/2016 01:59
1 A 02/01/2016 02:00 02/01/2016 02:35
1 B 02/01/2016 02:35 02/01/2016 02:59
1 B 02/01/2016 03:00 02/01/2016 03:59
1 B 02/01/2016 04:00 02/01/2016 04:45
2 A 01/01/2016 00:00 01/01/2016 00:59
2 A ...
any help?
You can try something like this:
SELECT DISTINCT session,
id,
st,
end
FROM ( SELECT DECODE(LEVEL, 1, start_date, TRUNC(start_date, 'HH') + (LEVEL - 1) / 24) st,
PRIOR id p_id,
id,
PRIOR session p_session,
session,
LEAST(end_date, TRUNC(start_date, 'HH') + (LEVEL) / 24 - 1 / 24 / 60) end
FROM tab
CONNECT BY LEVEL <= EXTRACT(HOUR FROM end_date - start_date) + 1)
WHERE ( p_id = id
AND p_session = session)
OR (p_id IS NULL)
I would like to avoid the usage of DISTINCT, but could not find a way, hope someone else could give a better solution
Here is a solution, you just have to play a little with hour extractions:
create table connect_by_hour_test(id number, sess varchar2(1), start_date date, end_date date);
insert into connect_by_hour_test (id, sess, start_date, end_date) values (1, 'A', to_date('01/01/2016 22:30', 'dd/mm/yyyy hh24:mi'), to_date('02/01/2016 02:35', 'dd/mm/yyyy hh24:mi'));
insert into connect_by_hour_test (id, sess, start_date, end_date) values (1, 'B', to_date('02/01/2016 02:35', 'dd/mm/yyyy hh24:mi'), to_date('02/01/2016 04:45', 'dd/mm/yyyy hh24:mi'));
insert into connect_by_hour_test (id, sess, start_date, end_date) values (2, 'A', to_date('01/01/2016 00:00', 'dd/mm/yyyy hh24:mi'), to_date('01/01/2016 02:00', 'dd/mm/yyyy hh24:mi'));
insert into connect_by_hour_test (id, sess, start_date, end_date) values (2, 'B', to_date('01/01/2016 02:00', 'dd/mm/yyyy hh24:mi'), to_date('01/01/2016 03:30', 'dd/mm/yyyy hh24:mi'));
commit;
with max_level as (
select id, sess, ceil((end_date - start_date) / (1/24)) as val from connect_by_hour_test ) -- get the max number of iterations for each (id, sess) pair
select distinct
id,
sess,
decode(level, 1, to_char(start_date, 'dd/mm/yyyy hh24:mi'), to_char(trunc(start_date, 'hh24') + 1/24 * level - 1/24, 'dd/mm/yyyy hh24:mi')) start_date, -- if first iteration in pair show the start_date, otherwise show the start date, plus the number of hours for the current iteration(first iteration - 1 hour, 2nd iteration - two hours, etc) minus one minute
decode(level, (select val from max_level ml where ml.id = src.id and ml.sess = src.sess), to_char(end_date, 'dd/mm/yyyy hh24:mi'), to_char(trunc(start_date, 'hh24') + 1/24 * level - 1/24/60, 'dd/mm/yyyy hh24:mi')) end_date -- if last iteration show end_date, otherwise show value from previous iteration plus one minute
from
connect_by_hour_test src
connect by
trunc(start_date, 'hh24') + 1/24 * (level - 1) <= end_date order by 1,2;
Edit: Corrections made after feedback, added step to be level - 1 and max_level calculated as ceil, instead of trunc, of end_date - start_date
select level,
NR_ATENDIMENTO,
CD_SETOR_ATENDIMENTO,
greatest(DT_ENTRADA_UNIDADE, trunc(DT_ENTRADA_UNIDADE+(level-1)/24, 'hh24')) DT_ENTRADA_UNIDADE,
to_char(trunc(DT_ENTRADA_UNIDADE, 'hh24') + 1/24 * level - 1/24/60, 'dd/mm/yyyy hh24:mi:ss') DT_SAIDA_UNIDADE
from
(
SELECT
CD_SETOR_ATENDIMENTO,
NR_SEQ_INTERNO,
DT_ENTRADA_UNIDADE,
nvl(DT_SAIDA_UNIDADE,sysdate) DT_SAIDA_UNIDADE,
NR_ATENDIMENTO
FROM ATEND_PACIENTE_UNIDADE
WHERE OBTER_TIPO_UNIDADE_ATEND(NR_ATENDIMENTO, NR_SEQ_INTERNO, IE_PASSAGEM_SETOR) NOT IN ('S')
AND nr_atendimento = 831838
ORDER BY NR_ATENDIMENTO,
CD_SETOR_ATENDIMENTO,
DT_ENTRADA_UNIDADE DESC
)
WHERE 1=1
CONNECT BY ROWID=PRIOR ROWID
and trunc(DT_ENTRADA_UNIDADE, 'hh24') + 1/24 * (level - 1) <= nvl(DT_SAIDA_UNIDADE,sysdate)
AND PRIOR SYS_GUID() IS NOT NULL
order by 2,3,4;

How to create day start and end date in PL SQL

What I am trying to do is to create two timestamps a StartDate timestamp which will be 09/08/2015 00:00:00 and an EndDate time stamp which should be 09/08/2015 23:59:59 as easy as it is to achieve in MS SQL, I have not been able to find a Make_Date function or Add_Days function to get either of the timestamps in Oracle PL SQL.
Can anyone help me out?
Rather than using fractional numbers 86399 / 86400 (which requires some working out when reviewing the code to see why you picked those magic numbers) to get the end date you can explicitly state the time periods using INTERVALS (which is easy to see at a glance what you are doing):
SQL Fiddle
Oracle 11g R2 Schema Setup:
Query 1:
SELECT TRUNC( CURRENT_DATE ) AS START_DATE,
TRUNC( CURRENT_DATE ) + INTERVAL '1' DAY - INTERVAL '1' SECOND AS END_DATE
FROM DUAL
Results:
| START_DATE | END_DATE |
|-----------------------------|-----------------------------|
| September, 08 2015 00:00:00 | September, 08 2015 23:59:59 |
Use TO_DATE to convert string into DATE.
SQL> alter session set nls_date_format='mm/dd/yyyy hh24:mi:ss';
Session altered.
SQL> SELECT to_date('09/08/2015 00:00:00' ,'mm/dd/yyyy hh24:mi:ss') start_date,
2 to_date('09/08/2015 23:59:59' ,'mm/dd/yyyy hh24:mi:ss') end_date
3 FROM dual;
START_DATE END_DATE
------------------- -------------------
09/08/2015 00:00:00 09/08/2015 23:59:59
SQL>
You could also use the ANSI TIMESTAMP Literal.
SQL> SELECT TIMESTAMP '2015-08-09 00:00:00' start_date,
2 TIMESTAMP '2015-08-09 23:59:59' end_date
3 FROM dual;
START_DATE END_DATE
---------------------------- -------------------------------
09-AUG-15 12.00.00.000000000 09-AUG-15 11.59.59.000000000 PM
SQL>
Update OP wants the date literal to be dynamic.
SQL> SELECT TRUNC(SYSDATE) start_date,
2 TRUNC(SYSDATE) + 86399 / 86400 end_date
3 FROM dual;
START_DATE END_DATE
------------------- -------------------
09/08/2015 00:00:00 09/08/2015 23:59:59
Update 2 OP wants to know why the time part is hidden in the date.
SQL> alter session set nls_date_format='mm/dd/yyyy';
Session altered.
SQL> SELECT sysdate FROM DUAL;
SYSDATE
----------
09/08/2015
SQL> alter session set nls_date_format='mm/dd/yyyy hh24:mi:ss';
Session altered.
SQL> SELECT sysdate FROM DUAL;
SYSDATE
-------------------
09/08/2015 15:46:14
So, what happened above? The same SYSDATE returns two different values. The reason is that the DATE has both datetime elements, what you see depends on the display properties driven by your locale-specific NLS settings.
Use TO_CHAR to convert the date into string to display it in your
desired format.
Using values from table:
SELECT
DATE_VALUE,
TRUNC(DATE_VALUE) START_DATE,
TRUNC(DATE_VALUE) + 86399 / 86400 END_DATE
FROM
(SELECT SYSDATE - LEVEL + 1 DATE_VALUE FROM DUAL CONNECT BY LEVEL <= 10)

Previous Weekdays

I have a requirement in which i have to find start and end date.
Start date is First sat of the previous month of created date and end date is previous friday of created date.
Eg Below .. I am passing created date and need to derive start and end date like this below.
CREATED_DT Start_date end_date
04/08/2015 15:36 04/07/2015 00:00 31/07/2015 23:59
07/07/2015 15:32 06/06/2015 00:00 03/07/2015 23:59
You should not depend on the locale-specific NLS settings.
You could use following functions:
NEXT_DAY
ADD_MONTHS
TRUNC
For example,
SQL> alter session set nls_date_format='DD/MM/YYYY HH24:MI:SS';
SQL> WITH t(created_dt) AS(
2 SELECT to_date('04/08/2015 15:36','DD/MM/YYYY HH24:MI') FROM DUAL UNION ALL
3 SELECT to_date('07/07/2015 15:32','DD/MM/YYYY HH24:MI') FROM DUAL
4 )
5 SELECT CREATED_DT,
6 NEXT_DAY(TRUNC(add_months(created_dt, -1),'MM') -1,TO_CHAR(to_date('6','J'),'Day')) -1 start_date,
7 NEXT_DAY(TRUNC(created_dt, 'MM') -1, TO_CHAR(to_date('5','J'),'Day')) -1 + 0.99999 AS end_date
8 FROM t;
CREATED_DT START_DATE END_DATE
------------------- ------------------- -------------------
04/08/2015 15:36:00 04/07/2015 00:00:00 31/07/2015 23:59:59
07/07/2015 15:32:00 06/06/2015 00:00:00 03/07/2015 23:59:59
SQL>
To get the time portion as 23:59:59, you could either add 0.99999 or subtract INTERVAL '1' SECOND. For example,
SQL> alter session set nls_date_format='DD/MM/YYYY HH24:MI:SS';
Session altered.
SQL> WITH t(created_dt) AS(
2 SELECT to_date('04/08/2015 15:36','DD/MM/YYYY HH24:MI') FROM DUAL UNION ALL
3 SELECT to_date('07/07/2015 15:32','DD/MM/YYYY HH24:MI') FROM DUAL
4 )
5 SELECT CREATED_DT,
6 NEXT_DAY(TRUNC(add_months(created_dt, -1),'MM') -1,TO_CHAR(to_date('6','J'),'Day')) -1 start_date,
7 NEXT_DAY(TRUNC(created_dt, 'MM') -1, TO_CHAR(to_date('5','J'),'Day')) - (INTERVAL '1' SECOND) AS end_date
8 FROM t;
CREATED_DT START_DATE END_DATE
------------------- ------------------- -------------------
04/08/2015 15:36:00 04/07/2015 00:00:00 31/07/2015 23:59:59
07/07/2015 15:32:00 06/06/2015 00:00:00 03/07/2015 23:59:59
SQL>
You can use some of the Date functions. I'm giving for sysdate. Use according to your requirement.
select NEXT_DAY(trunc((trunc (add_months (sysdate, -1), 'mm')), 'MONTH')-1, 'Saturday') as Start_date,
NEXT_DAY(SYSDATE-8, 'FRIDAY') as End_date
from dual;
Output
START_DATE END_DATE
04-JUL-15 21-AUG-15
Use Next_day function. The Oracle/PLSQL NEXT_DAY function returns the first weekday that is greater than a date.
select TO_DATE('04/08/2015 15:36' ,'DD/MM/YYYY hh24:mi') as created_date,
next_day(ADD_MONTHS(TRUNC(TO_DATE('04/08/2015 15:36','DD/MM/YYYY hh24:mi')+1,'MM'),-1),'SATURDAY')
as start_date,
next_day(trunc(TO_DATE('04/08/2015 15:36','DD/MM/YYYY hh24:mi')-8)+0.99999 ,'FRIDAY')as end_date
FROM DUAL
Instead of adding 0.99999 we can also achieve same thing with 1-(1/(24*60*60)) we are adding one day after that subtracting 1 part from 24*60*60 seconds.
I have achieved by this way
end date: Where created _dt is date value what i am passing..!!
next_day(TRUNC(to_date(created_dt,'DD-MM-YYYY HH24:MI:SS'))-7,'FRIDAY') +
INTERVAL '23:59:59' HOUR TO SECOND AS range_end_dt

Credit for sales report filing time

I have two date_time fields. The first is the date_time of the sale and the second is the date_time the sales report is filed.
In order for the salesperson to obtain credit, the sales report has to be filed by midnight on the date after the sale was made.
I'm currently calculating the difference, and know that anything over 24 hours could be out of a qualifying time period. Also, there are times when the sales report is filed prior to the date_time of the sale.
I searched through previous answers and couldn't find anything similar.
You need something like:
select to_char(report_time, 'mm/dd/yyyy hh24:mi:ss') report_time,
to_char(sales_time, 'mm/dd/yyyy hh24:mi:ss') sales_time,
round((report_time - sales_time) * 24, 6) diff_in_hours,
case when report_time < trunc(sales_time) + 2 then 'Y' else 'N' end decision
from sales
Function trunc() cuts hours, minutes and seconds from sales_time. For instance for date 01/15/2015 11:59:00 it returns 01/15/2015 00:00:00.
Next we add 2 days to this result. Report_date has to be lower then this value. So:
report_time < trunc(sales_time) + 2
Test with sample data:
with sales as (select
to_date('01/15/2015 11:59:00', 'mm/dd/yyyy hh24:mi:ss') report_time,
to_date('01/15/2015 11:45:00', 'mm/dd/yyyy hh24:mi:ss') sales_time from dual
union all select
to_date('01/16/2015 23:59:00', 'mm/dd/yyyy hh24:mi:ss'),
to_date('01/15/2015 12:45:00', 'mm/dd/yyyy hh24:mi:ss') from dual
union all select
to_date('01/17/2015 00:00:01', 'mm/dd/yyyy hh24:mi:ss'),
to_date('01/15/2015 23:59:00', 'mm/dd/yyyy hh24:mi:ss') from dual)
select to_char(report_time, 'mm/dd/yyyy hh24:mi:ss') report_time,
to_char(sales_time, 'mm/dd/yyyy hh24:mi:ss') sales_time,
round((report_time - sales_time) * 24, 6) diff_in_hours,
case when report_time < trunc(sales_time) + 2 then 'Y' else 'N' end decision
from sales
Output:
REPORT_TIME SALES_TIME DIFF_IN_HOURS DECISION
------------------- ------------------- ------------- --------
01/15/2015 11:59:00 01/15/2015 11:45:00 0,233333 Y
01/16/2015 23:59:00 01/15/2015 12:45:00 35,233333 Y
01/17/2015 00:00:01 01/15/2015 23:59:00 24,016944 N