Oracle INSERT, SELECT and NOT EXISTS - sql

I have a procedure, which is working fine. There are times when the same VALUES are passed into the procedure and generates a unique KEY violation.
I know this could be easily solved by using
the usual solutions of either
LOG ERRORS INTO
clause, or to use the
/*+ ignore_row_on_dupkey_index ... */
I saw this link and was trying to implement #OMG Ponies NOT EXISTS solution by seeing if the VALUES are already in the PRIMARY KEY but after hours of research and trying different methods I was unsuccessful
Avoid duplicates in INSERT INTO SELECT query in SQL Server
I'm very curious to see how this solution could be implemented in my situation.
Thanks in advance to all who respond and for your help, patience and expertise.
My working test CASE is below (not modified) is below. BTW im testing in live SQL in case anyone wants to use the same environment.
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
create table schedule(
seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
schedule_id NUMBER(4),
location_id number(4),
base_date DATE,
start_date DATE,
end_date DATE,
constraint schedule_pk primary key (schedule_id, location_id, base_date),
CONSTRAINT start_min check (start_date=trunc(start_date,'MI')),
CONSTRAINT end_min check (end_date=trunc(end_date,'MI')),
CONSTRAINT end_gt_start CHECK (end_date >= start_date)
);
/
CREATE TABLE locations AS
SELECT level AS location_id,
'Door ' || level AS location_name,
CASE round(dbms_random.value(1,3))
WHEN 1 THEN 'A'
WHEN 2 THEN 'T'
WHEN 3 THEN 'G'
END AS location_type
FROM dual
CONNECT BY level <= 15;
ALTER TABLE locations
ADD ( CONSTRAINT locations_pk
PRIMARY KEY (location_id));
CREATE OR REPLACE PROCEDURE CREATE_SCHEDULE
(
i_schedule_id IN PLS_INTEGER,
i_base_date IN DATE,
i_offset IN PLS_INTEGER DEFAULT 0,
i_incr IN PLS_INTEGER DEFAULT 10,
i_duration IN PLS_INTEGER DEFAULT 5
)
AS
l_offset interval day to second;
l_incr interval day to second;
l_duration interval day to second;
BEGIN
l_offset :=
NUMTODSINTERVAL(i_offset, 'SECOND') ;
l_incr :=
NUMTODSINTERVAL(i_incr, 'MINUTE') ;
l_duration :=
NUMTODSINTERVAL(i_duration, 'MINUTE') ;
INSERT INTO schedule(
schedule_id
,location_id
,base_date
,start_date
,end_date
)
SELECT i_schedule_id
, l.location_id
, i_base_date
, i_base_date + l_offset
+ (l_incr * (ROWNUM - 1)) AS start_date
, i_base_date + l_offset
+ (l_incr * (ROWNUM - 1))
+ l_duration AS end_date
FROM locations l;
END;
/
EXEC CREATE_SCHEDULE(1,TRUNC(SYSDATE))

Use a MERGE statement:
CREATE OR REPLACE PROCEDURE CREATE_SCHEDULE(
i_schedule_id IN PLS_INTEGER,
i_base_date IN DATE,
i_offset IN PLS_INTEGER DEFAULT 0,
i_incr IN PLS_INTEGER DEFAULT 10,
i_duration IN PLS_INTEGER DEFAULT 5
)
AS
l_offset interval day to second;
l_incr interval day to second;
l_duration interval day to second;
BEGIN
l_offset := NUMTODSINTERVAL(i_offset, 'SECOND') ;
l_incr := NUMTODSINTERVAL(i_incr, 'MINUTE') ;
l_duration := NUMTODSINTERVAL(i_duration, 'MINUTE') ;
MERGE INTO schedule dst
USING (
SELECT i_schedule_id AS schedule_id,
l.location_id,
i_base_date AS base_date,
i_base_date + l_offset + (l_incr * (ROWNUM - 1))
AS start_date,
i_base_date + l_offset + (l_incr * (ROWNUM - 1)) + l_duration
AS end_date
FROM locations l
) src
ON ( src.schedule_id = dst.schedule_id
AND src.location_id = dst.location_id
AND src.base_date = dst.base_date
)
WHEN NOT MATCHED THEN
INSERT (
schedule_id,
location_id,
base_date,
start_date,
end_date
) VALUES (
src.schedule_id,
src.location_id,
src.base_date,
src.start_date,
src.end_date
);
END;
/

