How to dynamically add interval to timestamp? - oracle

I need at some point to increment dynamically a timestamp plsql variable.
So, instead of doing this:
timestamp_ := timestamp_ + INTERVAL '1' DAY;
I would like to do thomething like this:
timestamp_ := timestamp_ + INTERVAL days_ DAY;
It doesn't really work.
My final goal is to dynamically create some scheduler jobs for some entities that have an variable expiration date, to avoid creating a single one which would be often executed.

It sounds like you want
timestamp_ := timestamp + numtodsinterval( days_, 'day' );
I would be somewhat cautious, however, about an architecture that involves creating thousands of scheduler jobs rather than one job that runs periodically to clear out expired rows. A single job is a heck of a lot easier to manage and oversee.

Special note:
1. INTERVAL YEAR TO MONTH and
2. INTERVAL DAY TO SECOND
are the only two valid interval datatypes;
Sample Example:
=============================
DECLARE
l_time INTERVAL YEAR TO MONTH;
l_newtime TIMESTAMP;
l_year PLS_INTEGER := 5;
l_month PLS_INTEGER := 11;
BEGIN
-- Notes :
-- 1. format is using "-" to connect year and month
-- 2. No need to mention any other keyword ; Implicit conversion takes place to set interval
l_time := l_year || '-' || l_month;
DBMS_OUTPUT.put_line ( l_time );
SELECT SYSTIMESTAMP + l_time INTO l_newtime FROM DUAL;
DBMS_OUTPUT.put_line ( 'System Timestamp :' || SYSTIMESTAMP );
DBMS_OUTPUT.put_line ( 'New Timestamp After Addition :' || l_newtime );
END;
=============================

Try this:
DECLARE
l_val NUMBER;
l_result VARCHAR2( 20 );
BEGIN
l_val := 1;
SELECT SYSDATE - INTERVAL '1' DAY * l_val INTO l_result FROM DUAL;
DBMS_OUTPUT.put_line( 'Current Date is ' || SYSDATE || ' minus ' || l_val || ' day(s) is ' || l_result );
END;
Output will be:
Current Date is 25-FEB-16 minus 1 day(s) is 24-FEB-16

Related

Oracle error: ORA-01847: day of month must be between 1 and last day of month

I tried the following anonymous block and get ORA-01847 error at line 14.
Is there anything wrong?
What is the definition of line 14th 'SSSS.FF' into TO_CHAR FUNCTION?
Thanks in advance.
DECLARE
PROCEDURE_NAME VARCHAR2(100) := 'CONV_CIB';
TIME_START TIMESTAMP;
TIME_END TIMESTAMP;
EXECUTION_TIME TIMESTAMP;
BEGIN
dbms_output.enable;
TIME_START := SYSTIMESTAMP;
select sysdate into procedure_name from dual;
TIME_END := SYSTIMESTAMP;
EXECUTION_TIME := TO_CHAR (TIME_END - TIME_START, 'SSSS.FF');
--dbms_output.put_line ('Start: ' || TIME_START);
--dbms_output.put_line (' End: ' || TIME_END);
dbms_output.put_line (PROCEDURE_NAME ||' PROCEDURE EXECUTION TIME: ' || TO_CHAR (TIME_END - TIME_START, 'SSSS.FF'));
DBMS_OUTPUT.PUT_LINE (EXECUTION_TIME);
EXECUTION_TIME := (EXECUTION_TIME || ' HRS');
DBMS_OUTPUT.PUT_LINE (EXECUTION_TIME);
END;
Yes. There is an error. You have declared variable EXECUTION_TIME as TIMESTAMP but you are assigning character value in it. As you changed the datatype of this variable to VARCHAR2, This will work like charm -
DECLARE
PROCEDURE_NAME VARCHAR2(100) := 'CONV_CIB';
TIME_START TIMESTAMP;
TIME_END TIMESTAMP;
EXECUTION_TIME VARCHAR2(50);
BEGIN
dbms_output.enable;
TIME_START := SYSTIMESTAMP;
select sysdate into procedure_name from dual;
TIME_END := SYSTIMESTAMP;
EXECUTION_TIME := TO_CHAR (TIME_END - TIME_START, 'SSSS.FF');
--dbms_output.put_line ('Start: ' || TIME_START);
--dbms_output.put_line (' End: ' || TIME_END);
dbms_output.put_line (PROCEDURE_NAME ||' PROCEDURE EXECUTION TIME: ' || TO_CHAR (TIME_END - TIME_START, 'SSSS.FF'));
DBMS_OUTPUT.PUT_LINE (EXECUTION_TIME);
EXECUTION_TIME := (EXECUTION_TIME || ' HRS');
DBMS_OUTPUT.PUT_LINE (EXECUTION_TIME);
END;
Demo.
Is there anything wrong?
Yes, as you state in the question, you get an ORA-01847 error at line 14.
Apart from that:
You overwrite the procedure name with the current time.
If you subtract two TIMESTAMPs you get an INTERVAL DAY TO SECOND data type as the result and not another TIMESTAMP.
You cannot use EXECUTION_TIME := (EXECUTION_TIME || ' HRS'); as the right-hand side results in a string and not a TIMESTAMP.
This will run without syntax errors:
DECLARE
PROCEDURE_NAME VARCHAR2(100) := 'CONV_CIB';
value DATE;
TIME_START TIMESTAMP;
TIME_END TIMESTAMP;
EXECUTION_TIME INTERVAL DAY TO SECOND(9);
BEGIN
dbms_output.enable;
TIME_START := SYSTIMESTAMP;
SELECT sysdate INTO value FROM DUAL;
TIME_END := SYSTIMESTAMP;
EXECUTION_TIME := TIME_END - TIME_START;
dbms_output.put_line ('Start: ' || TIME_START);
dbms_output.put_line (' End: ' || TIME_END);
dbms_output.put_line (
PROCEDURE_NAME
||' PROCEDURE EXECUTION TIME: ' || execution_time
);
END;
/
and outputs:
Start: 06-SEP-22 12.14.01.551337
End: 06-SEP-22 12.14.01.553333
CONV_CIB PROCEDURE EXECUTION TIME: +00 00:00:00.001996000
DB<>Fiddle here

