sql select statement as bind variable for dynamic plsql block - variables

I am trying to run plsql anonymous block using execute immediate and the plsql block contains a bind variable for which the value is a sql select statement. But it seems this does not work. Is there any solution for to solve this.
E.g.
BEGIN
V_SQL:='SELECT emp_id FROM emp WHERE dept_id=10;
PLSQL_BLOCK:='DECLARE
type emp_type
IS
TABLE OF NUMBER;
emp_id emp_type;
BEGIN
EXECUTE IMMEDIATE :1 BULK COLLECT INTO emp_id;
END';
EXECUTE IMMEDIATE PLSQL_BLOCK USING V_SQL;

If I understand well, you need to run an entire dynamic PLSQL block by using as SQL query as a bind variable; if so, you can try this way:
SQL> declare
2 vPlSqlBlock varchar2(10000);
3 vSQL varchar2(1000);
4 BEGIN
5 vSQL:='SELECT 1 from dual';
6 --
7 vPlSqlBlock:='DECLARE
8 type emp_type IS TABLE OF NUMBER;
9 emp_id emp_type;
10 vSQLDyn varchar2(1000) := :1;
11 BEGIN
12 EXECUTE IMMEDIATE vSQLDyn BULK COLLECT INTO emp_id;
13 --
14 /* whatever you need to do in your block */
15 for i in emp_id.first .. emp_id.last loop
16 dbms_output.put_line(emp_id(i));
17 end loop;
18 END;';
19
20 EXECUTE IMMEDIATE vPlSqlBlock USING vSQL;
21 end;
22 /
1
PL/SQL procedure successfully completed.
SQL> declare
2 vPlSqlBlock varchar2(10000);
3 vSQL varchar2(1000);
4 BEGIN
5 vSQL:='SELECT 1 from dual';
6 --
7 vPlSqlBlock:='DECLARE
8 type emp_type IS TABLE OF NUMBER;
9 emp_id emp_type;
10 vSQLDyn varchar2(1000) := :1;
11 BEGIN
12 EXECUTE IMMEDIATE vSQLDyn BULK COLLECT INTO emp_id;
13 /* this does nothing */
14 END;';
15
16 EXECUTE IMMEDIATE vPlSqlBlock USING vSQL;
17 end;
18 /
PL/SQL procedure successfully completed.
SQL>

Plz try this.Hope this helps.
set serveroutput on;
set define on;
DECLARE
V_SQL varchar2(1000):='SELECT emp_id from emp where deptno = 10';
PLSQL_BLOCK varchar2(1000):='DECLARE
type emp_type
IS
TABLE OF NUMBER;
emp_id emp_type;
BEGIN
EXECUTE IMMEDIATE :1 BULK COLLECT INTO emp_id;
END;';
BEGIN
EXECUTE IMMEDIATE PLSQL_BLOCK USING V_SQL;
END;

You have to first declare the variables you want to use in your execution block.
Try this:
DECLARE
V_SQL VARCHAR2(1000) := 'SELECT emp_id FROM emp WHERE dept_id=10';
PLSQL_BLOCK varchar2(1000) :='DECLARE type emp_type IS TABLE OF integer; emp_id emp_type;'
|| ' BEGIN EXECUTE IMMEDIATE :1 BULK COLLECT INTO emp_id; END;';
BEGIN
EXECUTE IMMEDIATE PLSQL_BLOCK USING V_SQL;
END;
/

Related

I'm trying to create a procedure for deleting rows how many rows inputted in special table, but I can't use table in input parameters