Related

Oracle SQL: Return a distinct list of days when inputting a min and max of a date field

Is the following the correct way to get a distinct list of days for a date range (min and max of a date field) I intend to create a sql view out of this:
with range as (
select min(date) start_date,
max(date) end_date
from table
)
select start_date + level - 1 AS "DATE",
extract(month from start_date + level - 1) AS "MONTH",
extract(year from start_date + level - 1) AS "YEAR"
from range
connect by level <= (
trunc(end_date) - trunc(start_date) + 1
);
Do you really need to create a DATE table when you can generate one on the fly if needed
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY';
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
/
CREATE OR REPLACE FUNCTION generate_dates_pipelined(
p_from IN DATE,
p_to IN DATE
)
RETURN nt_date PIPELINED DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
BEGIN
LOOP
PIPE ROW (v_start);
EXIT WHEN v_start >= v_end;
v_start := v_start + INTERVAL '1'DAY;
END LOOP;
RETURN;
END generate_dates_pipelined;
/
SELECT
c.COLUMN_VALUE
FROM
TABLE(generate_dates_pipelined(DATE '2022-07-01',
DATE '2022-07-31')) c

Add days/hours/minutes and months to time stamp

Is there a way to add days hours months and minutes into a timestamp that i want to insert the value of into a variable
I have separate variables for minutes,hours,days and months that I want to add to the timestamp
the timestamp format goes like this '04-FEB-21 10.25.12.013000 AM'
I tried using SELECT TO_TIMESTAMP(datecreated,'dd-mon-yyyy hh.mi.ss AM') + daysvar into duedate FROM dual; but it returns the error AM/A.M. or PM/P.M. required
daysvar contains the amount of days to be added to the timestamp
Thank you!
I have separate variables for minutes,hours,days and months that I want to add to the timestamp
Use NUMTODSINTERVAL:
DECLARE
datecreated VARCHAR2(30) := '04-FEB-21 10.25.12.013000 AM';
days INT := 1;
hours INT := 2;
minutes INT := 42;
seconds INT := 3;
duedate VARCHAR2(30);
BEGIN
duedate := TO_CHAR(
TO_TIMESTAMP(
datecreated,
'DD-MON-RR HH12.MI.SS.FF6 AM',
'NLS_DATE_LANGUAGE=American'
)
+ NUMTODSINTERVAL(days, 'DAY')
+ NUMTODSINTERVAL(hours, 'HOUR')
+ NUMTODSINTERVAL(minutes, 'MINUTE')
+ NUMTODSINTERVAL(seconds, 'SECOND'),
'DD-MON-RR HH12.MI.SS.FF6 AM',
'NLS_DATE_LANGUAGE=American'
);
DBMS_OUTPUT.PUT_LINE(duedate);
END;
/
db<>fiddle here
If you want to add months then use ADD_MONTHS; however, it returns a DATE (and not a TIMESTAMP) so you would lose the fractional seconds. You can add those back though:
DECLARE
datecreated VARCHAR2(30) := '04-FEB-21 10.25.12.013000 AM';
months INT := 4;
days INT := 1;
hours INT := 2;
minutes INT := 42;
seconds INT := 3;
created_date TIMESTAMP;
duedate VARCHAR2(30);
BEGIN
created_date := TO_TIMESTAMP(
datecreated,
'DD-MON-RR HH12.MI.SS.FF6 AM',
'NLS_DATE_LANGUAGE=American'
);
duedate := TO_CHAR(
CAST(ADD_MONTHS(created_date, months) AS TIMESTAMP)
+ NUMTODSINTERVAL(days, 'DAY')
+ NUMTODSINTERVAL(hours, 'HOUR')
+ NUMTODSINTERVAL(minutes, 'MINUTE')
+ NUMTODSINTERVAL(seconds, 'SECOND')
+ (created_date - CAST(created_date AS DATE)), -- Fractional seconds
'DD-MON-RR HH12.MI.SS.FF6 AM',
'NLS_DATE_LANGUAGE=American'
);
DBMS_OUTPUT.PUT_LINE(duedate);
END;
/
db<>fiddle here