plsql sleep without using dbms_lock.sleep/DBMS_SESSION.sleep

Has anyone have any oracle sql that will let the program wait for 10 seconds without using dbms_lock.sleep/DBMS_SESSION.sleep functions.
In UAT instance, i want run the update statement every 10s and my current db role does not have privilages to use dbms_lock.sleep/DBMS_SESSION.sleep
One of the alternative i could think of is use of the method sleep from the Java class Thread, which you can easily use through providing a simple PL/SQL wrapper procedure as shown below:
Procedure:
CREATE OR REPLACE PROCEDURE sleep (
p_milli_seconds IN NUMBER
) AS LANGUAGE JAVA NAME 'java.lang.Thread.sleep(long)';
Execution
BEGIN
DBMS_OUTPUT.PUT_LINE('Start ' || to_char(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
SLEEP(5 * 1000); -- Resting for 5 sec
DBMS_OUTPUT.PUT_LINE('End ' || to_char(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'));
END;
/
Output:
Start 2020-03-25 12:57:24
End 2020-03-25 12:57:36
I had the same problem and wrote this code to approximate a wait function using only PL/SQL:
DECLARE
v_minimum_seconds_to_wait NUMBER := 10; /* this is the only value you need to edit */
v_time_to_output DATE;
PROCEDURE wait_at_least(
p_minimum_seconds_to_wait IN NUMBER
)
IS
v_result VARCHAR2(5) := 'TRUE';
v_target_time DATE := SYSDATE + (p_minimum_seconds_to_wait / 86400); /* convert incoming number to seconds */
FUNCTION is_it_after(
p_target_time IN DATE
)
RETURN VARCHAR2
IS
v_result_after VARCHAR2(5) := 'TRUE';
BEGIN
IF SYSDATE < p_target_time THEN
v_result_after := 'FALSE';
END IF;
RETURN v_result_after;
END is_it_after;
BEGIN
v_result := is_it_after(v_target_time);
WHILE v_result != 'TRUE' LOOP
v_result := is_it_after(v_target_time);
END LOOP;
END wait_at_least;
BEGIN
v_time_to_output := SYSDATE;
DBMS_Output.put_line('Starting time: '|| TO_CHAR(v_time_to_output, 'DD-MON-YYYY HH24:MI:SS'));
wait_at_least(
p_minimum_seconds_to_wait => v_minimum_seconds_to_wait
);
v_time_to_output := SYSDATE;
DBMS_Output.put_line('Ending time: '|| TO_CHAR(v_time_to_output, 'DD-MON-YYYY HH24:MI:SS'));
END;
This is tested against Oracle 11g:
Starting time: 25-AUG-2022 17:09:01
Ending time: 25-AUG-2022 17:09:11

How to convert string in 'MMDD' format into a date using the current year

I have this procedure that goes PROCEDURE(monthday varchar2). It receives an varchar2 that represents the month and date concatenated always with the format of MMDD. I then want to create a DATE type variable that uses this month and day, and the year being the current year.
Like: desired_date DATE;
desired_date = ?
I'm using Oracle SQL Developer.
EXTRACT the year from SYSDATE and then combine using string concatenation with your input and use TO_DATE to convert to a date:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
desired_date := TO_DATE( EXTRACT( YEAR FROM SYSDATE ) || monthday, 'YYYYMMDD' );
END;
/
then:
DECLARE
dt DATE;
BEGIN
test( '0101', dt );
DBMS_OUTPUT.PUT_LINE( '0101: ' || dt );
test( '1231', dt );
DBMS_OUTPUT.PUT_LINE( '1231: ' || dt );
BEGIN
test( '9876', dt );
DBMS_OUTPUT.PUT_LINE( '9876: ' || dt );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( '9876: ' || SQLERRM );
END;
END;
/
outputs:
0101: 2019-01-01 00:00:00
1231: 2019-12-31 00:00:00
9876: ORA-01843: not a valid month
db<>fiddle here
If you want to return NULL for invalid inputs then:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
desired_date := TO_DATE( EXTRACT( YEAR FROM SYSDATE ) || monthday, 'YYYYMMDD' );
EXCEPTION
WHEN OTHERS THEN
-- In general don't catch OTHERS but in this case the only exceptions
-- are going to be from TO_DATE
desired_date := NULL;
END;
/
db<>fiddle here
Update:
You could simplify the code (as suggested by Aleksej and William Robertson) by not specifying the year value in TO_DATE and use the default which appears to be the current year; however this behaviour is not, obviously, documented in any Oracle documentation pages so I would also include inline documentation within the function so future developers reviewing your function know that you are deliberately using this behaviour:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
-- Assumes that TO_DATE will, when not specified, default the year to the
-- current year and the time to midnight.
desired_date := TO_DATE( monthday, 'MMDD' );
END;
/
This could be a way
desired_date := to_date(monthday, 'MMDD');
According to this old post in AskTom, to_date should use the current year, if not given:
default year = current year
default month = current month
default day = 1
default hour = 0
default minute = 0
default second = 0
ops$tkyte%ORA10GR2> select sysdate, to_date( ' ', ' ' ) from dual;
SYSDATE TO_DATE('','')
-------------------- --------------------
17-aug-2012 13:41:06 01-aug-2012 00:00:00
Still unable to find this information in Oracle Docs

ORA-01027: bind variables not allowed for data definition when trying to use if elseif

I am getting 'ORA-01027: bind variables not allowed for data definition'
procedure create_dates_testing (dummy_variable varchar2 default
to_char(sysdate,'YYYYMMDD')) is
begin
DECLARE
day_of_month varchar2(255) := extract(day from sysdate);
today varchar2(255) := to_char(sysdate, 'DAY');
start_date date;
next_start_date date;
BEGIN
IF today='SUNDAY' THEN
-- Select yesterday
start_date := trunc(sysdate) - interval '1' day;
next_start_date := trunc(sysdate);
ELSE IF day_of_month=3 then
-- Select the whole of last month
start_date := runc(sysdate, 'MM') - interval '1' month;
next_start_date := runc(sysdate, 'MM') - interval '1' month
END IF;
END;
execute immediate 'drop table new_customers';
execute immediate 'create table new_customers as
select id, client_name, invoice_date
from clients table
where transactiondate >= :start_date
and transactiondate < :next_start_date;';
end;
How can I resolve this error? Where am I going wrong? I need to put this procedure in a pl/sql package.
As the error says, you can't use bind variables here, so you have to concatenate:
create or replace procedure create_dates_testing
( dummy_variable varchar2 default to_char(sysdate,'YYYYMMDD') )
as
day_of_month varchar2(255) := extract(day from sysdate);
today varchar2(255) := to_char(sysdate +1, 'fmDAY', 'nls_date_language = English');
start_date date;
next_start_date date;
begin
if today = 'SUNDAY' then
-- select yesterday
start_date := trunc(sysdate) - interval '1' day;
next_start_date := trunc(sysdate);
elsif day_of_month = 3 then
-- select the whole of last month
start_date := trunc(sysdate, 'MM') - interval '1' month;
next_start_date := trunc(sysdate, 'MM') - interval '1' month;
else
return;
end if;
execute immediate 'drop table new_customers';
execute immediate 'create table new_customers as
select id, client_name, invoice_date
from clients table
where transactiondate >= date ''' || to_char(start_date,'YYYY-MM-DD') ||
''' and transactiondate < date ''' || to_char(next_start_date,'YYYY-MM-DD') ||'''';
end create_dates_testing;
Presumably there will be some more code to handle the case where it is neither Sunday nor the third of the month, or the new_customers table does not exist.
Edit: added else condition to end processing if neither of the date conditions are met.

Stored Procedure execution time not coming correct

Hi was trying to find out time taken for the execution of an API call: here is the code
DECLARE
l_time timestamp;
l_lapsetime VARCHAR2(100);
BEGIN
select systimestamp into l_time from dual;
--here goes my API call
select to_char((systimestamp-l_time),'HH24:MI:SS') into l_lapsetime from dual;
DBMS_OUTPUT.PUT_LINE('Time taken ' || l_lapsetime);
END;
I was expecting result to be coming in milliseconds but I’m getting output as:
Time taken +000000000 10:30:00.016938000
Isn’t this time too much??
When you substract two timestamps, the result is of type INTERVAL.
TO_CHAR is not defined on interval, what happens here is that Oracle performs a standard implicit conversion from INTERVAL to VARCHAR2, ignoring your second argument.
If you want to convert INTERVAL to characters, use EXTRACT:
SELECT extract(hour FROM v_interval) || ':'
|| extract(minute FROM v_interval) || ':'
|| extract(SECOND FROM v_interval)
FROM (SELECT &timestamp2 - &timestamp1 v_interval FROM dual)

Resources