Pass PL/SQL parameter as SCHEMA NAME - oracle

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>

Related

executing select statement stored in table column by replacing variable value dynamically

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>

how to take second column if first column not found in table in oracle DB

I'm expecting to write a query which takes second column if first column not found in table in oracle DB. In my case 'name' column is not present in table 'employees'
NOTE : I'm using reference cursor
I tried below,
query1:='select id or name,age from employees';
when I execute above statement, getting error
ORA-00904 "name": invalid identifier
ORA-06512 : at "employees", line 21
Explicitly, I don't think you can (as you saw).
Though, you can select * from employees and it'll work:
SQL> declare
2 l_rc sys_refcursor;
3 l_row dept%rowtype;
4 begin
5 open l_rc for select * from dept;
6 fetch l_rc into l_row;
7 end;
8 /
PL/SQL procedure successfully completed.
SQL>
Alternatively, did you consider creating a select statement dynamically, by querying user_tab_columns? Something like this:
SQL> declare
2 l_str varchar2(500);
3 l_rc sys_refcursor;
4 begin
5 for cur_r in (select column_name from user_tab_columns
6 where table_name = 'DEPT'
7 )
8 loop
9 l_str := l_str || ', '|| cur_r.column_name;
10 end loop;
11
12 l_str := 'select ' || ltrim(l_str, ', ') || ' from dept';
13
14 dbms_output.put_line(l_str);
15
16 open l_rc for l_str;
17 end;
18 /
select DEPTNO, DNAME, LOC from dept --> this is the SELECT statement
PL/SQL procedure successfully completed.
SQL>

Need Assistance with PL/SQL Syntax

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).

Single quote at then end is missing in create view in procedure

I have a stored procedure where a string parameter need to be passed to a create a view, I am facing difficulty to enclose the string in single quotes
EXECUTE IMMEDIATE
'CREATE VIEW view_Exec_Data as
Select * from Employees
where exec_id='''' ||To_NChar(EID)||''''; --EID is input parameter and value will be 9DE4D0106D1F390EE0
Above query is generated as
EXECUTE IMMEDIATE
'CREATE VIEW view_Exec_Data as
Select * from Employees
where exec_id='9DE4D0106D1F390EE0;
Single quote at the end is missing, not sure where I am doing wrong.
We lack some info; for example, I wonder why you used TO_NCHAR ... do you really need it?
Here's an example which presumes that employees table looks like this:
SQL> create table employees (exec_id varchar2(30), name varchar2(30));
Table created.
SQL> insert into employees values ('9DE4D0106D1F390EE0', 'Littlefoot');
1 row created.
SQL> select * From employees;
EXEC_ID NAME
------------------------------ ------------------------------
9DE4D0106D1F390EE0 Littlefoot
A procedure which creates a view. I'd suggest NOT to do that. Do you really really want to have zillion views, one per each EXEC_ID someone uses as a parameter? What's the purpose of doing that? Why don't you simply
select * from employees where exec_id = :par_eid;
Anyway, here you go: in order to make that many single quotes simpler, I used q-quoting mechanism.
SQL> create or replace procedure p_crv (par_eid in employees.exec_id%type)
2 is
3 l_str varchar2(200);
4 begin
5 l_str := q'[CREATE or replace VIEW view_Exec_Data as
6 Select * from Employees
7 where exec_id= to_nchar(']' || par_eid || q'[')]';
8
9 -- when using dynamic SQL, **ALWAYS** check whether command is properly written
10 dbms_output.put_line(l_str);
11
12 -- if it looks OK, then execute it
13 execute immediate l_str;
14 end;
15 /
Procedure created.
SQL> set serveroutput on
SQL> exec p_crv('9DE4D0106D1F390EE0');
CREATE or replace VIEW view_Exec_Data as
Select * from Employees
where exec_id= to_nchar('9DE4D0106D1F390EE0')
PL/SQL procedure successfully completed.
SQL> select * From view_exec_data;
EXEC_ID NAME
------------------------------ ------------------------------
9DE4D0106D1F390EE0 Littlefoot
SQL>
If you don't need to_nchar, it gets somewhat simpler:
SQL> create or replace procedure p_crv (par_eid in employees.exec_id%type)
2 is
3 l_str varchar2(200);
4 begin
5 l_str := q'[CREATE or replace VIEW view_Exec_Data as
6 Select * from Employees
7 where exec_id= ']' || par_eid || q'[']';
8
9 -- when using dynamic SQL, **ALWAYS** check whether command is properly written
10 dbms_output.put_line(l_str);
11
12 -- if it looks OK, then execute it
13 execute immediate l_str;
14 end;
15 /
Procedure created.
SQL> exec p_crv('9DE4D0106D1F390EE0');
CREATE or replace VIEW view_Exec_Data as
Select * from Employees
where exec_id= '9DE4D0106D1F390EE0'
PL/SQL procedure successfully completed.
SQL> select * From view_exec_data;
EXEC_ID NAME
------------------------------ ------------------------------
9DE4D0106D1F390EE0 Littlefoot
SQL>
You can use:
EXECUTE IMMEDIATE
'CREATE VIEW view_Exec_Data as
Select * from Employees
where exec_id=To_NChar(''' || EID ||''')';
-- ^ 3 (') ^ 3 + 1 (')
Update
Working for me. See this:
SQL> set serverout on
SQL> declare
2 EID varchar2(100) := '9DE4D0106D1F390EE0';
3 begin
4 DBMS_OUTPUT.PUT_LINE('CREATE VIEW view_Exec_Data as
5 Select * from Employees
6 where exec_id=To_NChar(''' || EID ||''')');
7 end;
8 /
CREATE VIEW view_Exec_Data as
Select * from Employees
where
exec_id=To_NChar('9DE4D0106D1F390EE0')
PL/SQL procedure successfully completed.
SQL>

Execute Immediate?

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>

Resources