Oracle converting working SQL with CTE to procedure

I have some SQL code, which works as designed. I'm trying to convert the code to a procedure to make it flexible so that different values maybe passed in.
I am running into an error while trying to create the procedure.
Errors: PROCEDURE CREATE_XXX
Line/Col: 28/1 PL/SQL: SQL Statement ignored
Line/Col: 37/3 PL/SQL: ORA-00928: missing SELECT keyword
The problem seems to be occurring in a CTE, which contains a SELECT so I'm a bit confused and can use some assistance.
Below is a test CASE that contains the working SQL along with the procedure I'm trying to create.
Thanks in advance for your help, patience and expertise and to all who answer.
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
create table schedule(
schedule_id NUMBER(4),
location_id number(4),
base_date DATE,
start_date DATE,
end_date DATE,
CONSTRAINT start_min check (start_date=trunc(start_date,'MI')),
CONSTRAINT end_min check (end_date=trunc(end_date,'MI')),
CONSTRAINT end_gt_start CHECK (end_date >= start_date),
CONSTRAINT same_day CHECK (TRUNC(end_date) = TRUNC(start_date))
);
/
CREATE TABLE locations AS
SELECT level AS location_id,
'Door ' || level AS location_name,
CASE round(dbms_random.value(1,3))
WHEN 1 THEN 'A'
WHEN 2 THEN 'T'
WHEN 3 THEN 'T'
END AS location_type
FROM dual
CONNECT BY level <= 15;
ALTER TABLE locations
ADD ( CONSTRAINT locations_pk
PRIMARY KEY (location_id));
-- works fine
WITH params AS
(
SELECT 1 AS schedule_id,
TO_DATE ( '2021-08-21 00:00:00'
, 'YYYY-MM-DD HH24:MI:SS'
) AS base_date
, INTERVAL '83760' SECOND AS offset
, INTERVAL '10' MINUTE AS incr
, INTERVAL '5' MINUTE AS duration
FROM dual
)
SELECT p.schedule_id
, l.location_id
, p.base_date
, p.base_date + offset
+ (incr * (ROWNUM - 1)) AS start_date
, p.base_date + offset
+ (incr * (ROWNUM - 1))
+ p.duration AS end_date
FROM locations l
CROSS JOIN params p
ORDER BY start_date
;
-- having problem
CREATE OR REPLACE PROCEDURE CREATE_XXX
(
i_schedule_id IN PLS_INTEGER,
i_base_date IN DATE,
i_offset IN PLS_INTEGER DEFAULT 0,
i_incr IN PLS_INTEGER DEFAULT 10,
i_duration IN PLS_INTEGER DEFAULT 5
)
AS
l_offset interval day to second;
l_incr interval day to second;
l_duration interval day to second;
BEGIN
l_offset :=
NUMTODSINTERVAL(i_offset, 'SECOND') ;
l_incr :=
NUMTODSINTERVAL(i_incr, 'MINUTE') ;
l_duration :=
NUMTODSINTERVAL(i_duration, 'MINUTE') ;
WITH params AS(
SELECT
i_schedule_id
,i_base_date
,l_offset
,l_incr
,l_duration
FROM DUAL
)
INSERT INTO schedule(
schedule_id
,location_id
,base_date
,start_date
,end_date
)
VALUES
(p.schedule_id
,l.location_id
,p.base_date
,start_date
,end_date
);
SELECT p.schedule_id
, l.location_id
, p.base_date
, p.base_date + p.offset
+ (p.incr * (ROWNUM - 1)) AS start_date
, p.base_date + p.offset
+ (p.incr * (ROWNUM - 1))
+ p.duration AS end_date
FROM locations l
CROSS JOIN params p
ORDER BY start_date;
END;
/
The issue is this statement.
WITH params AS(
SELECT
i_schedule_id
,i_base_date
,l_offset
,l_incr
,l_duration
FROM DUAL
)
INSERT INTO schedule(
schedule_id
,location_id
,base_date
,start_date
,end_date
)
VALUES
(p.schedule_id
,l.location_id
,p.base_date
,start_date
,end_date
);
I'm not sure what you're trying to accomplish. Syntactically, if you want to have a CTE as part of an insert, you'd want to do an insert ... select
INSERT INTO schedule(
schedule_id
,location_id
,base_date
,start_date
,end_date
)
WITH params AS(
SELECT
i_schedule_id
,i_base_date
,l_offset
,l_incr
,l_duration
FROM DUAL
)
SELECT
p.schedule_id
,l.location_id
,p.base_date
,start_date
,end_date
FROM params p;
From there, though, you've still got several syntax issues that it's not obvious how you'd want to solve all of them.
p.schedule_id isn't valid because there is no schedule_id column in the params CTE. My guess is that you want to alias i_schedule_id to schedule_id in the CTE.
l.location_id doesn't make sense because you're not selecting from a table that could plausibly be given an alias of l.
There is no base_date, start_date, or end_date column in your params CTE. And it's not obvious how you'd plausibly fix that.
Maybe you actually intended the subsequent select statement to actually be part of this insert statement despite the fact that you terminated the insert with a semicolon? If so, maybe you want
INSERT INTO schedule(
schedule_id
,location_id
,base_date
,start_date
,end_date
)
WITH params AS(
SELECT
i_schedule_id schedule_id
,i_base_date base_date
,l_offset offset
,l_incr incr
,l_duration duration
FROM DUAL
)
SELECT p.schedule_id
, l.location_id
, p.base_date
, p.base_date + p.offset
+ (p.incr * (ROWNUM - 1)) AS start_date
, p.base_date + p.offset
+ (p.incr * (ROWNUM - 1))
+ p.duration AS end_date
FROM locations l
CROSS JOIN params p
ORDER BY start_date;
That would produce code that compiles at least. There doesn't, however, appear to be any reason to bother with a CTE here at all. Just reference the local variables and input parameters directly. I've also eliminated the order by since it doesn't make sense in the context of an insert statement. I assume there is a reason that you need to take input parameters as integers and convert them to local variables with the same name and a different data type rather than just passing in interval parameters to the procedure. If you can do that, you can further simplify things by eliminating the local variables.
CREATE OR REPLACE PROCEDURE CREATE_XXX
(
i_schedule_id IN PLS_INTEGER,
i_base_date IN DATE,
i_offset IN PLS_INTEGER DEFAULT 0,
i_incr IN PLS_INTEGER DEFAULT 10,
i_duration IN PLS_INTEGER DEFAULT 5
)
AS
l_offset interval day to second;
l_incr interval day to second;
l_duration interval day to second;
BEGIN
l_offset :=
NUMTODSINTERVAL(i_offset, 'SECOND') ;
l_incr :=
NUMTODSINTERVAL(i_incr, 'MINUTE') ;
l_duration :=
NUMTODSINTERVAL(i_duration, 'MINUTE') ;
INSERT INTO schedule(
schedule_id
,location_id
,base_date
,start_date
,end_date
)
SELECT i_schedule_id
, l.location_id
, i_base_date
, i_base_date + l_offset
+ (l_incr * (ROWNUM - 1)) AS start_date
, i_base_date + l_offset
+ (l_incr * (ROWNUM - 1))
+ l_duration AS end_date
FROM locations l;
END;
/

