I must write a procedure which save the execute time of any sql-statement in a table.
The procedure is calling by exec measuresqltime('sql statement as string');
My idea is like this:
--declarations
timestart NUMBER;
BEGIN
dbms_output.enable;
timestart:=dbms_utility.get_time();
EXECUTE IMMEDIATE sql
COMMIT;
dbms_output.put_line(dbms_utility.get_time()-timestart);
-- save time
But it didn't work for me for a SELECT *... clause. (I think sql need a INTO-order)
Is there a way to execute any sql-atatements in a procedure?
If your SQL statement is a SELECT, you need to fetch from the cursor to have a meaningful measure of its execution time.
If you don't fetch from the cursor, you only measure the time spent in "parse" and "execution" phases, whereas much of the work is usually done in the "fetch" phase for SELECT statements.
You won't be able to fetch with EXECUTE IMMEDIATE or OPEN cursor FOR 'string' if you don't know the number of columns the actual statement will have. You will have to use the dynamic SQL package DBMS_SQL if the number/type of columns of the SELECT is unknown.
Here's an example:
SQL> CREATE OR REPLACE PROCEDURE demo(p_sql IN VARCHAR2) AS
2 l_cursor INTEGER;
3 l_dummy NUMBER;
4 timestart NUMBER;
5 BEGIN
6 dbms_output.enable;
7 timestart := dbms_utility.get_time();
8 l_cursor := dbms_sql.open_cursor;
9 dbms_sql.parse(l_cursor, p_sql, dbms_sql.native);
10 l_dummy := dbms_sql.execute(l_cursor);
11 LOOP
12 EXIT WHEN dbms_sql.fetch_rows(l_cursor) <= 0;
13 END LOOP;
14 dbms_sql.close_cursor(l_cursor);
15 dbms_output.put_line(dbms_utility.get_time() - timestart);
16 END;
17 /
Procedure created.
SQL> exec demo('SELECT * FROM dual CONNECT BY LEVEL <= 1e6');
744
PL/SQL procedure successfully completed.
Note that this will measure the time needed to fetch to the last row of the SELECT.
completing devosJava answered... avoid using it at dawn ;P
PROCEDURE MY_PROCEDURE IS
timeStart TIMESTAMP;
timeEnd TIMESTAMP;
timeSecond NUMBER
BEGIN
timeStart := SYSTIMESTAMP;
-- YOUR CODE HERE
timeEnd := SYSTIMESTAMP;
timeSecond :=((extract(hour from timeEnd)*3600)+(extract(minute from timeEnd)*60)+extract(second from timeEnd))-((extract(hour from timeStart)*3600)+(extract(minute from timeStart)*60)+extract(second from timeStart));
dbms_output.put_line('finished: '||timeSecond||' seconds');
END MY_PROC;
To calculate the duration for an execution time
PROCEDURE MY_PROCEDURE IS
timeStart TIMESTAMP;
timeEnd TIMESTAMP;
BEGIN
timeStart := SYSTIMESTAMP;
-- YOUR CODE HERE
timeEnd := SYSTIMESTAMP;
INSERT INTO PROC_RUNTIMES (PROC_NAME, START_TIME, END_TIME)
VALUES ('MY_PROCEDURE ', timeStart , timeEnd );
END MY_PROC;
INSERT INTO PROC_RUNTIMES (PROC_NAME, START_TIME, END_TIME)
VALUES ('PROC_NAME', TO_CHAR
(SYSDATE, 'DD/MM/YYYY HH24:MI:SS'),NULL );
Your query here;
INSERT INTO PROC_RUNTIMES (PROC_NAME, START_TIME, END_TIME)
VALUES ('PROC_NAME',NULL, TO_CHAR
(SYSDATE, 'DD/MM/YYYY HH24:MI:SS'));
Related
I want to insert looping with procedure, but when it's execute the error message is "ORA-01555: snapshot too old: rollback segment number with name "" too small". Is that a problem to do looping with sysdate in ref cursor clause?
here is my procedure
PROCEDURE AUTO_REGISTER_EXPIRED_POLICY
(
P_STATUS OUT VARCHAR2
,P_ERROR_MESSAGE OUT VARCHAR2
)
IS
P_USER_ID varchar2(3);
CURSOR C_GET_DTL_POLIS IS
SELECT pm.OFFICE, pm.SUBCLASS, pm.RESV, pm.YEAR, pm.MONTH, pm.SEQUENCE, pm.END_NO
FROM POLICY_MAIN pm
where to_char(trunc(exp_date), 'yyyymm') = to_char(add_months(trunc(sysdate)+0, 3),'yyyymm')
and not exists
;
BEGIN
p_user_id := 'adm';
FOR REC IN C_GET_DTL_POLIS
LOOP
PACKAGE_POLICY_RENEWAL_REVIEW.INSERT_RENEW
(
rec.office,
rec.subclass,
rec.resv,
rec.year,
rec.month,
rec.sequence,
rec.end_no
p_status,
p_error_message
);
END LOOP;
insert into log_renewal_review
select office, subclass, resv, year, month, sequence, end_no,
'LAPSED BY SYSTEM' REMARKS,
sysdate from renewal_review
where to_char(exp_date, 'yyyymm') = to_char(add_months(trunc(sysdate)+0, 3),'yyyymm')
and review_sts = '1'
;
P_STATUS := '1';
P_ERROR_MESSAGE := 'OK';
EXCEPTION WHEN OTHERS THEN
P_STATUS := '0';
P_ERROR_MESSAGE := SUBSTR(SQLERRM, 1, 100)
ROLLBACK;
END AUTO_REGISTER_EXPIRED_POLICY;```
when I look for solution mostly said id because sysdate is change time by time and it changed every time it insert data one by one.
Please Help me to find what is the problem? is it ok to use sysdate in ref cursor for looping?
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 pass the 'values' of the output cursor o_cur to a further loop?
CREATE OR REPLACE PROCEDURE dyn_cursor (o_cur OUT SYS_REFCURSOR)
IS
script VARCHAR2 (4000);
BEGIN
script := 'select sysdate-1 notnow, sysdate today from dual union all select sysdate+1 notnow, sysdate today from dual';
OPEN o_cur FOR script;
-- the question is related to this block:
for i in o_cur
loop
DBMS_OUTPUT.PUT_LINE(i.notnow);
end loop;
-----------------------
END;
FETCH is usually handled outside the procedure when the refcursor is an OUT variable
CREATE OR REPLACE PROCEDURE dyn_cursor (o_cur OUT SYS_REFCURSOR)
IS
script VARCHAR2 (4000);
BEGIN
script := 'select sysdate-1 notnow, sysdate today from dual union all select sysdate+1 notnow, sysdate today from dual';
OPEN o_cur FOR script;
END;
/
SET SERVEROUTPUT ON
DECLARE
v_cur SYS_REFCURSOR;
v_notnow DATE;
v_today DATE;
BEGIN
dyn_cursor(v_cur);
LOOP
FETCH v_cur INTO
v_notnow,
v_today;
EXIT WHEN v_cur%notfound;
dbms_output.put_line(v_notnow);
END LOOP;
END;
/
02-10-18
04-10-18
PL/SQL procedure successfully completed.
This seems a strange construct. I would create a view you can reuse at different places.
CREATE OR REPLACE VIEW VW_Times AS
SELECT sysdate-1 notnow, sysdate today FROM dual
UNION ALL
SELECT sysdate+1 notnow, sysdate today FROM dual;
and
CREATE OR REPLACE PROCEDURE OutputTime ()
IS
CURSOR o_cur IS
SELECT * FROM VW_Times;
BEGIN
FOR i IN o_cur
LOOP
DBMS_OUTPUT.PUT_LINE(i.notnow);
END LOOP;
END;
Tanks to post of Kaushik Nayak, I found the acceptable solution:
CREATE OR REPLACE PROCEDURE dyn_cursor (o_cur OUT SYS_REFCURSOR)
IS
script VARCHAR2 (4000);
type array is table of VARCHAR2(20) index by binary_integer;
l_datapoint1 array;
l_datapoint2 array;
BEGIN
script := 'select sysdate-1 notnow, sysdate today from dual union all select sysdate+1 notnow, sysdate today from dual';
OPEN o_cur FOR script;
LOOP
FETCH o_cur bulk collect into l_datapoint1, l_datapoint2;
for i in 1 .. l_datapoint1.count
loop
DBMS_OUTPUT.PUT_LINE( l_datapoint1(i) || ', ' || l_datapoint2(i) );
end loop;
exit when o_cur%notfound;
end loop;
close o_cur;
END;
I've been looking at the DBMS_SQL package in Oracle and trying to see if there is a way to create a view or something that users can select from to see the results of a dynamic SQL query.
I have a test procedure based on the documentation:
CREATE OR REPLACE PROCEDURE test_dyn_sql AS
cursor_name INTEGER;
rows_processed INTEGER;
l_query LONG;
BEGIN
l_query := 'SELECT SYSDATE AS the_date, ''ABC'' AS the_string, 1 AS the_int FROM dual';
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, l_query, DBMS_SQL.NATIVE);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(cursor_name);
DBMS_OUTPUT.put_line('ERROR');
END;
But this just executes the statement and does not return anything. What I'd like is a view so a user can just do a SELECT the_date FROM some_view and get the results. I will not know the names or number of columns in advance, so that's why I'm after a dynamic SQL solution.
" I will not know the names or number of columns in advance, so that's
why I'm after a dynamic SQL solution"
That's pretty hard to implement in SQL: SQL is all about the data structures, and it really expects the columns to exist up front. So you cannot build a VIEW on a mutable data structure.
You can implement a function which returns a ref cursor. This is a pointer to a data structure which can be interpreted by a client, say as a JDBC ResultSet.
Here's an example function which takes a table name and column name, assembles the query and returns its result set.
CREATE OR REPLACE FUNCTION test_dyn_sql
(tab_name in varchar2
, col_name in varchar2)
return sys_refcursor
AS
return_value sys_refcursor;
BEGIN
open return_value for
'SELECT SYSDATE AS the_date, '||col_name||' FROM '||tab_name;
return return_value;
END;
/
The output is not very elegant in SQL*Plus but you get the idea.
SQL> select test_dyn_sql ('EMP', 'NAME') from dual;
TEST_DYN_SQL('EMP','
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
THE_DATE NAME
--------- ------------------------------
03-APR-15 FOX IN SOCKS
03-APR-15 MR KNOX
03-APR-15 DAISY-HEAD MAYZIE
SQL>
I suggest you stick with Native Dynamic SQL (that is, execute immediate) as much as possible: as you can see, it's really simple compared to DBMS_SQL. Only reach for DBMS_SQL when you have some extremely complicated requirement.
I appreciate this may not be the solution form you're looking for. If that is the case please edit your question to provide more details about the problem you're trying to solve.
If you want your code to return the value of rows_processed variable use the code below which will return 0.
CREATE OR REPLACE FUNCTION test_dyn_sql(asd INTEGER) RETURN INTEGER AS
cursor_name INTEGER;
rows_processed INTEGER;
l_query LONG;
BEGIN
l_query := 'SELECT SYSDATE AS the_date, ''ABC'' AS the_string, 1 AS the_int FROM dual';
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, l_query, DBMS_SQL.NATIVE);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
RETURN rows_processed;
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(cursor_name);
DBMS_OUTPUT.put_line('ERROR');
RETURN 0;
END;
This is how you call the function from plsql
DECLARE
rows_precessed INTEGER;
BEGIN
rows_precessed := test_dyn_sqll(0);
DBMS_OUTPUT.put_line(rows_precessed);
END;
It might be a silly problem but I cant find a solution with "DATE" type passed in a PL/SQL proc which is called dynamically. What I need is to pass both date and time parts in the called proc:
create or replace
PROCEDURE DATE_TIME_TEST ( dte_Date_IN IN DATE )
IS
vch_SQL_Stmnt VARCHAR2(2000);
BEGIN
DBMS_OUTPUT.PUT_LINE('Date is :'||TO_CHAR(dte_Date_IN, 'DD-Mon-YYYY HH24:MI:SS'));
END;
/
declare
v_sql varchar2(2000);
begin
v_sql := 'begin DATE_TIME_TEST( dte_Date_IN => '''||
sysdate || ''''|| '); end;';
execute immediate v_sql;
end;
/
The output here is - Date is :27-Aug-2013 00:00:00.
I want it to be - Date is :27-Aug-2013 13:01:09
Use bind variables
SQL> create or replace procedure proc( p_dt in date )
2 as
3 begin
4 dbms_output.put_line( to_char( p_dt, 'yyyy-mm-dd hh24:mi:ss' ));
5 end;
6 /
Procedure created.
SQL> declare
2 l_sql varchar2(1000);
3 begin
4 l_sql := 'begin proc(:dt); end;';
5 execute immediate l_sql using sysdate;
6 end;
7 /
2013-08-26 22:14:26
PL/SQL procedure successfully completed.
The problem with your code is that in order to build up your string, Oracle has to convert the DATE to a VARCHAR2. It does that using your session's NLS_DATE_FORMAT. But your session's NLS_DATE_FORMAT probably doesn't include the time component so the time is lost when your procedure is actually called. Using bind variables means that you don't have to deal with that sort of implicit conversion (it is also more efficient and more secure).
If you really wanted to avoid using bind variables, you could explicitly cast sysdate to a string using a to_char and then put a to_date in the dynamic procedure call. But that's a lot of extra code and a number of unnecessary conversions.
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_sql varchar2(1000);
3 begin
4 l_sql := q'{begin proc(to_date('}' ||
5 to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss') ||
6 q'{', 'yyyy-mm-dd hh24:mi:ss')); end;}';
7 execute immediate l_sql;
8* end;
SQL> /
2013-08-26 22:19:52
PL/SQL procedure successfully completed.