CREATE OR REPLACE PROCEDURE delete_rows (table_name IN USER_TABLES.table_name%type, row_count in number)
IS
BEGIN
execute immediate 'delete from'||table_name||' where rowid in (select rowid from table_name fetch first Row_count rows only)';
dbms_output.put_line(sql%rowcount);
END;
/
I get this error:
PLS-00357: Table View Or Sequence reference 'EMPLOYEES' not allowed in this context
When working with dynamic SQL, it is almost ALWAYS useful to prepare statement you'll execute, review it and - once you're satisfied with it - actually execute it. Here's what your code is trying to do:
SQL> set serveroutput on
SQL> CREATE OR REPLACE PROCEDURE delete_rows
2 (table_name IN USER_TABLES.table_name%type,
3 row_count in number)
4 IS
5 l_str varchar2(200);
6 BEGIN
7 l_str := 'delete from'||table_name||' where rowid in ' ||
8 '(select rowid from table_name fetch first Row_count rows only)';
9 dbms_output.put_line(l_str);
10 -- execute immediate l_str;
11 dbms_output.put_line(sql%rowcount);
12 END;
13 /
Procedure created.
SQL> exec delete_rows('test', 2);
delete fromtest where rowid in (select rowid from table_name fetch first
Row_count rows only)
PL/SQL procedure successfully completed.
SQL>
What do you think, can you really run it? Sure you can, but it'll fail:
SQL> delete fromtest where rowid in (select rowid from table_name fetch first
2 Row_count rows only)
3 /
delete fromtest where rowid in (select rowid from table_name fetch first
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL>
Let's fix it. It is a good habit to precede parameters' names with a prefix so that you'd easily recognize them in the code; it also helps in where clause because - if you name the parameter the same as column, you'll get wrong result.
Also
, if you're worried about SQL injection, you could include call(s) to the `DBMS_ASSERT` package:
SQL> CREATE OR REPLACE PROCEDURE delete_rows
2 (par_table_name IN USER_TABLES.table_name%type,
3 par_row_count in number)
4 IS
5 l_str varchar2(200);
6 l_table_name user_tables.table_name%type;
7 BEGIN
8 l_table_name := dbms_assert.sql_object_name(par_table_name);
9
10 l_str := 'delete from ' || l_table_name ||' where rowid in ' ||
11 '(select rowid from ' || l_table_name ||
12 ' fetch first ' || par_row_count || ' rows only)';
13 dbms_output.put_line(l_str);
14 -- execute immediate l_str;
15 dbms_output.put_line(sql%rowcount);
16 END;
17 /
Procedure created.
OK; what's the new result:
SQL> exec delete_rows('test', 2);
delete from test where rowid in (select rowid from test fetch first 2 rows only)
PL/SQL procedure successfully completed.
Does this work? Yes, it does:
SQL> delete from test where rowid in (select rowid from test fetch first 2 rows only);
2 rows deleted.
SQL> rollback;
Rollback complete.
SQL>
OK; the final fix of the procedure: include execute immediate:
SQL> CREATE OR REPLACE PROCEDURE delete_rows
2 (par_table_name IN USER_TABLES.table_name%type,
3 par_row_count in number)
4 IS
5 l_str varchar2(200);
6 l_table_name user_tables.table_name%type;
7 BEGIN
8 l_table_name := dbms_assert.sql_object_name(par_table_name);
9
10 l_str := 'delete from ' || l_table_name ||' where rowid in ' ||
11 '(select rowid from ' || l_table_name ||
12 ' fetch first ' || par_row_count || ' rows only)';
13 -- dbms_output.put_line(l_str);
14 execute immediate l_str;
15 dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
16 END;
17 /
Procedure created.
SQL> exec delete_rows('test', 2);
Deleted 2 row(s)
PL/SQL procedure successfully completed.
SQL>
There are 2 mistakes.
You are missing a space after from.
row_count must be passed as parameter.
create or replace procedure delete_rows (table_name IN USER_TABLES.table_name%type, row_count in number)
IS
BEGIN
execute immediate 'delete from '||table_name||' where rowid in (select rowid from ' ||table_name||' fetch first '|| Row_count || ' rows only)';
dbms_output.put_line(sql%rowcount);
END;
/

Display result from loop tables (oracle, pl/sql)

I'm try to loop some tables and run select as below:
set serveroutput on
declare
type tables_names is table of varchar2(30);
type selectTable is table of varchar2(30);
tName tables_names;
sTableName selectTable;
begin;
tName := tables_names('PERIOD','SETTING','RAP','LOG');
sTableName := selectTable('m_table1','m_table2','m_table3','m_table4','m_table5');
for i in 1..tName.count loop
for j in 1..sTableName.count loop
select col10, count(*) from user.sTableName(j)
where table_name = tName(i) group by col10;
end loop;
end loop;
end;
I got error:PL/SQL: ORA-00933.
Can you please tell me how can I correctly run PL/SQL procedure to have displayed result from my select?
UPDATE: looking result
Normally, to get this I need to run below select's:
select column_name,
count(*) as countColumn
from user.m_table1 where table_name = 'PERIOD' group by column_name;
select column_name,
count(*) as countColumn
from user.m_table2 where table_name = 'PERIOD' group by column_name;
Oracle complains (ORA-00933) that command isn't properly ended. That's probably because of a semi-colon behind the BEGIN; also, you lack the INTO clause.
I'm not sure what PERIOD, SETTING, ... are opposed to m_table1, m_table2, ... Which ones of those are table names? What are those other values, then?
Anyway: here's an example which shows how to do something like that - counting rows from tables. Try to adjust it to your situation, or - possibly - add some more info so that we'd know what you are doing.
SQL> set serveroutput on
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 l_cnt number;
4 l_str varchar2(200);
5 begin
6 tname := sys.odcivarchar2list('EMP', 'DEPT');
7
8 for i in 1 .. tname.count loop
9 l_str := 'select count(*) from ' || tname(i);
10 execute immediate l_str into l_cnt;
11 dbms_output.put_line(tname(i) ||': '|| l_cnt);
12 end loop;
13 end;
14 /
EMP: 14
DEPT: 4
PL/SQL procedure successfully completed.
SQL>
[EDIT: added GROUP BY option]
Here you go; as EMP and DEPT share the DEPTNO column, I chose it for a GROUP BY column.
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 type t_job is record (deptno varchar2(20), cnt number);
4 type t_tjob is table of t_job;
5 l_tjob t_tjob := t_tjob();
6 l_str varchar2(200);
7 begin
8 tname := sys.odcivarchar2list('EMP', 'DEPT');
9
10 for i in 1 .. tname.count loop
11 l_str := 'select deptno, count(*) from ' || tname(i) ||' group by deptno';
12 execute immediate l_str bulk collect into l_tjob;
13
14 for j in l_tjob.first .. l_tjob.last loop
15 dbms_output.put_Line('Table ' || tname(i) || ': Deptno ' || l_tjob(j).deptno||
16 ': number of rows = '|| l_tjob(j).cnt);
17 end loop;
18
19 end loop;
20 end;
21 /
Table EMP: Deptno 30: number of rows = 6
Table EMP: Deptno 20: number of rows = 5
Table EMP: Deptno 10: number of rows = 3
Table DEPT: Deptno 10: number of rows = 1
Table DEPT: Deptno 20: number of rows = 1
Table DEPT: Deptno 30: number of rows = 1
Table DEPT: Deptno 40: number of rows = 1
PL/SQL procedure successfully completed.
SQL>
You are probably looking for something like this. Note that you can't run a simple select statement inside a PL/SQL without INTO clause. use a refcursor and DBMS_SQL.RETURN_RESULT
DECLARE
TYPE tables_names IS TABLE OF VARCHAR2 (30);
TYPE selectTable IS TABLE OF VARCHAR2 (30);
tName tables_names;
sTableName selectTable;
rc SYS_REFCURSOR;
BEGIN
tName :=
tables_names ('PERIOD',
'SETTING',
'RAP',
'LOG');
sTableName :=
selectTable ('m_table1',
'm_table2',
'm_table3',
'm_table4',
'm_table5');
FOR i IN 1 .. tName.COUNT
LOOP
FOR j IN 1 .. sTableName.COUNT
LOOP
OPEN rc FOR
'select col10, count(*) from '||USER||'.'
|| sTableName (j)
|| ' where table_name = '''
|| tName (i)
|| ''' group by col10';
DBMS_SQL.RETURN_RESULT (rc);
END LOOP;
END LOOP;
END;
/

How to handle array list in a dynamic sql query

I want to create a dynamic query to handle array list.
create or replace TYPE p_type IS table of varchar2(4000) ;
CREATE OR REPLACE PROCEDURE test_proc_sk(
p_class_array IN p_type,
p_emp_record OUT SYS_REFCURSOR)
IS
lv_stmt VARCHAR2(100);
BEGIN
lv_stmt := 'Select * from dept where deptno = 10 ';
IF(p_class_array IS NOT NULL) THEN
lv_stmt := lv_stmt || 'AND dname IN (select column_value from table(' || p_class_array ||'))';
END IF;
dbms_output.put_line(lv_stmt);
OPEN p_emp_record FOR lv_stmt;
END;
It gives a compilation error
Error(9,5): PL/SQL: Statement ignored Error(9,23): PLS-00306: wrong
number or types of arguments in call to '||'
Please help
Use the MEMBER OF operator:
CREATE OR REPLACE PROCEDURE test_proc_sk(
p_class_array IN p_type,
p_emp_record OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN p_emp_record FOR
SELECT *
FROM dept
WHERE deptno = 10
AND ( p_class_array IS EMPTY OR dname MEMBER OF p_class_array );
END;
You will have to bind the parameter p_class_array .In dynamic SQL you would prefix them with a colon (:). If you use EXECUTE IMMEDIATE or OPEN ... FOR, you will bind your parameters via position to avoid sql injection.
Also note that in your Select statement you are doing *, so while execution you must declare individual variable to hold the outcome. To aviod mistake you must declare the column name in the select statement as shown below:
CREATE OR REPLACE PROCEDURE test_proc_sk(
p_class_array IN p_type,
p_emp_record OUT SYS_REFCURSOR)
IS
lv_stmt VARCHAR2(200);
BEGIN
lv_stmt := 'Select deptno,dname from dept where deptno = 10 ';
IF(p_class_array.count > 0) THEN
lv_stmt := lv_stmt || ' AND dname IN (select column_value from table(:p_class_array))';
END IF;
dbms_output.put_line(lv_stmt);
OPEN p_emp_record FOR lv_stmt using p_class_array;
END;
/
Execution:
SQL> Select * from dept ;
SQL> /
DEPTNO DNAME
---------- ----------------------------------------------------------------------------------------------------
10 CTS
20 WIPRO
30 TCS
SQL> DECLARE
vr p_type:= p_type();
x SYS_REFCURSOR;
z VARCHAR2(10);
z1 number;
BEGIN
vr.extend(2);
vr (1) := 'CTS';
vr (2) := 'TCS';
test_proc_sk (vr, x);
LOOP
FETCH x INTO z1,z;
EXIT WHEN x%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (z ||' ' ||z1);
END LOOP;
END;
/
SQL> /
Select deptno,dname from dept where deptno = 10 AND dname IN (select column_value from table(:p_class_array))
CTS 10
PL/SQL procedure successfully completed.

generating multiple columns from dual table

I need to generate multiple columns from dual table. Number of columns to be generated gets decided by user input. If input is 3 then three times 'ABC'. If it is 4 then 4 times 'ABC' should be selected from dual.
I tried union all but I am trying to find out more efficient way of doing it.
DECLARE
v_value varchar2(10):='ABC'
v_count number:=3;
VAR varchar2(4000)
BEGIN
select 'ABC','ABC','ABC' INTO VAR FROM DUAL;
END;
Try this. Here you can input the number of times 'ABC' needed as column at runtime:
Code:
declare
user_input number := #
var varchar2(10) := '''ABC''';
var1 varchar2(2000);
v_sql varchar2(1000);
v_res varchar2(4000);
begin
var1 := var;
for i in 1 .. user_input - 1 loop
var1 := var1 || ',' || var;
end loop;
var1 := LTRIM(RTRIM(var1, ','), ',');
--dbms_output.put_line(var1);
v_sql := 'select :var1 from dual';
--dbms_output.put_line(v_sql);
Execute immediate v_sql
into v_res
using var1;
dbms_output.put_line(v_res);
end;
Demo:
SQL> declare
2
3 user_input number := #
4
5 var varchar2(10) := '''ABC''';
6 var1 varchar2(2000);
7 v_sql varchar2(1000);
8 v_res varchar2(4000);
9 begin
10
11 var1 := var;
12
13 for i in 1 .. user_input - 1 loop
14 var1 := var1 || ',' || var;
15 end loop;
16
17 var1 := LTRIM(RTRIM(var1, ','), ',');
18
19 --dbms_output.put_line(var1);
20
21 v_sql := 'select :var1 from dual';
22
23 --dbms_output.put_line(v_sql);
24
25 Execute immediate v_sql
26 into v_res
27 using var1;
28
29 dbms_output.put_line(v_res);
30
31 end;
32 /
Enter value for num: 2
old 3: user_input number := #
new 3: user_input number := 2;
'ABC','ABC'
PL/SQL procedure successfully completed.
SQL> /
Enter value for num: 5
old 3: user_input number := #
new 3: user_input number := 5;
'ABC','ABC','ABC','ABC','ABC'
PL/SQL procedure successfully completed.
SQL> /
Enter value for num: 7
old 3: user_input number := #
new 3: user_input number := 7;
'ABC','ABC','ABC','ABC','ABC','ABC','ABC'
PL/SQL procedure successfully completed.
SQL> /
Enter value for num: 6
old 3: user_input number := #
new 3: user_input number := 6;
'ABC','ABC','ABC','ABC','ABC','ABC'
PL/SQL procedure successfully completed.
SQL> /
Enter value for num: 9
old 3: user_input number := #
new 3: user_input number := 9;
'ABC','ABC','ABC','ABC','ABC','ABC','ABC','ABC','ABC'
PL/SQL procedure successfully completed.
Is that what you mean?
select RPAD('ABC',length('ABC')*3,'ABC') from dual
ABCABCABC
DECLARE
v_value varchar2(10):='ABC'
v_count number:=3;
VAR varchar2(4000)
BEGIN
select RPAD(v_value,length(v_value)*v_count,v_value) INTO VAR FROM DUAL;
END;
Your question is not that clear.
The fact that you are using a VARCHAR2(4000) variable for your result value makes me think that you need to get a single string composed by the concatenation of a string n times; if this is the case, you don't need a select ... from DUAL and can simply do:
DECLARE
v_value varchar2(10):='ABC';
v_count number:=4;
VAR varchar2(4000);
BEGIN
VAR := rpad(v_value, length(v_value) * v_count, v_value);
dbms_output.put_line(VAR);
END;
/
ABCABCABCABC
But you say you tried UNION, and this makes me think you need to get n rows with the same value; in this case, you may try:
DECLARE
type yourResultType is table of varchar2(10);
v_value varchar2(10):='ABC';
v_count number:=4;
VAR yourResultType;
BEGIN
select v_value
bulk collect into VAR
from dual
connect by level <= v_count;
--
for i in VAR.first .. VAR.last loop
dbms_output.put_line(VAR(i));
end loop;
END;
/
ABC
ABC
ABC
ABC

procedure to delete duplicate records using dynamic sql

i have successfully implemented a proc which can delete delete duplicate records from a table .
Carrying on i am further trying to generalize this procedure using Dynamic sql But i get the following error when compiling itself .
ERROR line 13, col 7, ending_line 14, ending_col 14, Found ''SELECT ROWID, ERR_MOD
FROM '', Expecting: ( SELECT -or- WITH
Here is my code :-
CREATE OR REPLACE PROCEDURE BIOCON.proc_dedup (
p_tblname IN VARCHAR2,
p_cname IN varchar2,
p_cvalue IN varchar2,
p_out OUT VARCHAR2:=0
)
IS
v_cnt NUMBER;
CURSOR TMP
IS
'SELECT ROWID, ERR_MOD
FROM ' || p_tblname||
'WHERE '|| p_cname '='||''p_cvalue '';
BEGIN
BEGIN
'SELECT COUNT ( * )
INTO v_cnt
FROM ' || p_tblname||
'WHERE '|| p_cname' = '||''p_cvalue'';
EXCEPTION
WHEN OTHERS
THEN
p_out:=1;
end;
FOR REC IN tmp
LOOP
IF v_cnt = 1
THEN
EXIT;
ELSE
'DELETE FROM '|| p_tblname||
'WHERE ROWID = REC.ROWID';
END IF;
v_cnt := v_cnt - 1;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
p_out := 2;
END;
/
Hi you cannot create a dynamic query for normal cursors in such cases you should use ref cursors and follow the the steps below
--create a query string
--open the ref cursor
and please use execute immediate for execution of delete statements...
CREATE OR REPLACE PROCEDURE BIOCON.proc_dedup (
p_tblname IN VARCHAR2,
p_cname IN varchar2,
p_cvalue IN varchar2,
p_out OUT VARCHAR2:=0
IS
v_cnt NUMBER;
v_string varchar2(200);
TYPE ref_cur IS REF CURSOR
ref_cursor_name ref_cur;
BEGIN
v_string:='SELECT ROWID, ERR_MOD FROM ' || p_tblname|| 'WHERE '|| p_cname
'='||''p_cvalue '';
OPEN cursor_variable_name FOR v_string;
.....
.....
EXECUTE IMMEDIATE 'DELETE FROM '|| p_tblname|| 'WHERE ROWID = REC.ROWID';--you need execute immediate for this
.....
.....