Oracle calling a function within DML

I have a function that takes in a string 'HH:MM:SS' and converts it to the number of seconds. See below example
CREATE OR REPLACE FUNCTION CONVERT_TO_SECONDS(
i_date_string IN VARCHAR2
)
RETURN INTEGER
AS
l_hours NUMBER;
l_minutes NUMBER;
l_seconds NUMBER;
BEGIN
SELECT trim('"'
FROM regexp_substr(i_date_string,'".*?"|[^:]+',1,1)) hours,
trim('"'
FROM regexp_substr(i_date_string,'".*?"|[^:]+',1,2)) minutes,
trim('"'
FROM regexp_substr(i_date_string,'".*?"|[^:]+',1,3)) seconds
INTO l_hours ,
l_minutes ,
l_seconds
FROM dual ;
return
l_hours*3600 +
l_minutes*60 +
l_seconds;
END;
/
SELECT CONVERT_TO_SECONDS('08:08:08') FROM DUAL;
CONVERT_TO_SECONDS('08:08:08')
29288
I have a procedure that works fine, which creates emp_attendance rows.
After the rows are created I am trying to update the end_date of each row with the number of seconds returned by the function. Is this possible? If so, how can I get past the syntax error on the update.
Thanks in advance to all that answer and for your help, patience and expertise.
My test CASE is below.
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
/
CREATE OR REPLACE FUNCTION generate_dates_pipelined(
p_from IN DATE,
p_to IN DATE
)
RETURN nt_date PIPELINED DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
BEGIN
LOOP
PIPE ROW (v_start);
EXIT WHEN v_start >= v_end;
v_start := v_start + INTERVAL '1' DAY;
END LOOP;
RETURN;
END generate_dates_pipelined;
/
Create table employees(
employee_id NUMBER(6),
first_name VARCHAR2(20),
last_name VARCHAR2(20),
card_num VARCHAR2(10),
work_days VARCHAR2(7)
);
INSERT INTO employees (
employee_id,
first_name,
last_name,
card_num,
work_days
)
WITH names AS (
SELECT 1, 'John', 'Doe', 'D564311','YYYYYNN' FROM dual UNION ALL
SELECT 2, 'Justin', 'Case', 'C224311','YYYYYNN' FROM dual UNION ALL
SELECT 3, 'Mike', 'Jones', 'J288811','YYYYYNN' FROM dual UNION ALL
SELECT 4, 'Jane', 'Smith', 'S564661','YYYYYNN' FROM dual
) SELECT * FROM names;
CREATE TABLE emp_attendance(
seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
employee_id NUMBER(6),
start_date DATE,
end_date DATE,
week_number NUMBER(2));
CREATE OR REPLACE PROCEDURE create_emp_attendance (
p_start_date IN DATE,
p_end_date IN DATE
)
IS
BEGIN
INSERT INTO emp_attendance ( employee_id, start_date, end_date, week_number)
SELECT
employee_id
, start_date
, start_date+NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(3600,43200)), 'SECOND') AS end_date
, to_char(start_date,'WW') AS week_number
FROM ( -- Need subquery to generate end_date based on start_date.
SELECT e.employee_id, d.COLUMN_VALUE + NUMTODSINTERVAL(FLOOR(DBMS_RANDOM.VALUE(0,86399)), 'SECOND') AS start_date
FROM employees e
CROSS JOIN TABLE( generate_dates_pipelined(p_start_date, p_end_date) ) d
) ed
;
END;
/
EXEC create_emp_attendance(SYSDATE, SYSDATE);
-- Having problem with this update
update emp_attendance
set end_date=start_date + NUMTODSINTERVAL
CONVERT_TO_SECONDS('08:08:08'),'SECOND';
-- Once the update is working the query below should show 8hrs 8mins 8sec for each row.
select e.employee_id,
e.first_name,
e.last_name,
trunc(sum(a.end_date - a.start_date) * 24) hours,
trunc(mod(sum(a.end_date - a.start_date) * 24 * 60,60)) minutes,
round(mod(sum(a.end_date - a.start_date) * 24 * 60 * 60,60)) seconds
from employees e,
emp_attendance a
where a.employee_id = e.employee_id
AND start_date BETWEEN TRUNC(SYSDATE)
AND
TRUNC(SYSDATE)+ (1-1/24/60/60)
group by e.employee_id, e.first_name, e.last_name
order by e.employee_id, e.first_name,
e.last_name;
You can simplify your function to:
CREATE OR REPLACE FUNCTION CONVERT_TO_SECONDS(
i_date_string IN VARCHAR2
)
RETURN INTEGER DETERMINISTIC
AS
BEGIN
RETURN ( TO_DATE(i_date_string, 'HH24:MI:SS')
- TO_DATE('00:00:00', 'HH24:MI:SS')
) * 86400;
END;
/
Then you need brackets around the NUMTODSINTERVAL function arguments:
UPDATE emp_attendance
SET end_date = start_date + NUMTODSINTERVAL( CONVERT_TO_SECONDS('08:08:08'),'SECOND' );
db<>fiddle here
NUMTODSINTERVAL is a function. So it needs to be used like this:
update emp_attendance
set end_date=start_date + NUMTODSINTERVAL(CONVERT_TO_SECONDS('08:08:08'), 'SECOND')
;
There's no need to create custom function, because Oracle already has it: to_dsinterval. But you need to provide also a number of days, so add a zero before your time:
select
sysdate,
sysdate + to_dsinterval('0 ' || '08:08:08') as sysdate_shifted
from dual
SYSDATE | SYSDATE_SHIFTED
:------------------ | :------------------
2021-08-15 18:31:49 | 2021-08-16 02:39:57
db<>fiddle here
Then your update will be:
update emp_attendance
set end_date = start_date + to_dsinterval('0 ' || '08:08:08')

