I am trying to get all the tables where bank_id is 01.
I have written the following block
DECLARE
cursor cBankId is
select owner||'.'||table_name from all_tab_columns where column_name = 'BANK_ID';
v_table all_tab_columns.table_name%TYPE;
vcount varchar2(50);
BEGIN
open cBankId;
loop
fetch cBankId into v_table;
exit when cBankId%notfound;
execute immediate 'select count(*) from ' || v_table into vcount || ' where bank_id = 01';
IF vcount > 0 THEN
DBMS_OUTPUT.PUT_LINE (v_table);
END IF;
end loop;
close cBankId;
END;
I want to know how to put where clause in the execute immediate statement .
I am getting the error
ORA-06550: line 15, column 67:
PLS-00103: Encountered the symbol "|" when expecting one of the following:
. ( , % ; return returning using
You only need to change the order you're writing the parts of your query.
When using dynamic SQL, you need something like this:
SQL> declare
2 v_table varchar2(30);
3 vCount number;
4 begin
5 v_table := 'dual';
6 execute immediate 'select count(*) from ' || v_table || ' where 1=1' into vcount;
7 --
8 dbms_output.put_line('vCount: ' || vCount);
9 end;
10
11 /
vCount: 1
PL/SQL procedure successfully completed.
That is: execute immediate 'select ... from ... where ...' INTO ...;
You can't dynamically use a variable for the table name, instead use:
DECLARE
cursor cBankId is
select owner||'.'||table_name from all_tab_columns where column_name = 'BANK_ID';
v_table all_tab_columns.table_name%TYPE;
vcount varchar2(50);
v_sql varchar2(1000);
BEGIN
open cBankId;
loop
fetch cBankId into v_table;
exit when cBankId%notfound;
v_sql := 'select count(*) from ' || v_table || ' into vcount where bank_id = 01';
execute immediate v_sql;
IF vcount > 0 THEN
DBMS_OUTPUT.PUT_LINE (v_table);
END IF;
end loop;
close cBankId;
END;
Related
I am trying to generate a dynamic SQL statement with a generic condition.
Depending some conditions, it will be added different other conditions in where clause.
I was trying to have something like this:
declare
v_sql varchar2(500);
a number;
v_dummy number := 1;
begin
v_sql := 'delete tab_1 where v_dummy = 1 ';
if a = 1
then v_sql := v_sql || ' and col_1 = 1';
else v_sql := v_sql || ' and col_2 = 3';
end if;
execute immediate v_sql;
dbms_output.put_line(v_sql);
end;
The error raised is:
ORA-00904: "V_DUMMY": invalid identifier
Can anyone, please, guide me how to handle this situation?
The problem is with the definition of first condition (v_dummy = 1) that I need to add in order to use the "and" operand for the second condition.
Thank you,
If there is a possibility that you need multiple conditions (not in your code) you can set initial condition simply putting col_1 = col_1. No need for dummy variable at all and leaves you options to add some more conditions:
declare
v_sql varchar2(500);
a number;
begin
v_sql := 'delete tab_1 where col_1 = col_1 ';
if a = 1 then
v_sql := v_sql || ' and col_1 = 1';
else
v_sql := v_sql || ' and col_2 = 3';
end if;
execute immediate v_sql;
dbms_output.put_line(v_sql);
end;
Use a bind variable:
declare
v_sql varchar2(500);
a number;
v_dummy number := 1;
begin
v_sql := 'delete tab_1 where :v_dummy = 1 ';
if a = 1
then v_sql := v_sql || ' and col_1 = 1';
else v_sql := v_sql || ' and col_2 = 3';
end if;
execute immediate v_sql USING v_dummy;
dbms_output.put_line(v_sql);
end;
/
or since it evaluates to 1 = 1, you can omit it:
declare
v_sql varchar2(500);
a number;
begin
v_sql := 'delete tab_1';
if a = 1
then v_sql := v_sql || ' where col_1 = 1';
else v_sql := v_sql || ' where col_2 = 3';
end if;
execute immediate v_sql;
dbms_output.put_line(v_sql);
end;
/
Declare
Type t_approved_node is record( node_rowid Hr
node_rowid%type, Node_+type hr.node_type%type);
Type t_val is table of t_approved_node Index by pls_integer;
V_node t_val;
V_tab varchar2(20);
V_col varchar2(400);
V_nrf_flg hr.hr_flag%type;
V_ubrf_flg hr.hr_flag%type := 3;
V_col_str varchar2(4000);
Begin
Begin
Select hr_flag into v_nrf_flg from hr;
End;
Begin
Select h.node_rowid, h.node_type bulk collect into v_node
from hr h, hr_attr_wfm haw
Where h.hr_relation_id = haw.uc_hr_relation_id
And h.node_type = 'UBR';
Begin
V_tab := 'UC_UBR';
Select listagg(column_name, ',' within group(order by
column_id)
Into v_col from user_tab_columns where table_name = v_tab;
End;
V_col_str := regex_replace( v_col, 'HR_FLAG', v_ubrf_flg);
Execute immediate ' insert into ' || v_tab || '( ' ||
V_col || ') ' || ' select '|| v_col_str || ' from ' ||
V_tab || 'R ' || q' [ where node_type = ' UBR' a
and hr_flag =:1 and exists( ] ' || ' select 1 ' || ' from table( ' ||
v_node || ')y' || q' [ where y.node_rowid = R.node_rowid ] )'
Using v_nrf_flag;
End;
End;
I was trying to execute above block getting below error.
Wrong number or types of arguments in call to ||
Final query should be like
insert into UC_UBR ( v_col)/*3 columns into v_col variable*/
select v_col_str /* 3 columns in v_col_str variable*/ from UC_UBR R where hr_flag =:1
and exists
(select 1 from table(v_node) /*collection variable*/ y
where y.node_rowid = r.node_rowod;
Can anyone help on this?
Your sample code is full of errors and does not make any sense at all. But if I focus on your question, then the answer is "yes". See this example:
CREATE OR REPLACE TYPE t_app AS OBJECT( nodeid NUMBER, Nodetype VARCHAR2(100));
CREATE OR REPLACE TYPE t_val IS TABLE OF t_app;
DECLARE
V_node t_val;
V_result t_val;
V_app t_app;
V_count NUMBER;
Sql_stmt VARCHAR2(100);
nodeid NUMBER;
Nodetype VARCHAR2(100);
BEGIN
SELECT t_app(nodeid, Nodetype) BULK COLLECT INTO V_node FROM HR;
Sql_stmt := 'SELECT count(*) FROM TABLE(:t)';
EXECUTE IMMEDIATE Sql_stmt INTO V_count USING V_node;
DBMS_OUTPUT.PUT_LINE ( 'V_count = ' || V_count );
Sql_stmt := 'SELECT nodeid, Nodetype FROM TABLE(:t) WHERE ROWNUM = 1';
EXECUTE IMMEDIATE Sql_stmt INTO nodeid, Nodetype USING V_node;
DBMS_OUTPUT.PUT_LINE ( 'nodeid = ' || nodeid );
DBMS_OUTPUT.PUT_LINE ( 'Nodetype = ' || Nodetype );
Sql_stmt := 'SELECT t_app(nodeid, Nodetype) FROM TABLE(:t) WHERE ROWNUM = 1';
EXECUTE IMMEDIATE Sql_stmt INTO V_app USING V_node;
DBMS_OUTPUT.PUT_LINE ( 'V_app = ' || XMLTYPE(V_app).getClobVal() );
Sql_stmt := 'SELECT t_app(nodeid, Nodetype) FROM TABLE(:t)';
EXECUTE IMMEDIATE Sql_stmt BULK COLLECT INTO V_result USING V_node;
END;
It would help if you posted real code you used, because this is full of syntax errors (missing sql_stmt local variable declaration, put.line (?)).
I have no idea what you plan to do with such a select statement as you can't execute it, it doesn't make any sense but - here you go; see line #20.
SQL> set serveroutput on
SQL>
SQL> DECLARE
2 TYPE t_app IS RECORD
3 (
4 nodeid NUMBER,
5 Nodetype VARCHAR2 (20)
6 );
7
8 TYPE t_val IS TABLE OF t_app
9 INDEX BY PLS_INTEGER;
10
11 V_node t_val;
12 V_tab VARCHAR2 (20);
13
14 sql_stmt VARCHAR2 (200);
15 BEGIN
16 SELECT empno, ename
17 BULK COLLECT INTO v_node
18 FROM emp;
19
20 Sql_stmt := 'select 1 from (' || v_node (1).nodeid || 'Y)';
21
22 DBMS_OUTPUT.put_line (sql_stmt);
23 END;
24 /
select 1 from (7369Y)
PL/SQL procedure successfully completed.
SQL>
I have a procedure which receive as input parameter a record with 170 columns (it is based on the structure of a table).
In the procedure I want to call a debugging procedure one of whose parameters is a text string containing all the field names and values of this record.
For example:
CREATE OR REPLACE PROCEDURE xxx (pi_record IN table_name%ROWTYPE) as
text VARCHAR2(10000) := NULL;
BEGIN
...
text := 'pi_record.column1 = ' || pi_record.column1 || CHR(13) ||
'pi_record.column2 = ' || pi_record.column2 || CHR(13) ||
...
'pi_record.column170 = ' || pi_record.column170;
logging_procedure (text);
...
END;
Is there any simple way to achieve this in a dynamic way (looping through record fields names and values) without enumerating all of them?
Maybe something like this:
CREATE OR REPLACE PROCEDURE xxx (pi_record IN table_name%ROWTYPE) as
text VARCHAR2(10000) := NULL;
BEGIN
...
LOOP in pi_record.columns
text := text || CHR(13) || pi_record.column.name || ' : ' || pi_record.column.value
END LOOP
logging_procedure (text);
...
END;
Many thanks,
Here's one way to do that. A package spec contains a variable whose type matches the one we'll use in a procedure.
SQL> set serveroutput on
SQL> create or replace package pkg_xxx
2 as
3 dept_rec dept%rowtype;
4 end;
5 /
Package created.
SQL> create or replace procedure xxx (pi_record in dept%rowtype)
2 as
3 text varchar2 (10000) := null;
4 l_str varchar2 (200);
5 l_var varchar2 (200);
6 begin
7 pkg_xxx.dept_rec := pi_record;
8
9 for cur_r in ( select column_name
10 from user_tab_columns
11 where table_name = 'DEPT'
12 order by column_id)
13 loop
14 l_str :=
15 'begin '
16 || ':x := to_char(pkg_xxx.dept_rec.'
17 || cur_r.column_name
18 || '); '
19 || 'end; ';
20
21 execute immediate l_str using out l_var;
22
23 text := text || chr (10) || cur_r.column_name || ' = ' || l_var;
24 end loop;
25
26 dbms_output.put_line (text);
27 end;
28 /
Procedure created.
Now, let's pass something to the procedure and see what happens:
SQL> declare
2 cursor c1
3 is
4 select *
5 from dept
6 where deptno = 10;
7
8 c1r c1%rowtype;
9 begin
10 open c1;
11 fetch c1 into c1r;
12 close c1;
13
14 xxx (c1r);
15 end;
16 /
DEPTNO = 10
DNAME = ACCOUNTING
LOC = NEW YORK
PL/SQL procedure successfully completed.
SQL>
Huh, kind of works (if that's what you asked). Of course, it is just an example, you'll have to modify it if you want to get something really smart (hint: DATE columns).
The only idea I have is to insert the record into a TEMP table:
CREATE OR REPLACE PROCEDURE xxx (pi_record IN TABLE_NAME%ROWTYPE) AS
TEXT VARCHAR2(10000) := NULL;
item VARCHAR2(1000);
TABLE_DOES_NOT_EXIST EXCEPTION;
PRAGMA EXCEPTION_INIT(TABLE_DOES_NOT_EXIST, -942);
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE TABLE_NAME_TMP';
EXCEPTION
WHEN TABLE_DOES_NOT_EXIST then null;
END;
EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE TABLE_NAME_TMP AS SELECT * FROM TABLE_NAME WHERE ROWNUM = 0';
DELETE FROM TABLE_NAME_TMP;
INSERT INTO TABLE_NAME_TMP VALUES pi_record;
FOR aCol IN (SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE table_name = 'TABLE_NAME' ORDER BY COLUMN_ID) LOOP
EXECUTE IMMEDIATE 'SELECT '||aCol.COLUMN_NAME||' FROM TABLE_NAME_TMP' INTO item;
TEXT := TEXT || CHR(13) || aCol.COLUMN_NAME || ' : ' || item;
END LOOP;
DBMS_OUTPUT.PUT_LINE ( TEXT );
END;
In case table TABLE_NAME has static attributes then you should skip dynamic DROP TABLE ... and CREATE GLOBAL TEMPORARY TABLE ... and create the TEMP table only once.
everyone!
I got a different approach to get the difference between records dynamically:
You just have to create the global variables on the package header as bellow:
v_NAME_OF_TABLE_new NAME_OF_TABLE%rowtype;
v_NAME_OF_TABLE_old NAME_OF_TABLE%rowtype;
then create the function on your pkg body that return a boolean even if a field is different:
function is_different(p_old NAME_OF_TABLE%rowtype, p_new NAME_OF_TABLE%rowtype)
return boolean
is
cursor cols is
select tb.COLUMN_NAME
from all_tab_columns tb
where tb.OWNER = 'DW'
and tb.TABLE_NAME = 'NAME_OF_TABLE'
order by tb.COLUMN_ID;
l_sql varchar2(4000);
l_new varchar2(4000);
l_old varchar2(4000);
begin
pkg_NAME.v_NAME_OF_TABLE_new := p_new;
pkg_NAME.v_NAME_OF_TABLE_old := p_old;
for reg in cols loop
l_sql := '
begin
:x := pkg_NAME.v_NAME_OF_TABLE_new.'||reg.COLUMN_NAME||';'||'
end;';
execute immediate l_sql using out l_new;
l_sql := '
begin
:x := pkg_NAME.v_NAME_OF_TABLE_old.'||reg.COLUMN_NAME||';'||'
end;';
execute immediate l_sql using out l_old;
--- dbms_output.put_line(l_new||' - '||l_old);
if nvl(l_new,'NULO') <> nvl(l_old,'NULO') then
return true;
end if;
end loop;
return false;
end;
Atention: This can turn your process heavier and slower.
That's all!
Hope this can be helpful!
can't make this work... have a multiple tables names which i need to pass into select. Each select will return multiple records. Resultset should be printed to the user on a screen .
SQL> set serveroutput on;
SQL> DECLARE
vs_statement VARCHAR2 (1000);
my_var1 VARCHAR2(100);
my_var2 VARCHAR2(100);
CURSOR c1 IS
SELECT table_name
FROM all_tables
WHERE table_name LIKE Upper('redit_1%');
BEGIN
FOR table_rec IN c1 LOOP
vs_statement :=
'select a.userinfo, a.userstatus into my_var1, my_var12 from '
|| table_rec.table_name
|| ' A, FILES b where A.objectid = B.id order by 1';
EXECUTE IMMEDIATE vs_statement INTO my_var1,
my_var2;
dbms_output.Put_line(my_var1
||' '
|| my_var2);
END LOOP;
END;
/
This line should look like the following (i.e. remove INTO clause):
vs_statement :=
'select a.userinfo, a.userstatus from '
|| table_rec.table_name
|| ' A, FILES b where A.objectid = B.id order by 1';
Also, make sure that this SELECT returns only 1 row, otherwise you'll end up with the TOO-MANY-ROWS error.
Other than that, I guess it should work.
[EDIT, regarding fear of TOO-MANY-ROWS]
Huh, I'd create a whole new PL/SQL BEGIN-END block, containing a loop which would do the job, displaying all rows returned by SELECT statement.
As I don't have your tables, I used HR's. Have a look:
SQL> DECLARE
2 vs_statement VARCHAR2 (1000);
3 my_var1 VARCHAR2(100);
4 my_var2 VARCHAR2(100);
5 CURSOR c1 IS
6 SELECT table_name
7 FROM all_tables
8 WHERE table_name LIKE Upper('%departments%');
9 BEGIN
10 FOR table_rec IN c1 LOOP
11 vs_statement := 'begin for cur_r in ( '
12 || Chr(10)
13 || 'select distinct a.department_id, a.department_name from '
14 || Chr(10)
15 || table_rec.table_name
16 ||
17 ' A, employees b where A.department_id = B.department_id order by 1) loop'
18 || Chr(10)
19 || ' dbms_output.put_line(cur_r.department_name); '
20 || Chr(10)
21 || ' end loop; end;';
22
23 EXECUTE IMMEDIATE vs_statement; -- into my_var1, my_var2;
24 END LOOP;
25 END;
26 /
Administration
Marketing
Purchasing
Human Resources
Shipping
IT
Public Relations
Sales
Executive
Finance
Accounting
PL/SQL procedure successfully completed.
SQL>
"Each select will return multiple records...Resultset should be printed to the user on a screen"
Open a ref cursor for each generated statement. Loop round that and display the values.
DECLARE
rc sys_refcursor;
vs_statement VARCHAR2 (1000);
my_var1 VARCHAR2(100);
my_var2 VARCHAR2(100);
CURSOR c1 IS
SELECT table_name
FROM all_tables
WHERE table_name LIKE Upper('redit_1%');
BEGIN
<< tab_loop >>
FOR table_rec IN c1 LOOP
vs_statement :=
'select a.userinfo, a.userstatus from '
|| table_rec.table_name
|| ' A, FILES b where A.objectid = B.id order by 1';
open rc for vs_statement;
dbms_output.put_line('records for '||table_rec.table_name);
<< rec_loop >>
loop
fetch rc into my_var1,my_var2;
exit when rc%notfound;
dbms_output.Put_line(my_var1
||' '
|| my_var2);
end loop rec_loop;
close rc;
END LOOP tab_loop;
END;
/
CREATE OR REPLACE PROCEDURE ResetVersionNumberValue IS
sql_stmt VARCHAR2(2000);
BEGIN
FOR sql_stmt IN (select 'update '|| table_name ||
' set version = 0'
from user_tables
where table_name like 'MY_%')
LOOP
EXECUTE IMMEDIATE sql_stmt;
END LOOP;
COMMIT;
END;
Why the about procedure is not compiling? Its giving error saying
Error(8,26): PLS-00382: expression is of wrong type
How to resolve this?
Two things:
You need to remember that when iterating a cursor it returns a ROW, not a VALUE, and
The SELECT in the cursor needs to give a name to the value in the row being generated by the cursor.
Try:
CREATE OR REPLACE PROCEDURE ResetVersionNumberValue IS
BEGIN
FOR aRow IN (select 'update '|| table_name ||
' set version = 0' AS SQL_STMT
from user_tables
where table_name like 'MY_%')
LOOP
EXECUTE IMMEDIATE aRow.SQL_STMT;
END LOOP;
COMMIT;
END;
Share and enjoy.
CREATE OR REPLACE PROCEDURE ResetVersionNumberValue IS
BEGIN
FOR sql_stmt IN (
select 'update '|| table_name || ' set version = 0' as x
from user_tables
where table_name like 'MY_%')
LOOP
EXECUTE IMMEDIATE sql_stmt.x;
END LOOP;
COMMIT;
END;