Why do we use execute immediate command in PL/SQL?
I'm looking at some procedures written by a previous colleague, and I see that the person has used execute immediate a lot many time to log the progress of the procedure and also when truncating the tables.
My question is why would he do so? Can we not just truncate tables just like that in pl/sql proc?
Oracle wouldn't allow execution of DDL inside executable block. This is not allowed
begin
alter table . . .
end;
Originally Oracle had SYS.DBMS_DDL package to do this job. But it was cumbersome to use, so oracle introduced execute immediate circa v9.
Apart from executing DDL from PL/SQL (which you've already been told), execute immediate is used to run dynamic SQL. What would that be? Creating statements that depend on information that is not known at the time you're creating the PL/SQL procedure. For example, selecting from several tables in your schema:
SQL> set serveroutput on
SQL> declare
2 l_str varchar2(1000);
3 l_cnt number;
4 begin
5 for cur_r in (select table_name from user_tables
6 where table_name in ('EMP', 'DEPT', 'BONUS')
7 )
8 loop
9 l_str := 'select count(*) from ' || cur_r.table_name;
10 execute immediate l_str into l_cnt;
11 dbms_output.put_line(cur_r.table_name ||': '|| l_cnt);
12 end loop;
13 end;
14 /
BONUS: 0
DEPT: 4
EMP: 14
PL/SQL procedure successfully completed.
SQL>
Similarly, you might create a function that uses table (or column) names dynamically, e.g.
SQL> create or replace function f_cnt (par_table_name in varchar2)
2 return number
3 is
4 l_str varchar2(1000);
5 l_cnt number;
6 begin
7 l_str := 'select count(*) from ' || dbms_assert.sql_object_name(par_table_name);
8 execute immediate l_str into l_cnt;
9 return l_cnt;
10 end;
11 /
Function created.
SQL> select f_cnt('emp') from dual;
F_CNT('EMP')
------------
14
SQL>
Related
Below sql command is not working in procedure
PROCEDURE P_EMPDETAIL
AS
V_WHERE := 'E.EMP_ID = 123'B
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO EMPLOYEE E ' || V_WHERE || ;
END;
It seems to me there are various issues with your syntax and approach (you shouldn't be using dynamic SQL this way), perhaps you should learn PL/SQL and reference the manuals. The insert statement is also wrong. Below is the correct syntax.
CREATE OR REPLACE PROCEDURE P_EMPDETAIL as
V_WHERE varchar2(100);
BEGIN
V_WHERE := 'E.EMP_ID = 123';
EXECUTE IMMEDIATE 'INSERT INTO EMPLOYEE E (colname) values (1) ' || V_WHERE;
END;
Well, not exactly like that (obviously; otherwise, you wouldn't be asking for help).
It is unclear what you want to do because syntax is really strange. If you wanted to insert a row into the table, then:
SQL> CREATE TABLE employees
2 (
3 emp_id NUMBER
4 );
Table created.
SQL> CREATE OR REPLACE PROCEDURE p_empdetail (par_emp_id IN NUMBER)
2 AS
3 l_str VARCHAR2 (200);
4 BEGIN
5 l_str := 'insert into employees (emp_id) values (:1)';
6
7 EXECUTE IMMEDIATE l_str
8 USING par_emp_id;
9 END;
10 /
Procedure created.
Testing:
SQL> EXEC p_empdetail(123);
PL/SQL procedure successfully completed.
SQL> SELECT * FROM employees;
EMP_ID
----------
123
SQL>
I have simple PL/SQL block with below code
declare
rule1 varchar2(100 char);
begin
for i in (select table_name from all_tables where table_owner='EqEDI') loop
execute immediate 'select rule_stmt from rulebook ' into rule1 ;
execute immediate rule1 into result;
dbms_output.put_line('Result is '||result);
end loop;
end;
the rule statement stored in table rulebook is :
"'select count(1) from '|| tablename"
I want this above statement to be executed for all tables present for given owner
but while executing, it does not replace tablename in query with actual tables.
How can I achieve this with simple PL/SQL block.
Regards
rulebook table's contents is kind of wrong. Not that you can NOT do it the way you stored select statement into it - it is just impractical as you have to remove single quotes, remove tablename (as you can't even bind it, but concatenate what cursor returned) ... too much unnecessary jobs to be done.
Also, check all_tables and names of its columns - there's no table_owner, just owner.
Therefore, I'd suggest you to store such a statement:
SQL> SELECT * FROM rulebook;
RULE_STMT
--------------------------------------------------------------------------------
select count(*) from
Fix your PL/SQL script:
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 rule1 VARCHAR2 (100 CHAR);
3 l_str VARCHAR2 (100);
4 result NUMBER;
5 BEGIN
6 FOR i IN (SELECT table_name
7 FROM all_tables
8 WHERE owner = 'SCOTT'
9 AND table_name = 'EMP')
10 LOOP
11 EXECUTE IMMEDIATE 'select rule_stmt from rulebook '
12 INTO rule1;
13
14 l_str := rule1 || i.table_name;
15
16 EXECUTE IMMEDIATE l_str
17 INTO result;
18
19 DBMS_OUTPUT.put_line ('Result is ' || result);
20 END LOOP;
21 END;
22 /
Result is 14
PL/SQL procedure successfully completed.
SQL>
I'm trying to send variable schema name to cursor via procedure input
Here is my lame try, but you can see what I want to do:
CREATE OR REPLACE PROCEDURE HOUSEKEEPING
(SCHEMANAME in varchar2)
IS
CURSOR data_instances IS select table_name
from SCHEMANAME.table_name where TYPE='PERMANENT' and rownum<200 ;
BEGIN
DBMS_OUTPUT.PUT_LINE(SCHEMANAME);
END;
/
it throws expected
PL/SQL: ORA-00942: table or view does not exist
is there lawful way to make schema name work as variable? thanks
There is a way; you'll need some kind of dynamic SQL because you can't use schema (or object) names like that. For example, you could use refcursor instead.
Sample table:
SQL> create table table_name as
2 select 'EMP' table_name, 'PERMANENT' type from dual union all
3 select 'DEPT' , 'TEMPORARY' from dual union all
4 select 'BONUS' , 'PERMANENT' from dual;
Table created.
Procedure; note the way I composed SELECT statement first (so that I could display it and check whether it is correct), and then used it in OPEN. Loop is here to ... well, loop through the cursor. I'm just displaying table names I found - you'd probably do something smarter.
SQL> create or replace procedure housekeeping (par_schemaname in varchar2)
2 is
3 l_str varchar2(500);
4 l_rc sys_refcursor;
5 l_table_name varchar2(30);
6 begin
7 l_str := 'select table_name from ' ||
8 dbms_assert.schema_name(upper(par_schemaname)) ||
9 '.table_name where type = ''PERMANENT'' and rownum < 200';
10 open l_rc for l_str;
11
12 loop
13 fetch l_rc into l_table_name;
14 exit when l_rc%notfound;
15
16 dbms_output.put_line(l_table_name);
17 end loop;
18 close l_rc;
19 end;
20 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec housekeeping('SCOTT');
EMP
BONUS
PL/SQL procedure successfully completed.
SQL>
Can someone please pinpoint the syntax error in the below code.
DECLARE
plsql_blk VARCHAR2 (250);
begin
plsql_blk := 'begin DBMS_STATS.DELETE_TABLE_STATS ('||''''|| OWNER ||''''||','||''''|| TABLE_NAME ||''''||');'
from dba_tables
where owner = 'SYSADM'
and table_name like 'AS_TAO%'
or table_name like 'AS_BP_XLBP_TAO%'
order by table_name; end;';
execute immediate plsql_blk;
end;
/
Thanks in advance.
That would be "too many errors" error (only if it existed).
you can't execute a procedure which "looks like" a SELECT statement. Where did FROM clause come from (as well as the rest of that piece of code)?
if you are using OR among other conditions, you have to enclose it into parenthesis, otherwise you'll get false result
I didn't count them, but it looks as if you got lost in that many single quotes. Instead of that, consider using the q-quoting mechanism which helps A LOT
Let's try to make it work. I don't have your users nor tables so I'll do that in Scott's sample schema.
First, gather statistics (otherwise there's nothing to delete):
SQL> exec dbms_stats.gather_table_stats('SCOTT', 'EMP');
PL/SQL procedure successfully completed.
SQL>
This is a procedure which shows some statistics (why a procedure? So that I wouldn't have to copy/paste the whole code once again at the end of this demonstration):
SQL> create or replace procedure p_getstat(par_table in varchar2)
2 is
3 l_numrows number;
4 l_numblks number;
5 l_avgrlen number;
6 l_cachedblk number;
7 l_cachehit number;
8 begin
9 dbms_stats.get_table_stats
10 (ownname => 'SCOTT',
11 tabname => dbms_assert.simple_sql_name(par_table),
12 numrows => l_numrows,
13 numblks => l_numblks,
14 avgrlen => l_avgrlen,
15 cachedblk => l_cachedblk,
16 cachehit => l_cachehit
17 );
18 dbms_output.put_line(par_table || ' has ' || l_numrows || ' row(s)');
19 end;
20 /
Procedure created.
Let's try it:
SQL> set serveroutput on
SQL>
SQL> exec p_getstat('EMP');
EMP has 14 row(s)
PL/SQL procedure successfully completed.
SQL>
Now, your procedure, fixed. You overcomplicated it by putting everything into dynamic SQL. Loop through tables you're interested in, but execute only the necessary part in dynamic fashion:
SQL> create or replace procedure p_delstat (par_owner in varchar2)
2 is
3 l_str varchar2(200);
4 begin
5 for cur_r in (select owner, table_name
6 from all_tables
7 where owner = dbms_assert.simple_sql_name(par_owner)
8 and ( table_name like 'EMP%'
9 or table_name like 'AS_BP_XLBP_TAO%'
10 )
11 order by table_name
12 )
13 loop
14 l_str := 'begin dbms_stats.delete_table_stats(:a, :b); end;';
15 execute immediate l_str using cur_r.owner, cur_r.table_name;
16 end loop;
17 end;
18 /
Procedure created.
Does it work?
SQL> exec p_delstat('SCOTT');
PL/SQL procedure successfully completed.
SQL>
Seems so; at least, didn't raise any error. Let's check whether statistics for previously mentioned EMP table still exist:
SQL> exec p_getstat('EMP');
BEGIN p_getstat('EMP'); END;
*
ERROR at line 1:
ORA-20000: Unable to get values for table EMP
ORA-06512: at "SYS.DBMS_STATS", line 7688
ORA-06512: at "SCOTT.P_GETSTAT", line 9
ORA-06512: at line 1
SQL>
Nope, statistics is gone. Looks like we've done it correctly.
Adjust that code so that it works in your database, with your user(s) and your table(s).
I have a set of stored procedures with the same interface and one of these stored procedures will be passed to a runner stored procedure as an input parameter. How can I execute this stored procedure within the runner proc.
I tried this by using dynamic SQL. The code snippet I wrote for this:
v_proc_query := ':1(:2, :3)';
execute immediate v_proc_query using p_proc_name, p_param1, p_param2;
But the above statement give error: ORA-00900: invalid SQL statement
I'm using Oracle 12c.
What is the right approach to achieve the goal?
Something like this, perhaps?
SQL> set serveroutput on
SQL> create or replace procedure p_test (par_deptno in number)
2 is
3 l_cnt number;
4 begin
5 select count(*)
6 into l_cnt
7 from emp
8 where deptno = par_deptno;
9 dbms_output.put_line('count = ' || l_cnt);
10 end;
11 /
Procedure created.
SQL> create or replace procedure p_test_2 (par_proc_name in varchar2, par_deptno in number)
2 is
3 l_str varchar2(200);
4 begin
5 l_str := 'begin ' ||
6 dbms_assert.sql_object_name(par_proc_name) ||
7 '(' || par_deptno || ');' ||
8 'end;';
9 execute immediate l_str;
10 end;
11 /
Procedure created.
SQL> exec p_test_2('p_test', 10);
count = 3
PL/SQL procedure successfully completed.
SQL>