How to sum column which stores times in hh24:mi:ss in Oracle Sql?

I have a PROCESS_TIME with data type VARCHAR2(32),
in which I store the times as hh24:mi:ss,
and I intend to add these times on some grouping logic.
A minified version of the table
CREATE TABLE "SCHEMA"."MY_TABLE"
( "MY_TABLE_ID" VARCHAR2(32 BYTE) NOT NULL ENABLE,
"START_TIME" DATE NOT NULL ENABLE,
"END_TIME" DATE NOT NULL ENABLE,
"LOAD_PROCESS_TIME" VARCHAR2(32 BYTE) DEFAULT '00:00:00',
)
and a minified version of Insert
Insert into MY_TABLE (MY_TABLE_ID,START_TIME,END_TIME,LOAD_PROCESS_TIME)
values ('8880508C9AC4441DB8E16E023F206C2F',to_date('05/11/2018 07:22','MM/DD/YYYY HH:MI'),to_date('05/11/2018 08:22','MM/DD/YYYY HH:MI'),'01:00:14');
Insert into MY_TABLE (MY_TABLE_ID,START_TIME,END_TIME,LOAD_PROCESS_TIME) values ('C858EB646A794D04B5C77779C50EBFCF',
to_date('05/12/2018 10:20','MM/DD/YYYY HH:MI'),
to_date('05/12/2018 11:20','MM/DD/YYYY HH:MI'),
'02:30:10');
Intended Query
SELECT TO_CHAR(TRUNC(END_TIME, 'DD'), 'DD-MON-YY'),
sum(LOAD_PROCESS_TIME)
FROM MY_TABLE
WHERE END_TIME > SYSDATE -14
GROUP BY TRUNC(END_TIME,'DD')
ORDER BY TRUNC(END_TIME,'DD');
Can you please help me achieve this using Oracle SQL?
You can calculate the time interval as a fractional number of days and sum that:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE table_name ( PROCESS_TIME ) AS
SELECT '01:23:04' FROM DUAL UNION ALL
SELECT '23:00:00' FROM DUAL UNION ALL
SELECT '11:36:56' FROM DUAL;
Query 1:
SELECT SUM(
TO_DATE( PROCESS_TIME, 'HH24:MI:SS' )
- TO_DATE( '00:00:00', 'HH24:MI:SS' )
) AS num_days
FROM table_name
Results:
| NUM_DAYS |
|----------|
| 1.5 |
You could also create a custom object to aggregate INTERVAL DAY TO SECOND data types:
CREATE TYPE IntervalSumType AS OBJECT(
total INTERVAL DAY(9) TO SECOND(9),
STATIC FUNCTION ODCIAggregateInitialize(
ctx IN OUT IntervalSumType
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT IntervalSumType,
value IN INTERVAL DAY TO SECOND
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(
self IN OUT IntervalSumType,
returnValue OUT INTERVAL DAY TO SECOND,
flags IN NUMBER
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT IntervalSumType,
ctx IN OUT IntervalSumType
) RETURN NUMBER
);
/
CREATE OR REPLACE TYPE BODY IntervalSumType
IS
STATIC FUNCTION ODCIAggregateInitialize(
ctx IN OUT IntervalSumType
) RETURN NUMBER
IS
BEGIN
ctx := IntervalSumType( INTERVAL '0' DAY );
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT IntervalSumType,
value IN INTERVAL DAY TO SECOND
) RETURN NUMBER
IS
BEGIN
IF value IS NOT NULL THEN
self.total := self.total + value;
END IF;
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateTerminate(
self IN OUT IntervalSumType,
returnValue OUT INTERVAL DAY TO SECOND,
flags IN NUMBER
) RETURN NUMBER
IS
BEGIN
returnValue := self.total;
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT IntervalSumType,
ctx IN OUT IntervalSumType
) RETURN NUMBER
IS
BEGIN
self.total := self.total + ctx.total;
RETURN ODCIConst.SUCCESS;
END;
END;
/
And then a custom aggregation function:
CREATE FUNCTION SUM_INTERVALS( value INTERVAL DAY TO SECOND )
RETURN INTERVAL DAY TO SECOND
PARALLEL_ENABLE AGGREGATE USING IntervalSumType;
/
Query 2:
SELECT SUM_INTERVALS( TO_DSINTERVAL( '+0 '||PROCESS_TIME ) )
AS total_difference
FROM table_name
Results:
| TOTAL_DIFFERENCE |
|------------------|
| 1 12:0:0.0 |
Update - Query 3:
SELECT TO_CHAR( num_days * 24, 'FM99990' )
|| ':' || TO_CHAR( MOD( num_days * 24*60, 60 ), 'FM00' )
|| ':' || TO_CHAR( MOD( num_days * 24*60*60, 60 ), 'FM00' )
AS total_time
FROM (
SELECT SUM(
TO_DATE( PROCESS_TIME, 'HH24:MI:SS' )
- TO_DATE( '00:00:00', 'HH24:MI:SS' )
) AS num_days
FROM table_name
)
Results:
| TOTAL_TIME |
|------------|
| 36:00:00 |
------------------------------answer----------------------------------------
As a completion to the discussion , the query which worked was
SELECT TO_CHAR(TRUNC(END_TIME, 'DD'), 'DD-MON-YY'),
to_char(sum(TO_DATE(LOAD_PROCESS_TIME,'HH24:MI:SS')- TO_DATE( '00:00:00', 'HH24:MI:SS' ))*24,'FM00')
|| ':'|| TO_CHAR(MOD(sum(TO_DATE(LOAD_PROCESS_TIME,'HH24:MI:SS')- TO_DATE( '00:00:00', 'HH24:MI:SS' ))*24*60,60),'FM00')
||':'|| TO_CHAR(MOD(sum(TO_DATE(LOAD_PROCESS_TIME,'HH24:MI:SS')- TO_DATE( '00:00:00', 'HH24:MI:SS' ))*24*60*60,60),'FM00')
as Days
FROM MY_TABLE
WHERE END_TIME > SYSDATE -30
GROUP BY TRUNC(END_TIME,'DD')
ORDER BY TRUNC(END_TIME,'DD');