Oracle PL/SQL - Loop value as dynamic column name without dynamic SQL - oracle

I would like to update multiple tables that have the same column name with the same value, instead of having an update for each table, and since Oracle doesn't provide a way to update multiple tables at once I though of using a loop for this.
I tried this but it doesn't work as expected:
begin
for i in (select column_value from table(sys.dbms_debug_vc2coll('tab1', 'tab2'))) loop
update i set my_col = 'my value';
end loop;
end;
I know I can use dynamic SQL for this with execute immediate but is there a way to avoid it?

The question (and so the problem) looks pretty simple:
update multiple tables that have the same column name with the same value
How about creating a view based on those tables & an instead of trigger which would do the job? Here's how:
Sample tables:
SQL> select * from tab_a;
ID M
---------- -
1 x
2 y
SQL> select * from tab_b;
NA MY_
-- ---
LF www
JW zzz
MC
View:
SQL> create or replace view v_tabs as
2 select to_char(id) idn, my_col from tab_a
3 union all
4 select name , my_col from tab_b;
View created.
SQL> select * from v_tabs;
IDN MY_
---------------------------------------- ---
1 x
2 y
LF www
JW zzz
MC
Instead of trigger:
SQL> create or replace trigger trg_tabs
2 instead of update on v_tabs
3 for each row
4 begin
5 update tab_a set my_col = :new.my_col;
6 update tab_b set my_col = :new.my_col;
7 end;
8 /
Trigger created.
Testing:
SQL> update v_tabs set my_col = 'e';
5 rows updated.
SQL> select * from tab_a;
ID M
---------- -
1 e
2 e
SQL> select * from tab_b;
NA MY_
-- ---
LF e
JW e
MC e
SQL>
All MY_COL values, in all tables involved, are set to e. That's what you asked for, right?

If you can't use execute immediate try with the DBMS_SQL package:
https://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sql.htm#i996963
Here is a syntyax sample of how to use this package taken from the doc:
CREATE OR REPLACE PROCEDURE demo(salary IN NUMBER) AS
cursor_name INTEGER;
rows_processed INTEGER;
BEGIN
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, 'DELETE FROM emp WHERE sal > :x', DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', salary);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(cursor_name);
END;
/

Related

reuse same "select bulk collect into" statement multiple times?

How can be reused the same "select bulk collect into" statement multiple times? So define once at the beginning and reuse shorter form later on? Well the statement is almost the same, every time the filter is different like this:
...where filter_condition = variable(i)
I don't think so.
Though, a simple option might be to create a view that utilizes the select statement you currently use, e.g.
SQL> create or replace view v_bulk as
2 select d.deptno, d.dname, e.ename, e.job, e.sal
3 from emp e join dept d on d.deptno = e.deptno;
View created.
Then, in PL/SQL procedure, reuse the view:
SQL> declare
2 type tr is record
3 (deptno number, dname varchar2(20), ename varchar2(20), job varchar2(20), sal number);
4 type tt is table of tr;
5 l_tab tt;
6 begin
7 select * bulk collect into l_tab from v_bulk --> this is always the same
8 where job = 'CLERK'; --> WHERE clause is changing
9
10 select * bulk collect into l_tab from v_bulk --> this is always the same
11 where sal > 2000; --> WHERE clause is changing
12 end;
13 /
PL/SQL procedure successfully completed.
SQL>
You can reuse the SELECT part of the query with dynamic SQL. Dynamic SQL may be a little slower and a little riskier (in terms of SQL injection possibilities) than the view-based solution offered by Littlefoot, but this approach doesn't require creating extra objects.
declare
v_numbers sys.odcivarchar2list;
v_sql clob := 'select 1 from dba_objects where 1=1';
begin
execute immediate v_sql||' and owner = ''SYSTEM''' bulk collect into v_numbers;
execute immediate v_sql||' and owner = ''DBSNMP''' bulk collect into v_numbers;
end;
/

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 select/insert statement within an IF clause Oracle

I need to execute some statements within the IF clause only if a table exists.
But the issue I am facing is, even when the condition is false, the statements are getting executed.
DECLARE
count_matching_row NUMBER := 0;
count_matching_tbl NUMBER := 0;
BEGIN
SELECT COUNT(*)
INTO count_matching_tbl
FROM user_tables
WHERE LOWER(table_name) = 'tab1';
IF(count_matching_tbl = 1)
THEN
SELECT COUNT (*)
INTO count_matching_row
FROM test1
WHERE ID IN (SELECT ID FROM tab1);
IF(count_matching_row = 0)
THEN
INSERT INTO review_case
SELECT
DISTINCT ID, d,e
FROM tab1
WHERE ID IS NOT NULL;
INSERT INTO review_case_payer
SELECT
a,b,c
FROM tab1
WHERE a IS NOT NULL;
COMMIT;
END IF;
END IF;
END;
/
Whenever I execute these statements, if the table 'tab1' exists it works fine.
If the table tab1 does not exist I get the error
"ORA-06550: line 13, column 14:
PL/SQL: ORA-00942: table or view does not exist"
I get similar errors for each line where I try to access table "tab1"
I tried with ref cursor but still the same, I cannot use it for insert statements.
Your error is due to the fact that you're using a table that may not exist; this error is thrown because the script has compile problems, not data problems, so the way you try to use the IF is not enough to handle your situation.
You need to use some dynamic SQL to handle an object that could not exist; for example, see the following.
If the table does not exist, nothing will be done:
SQL> select * from tab1;
select * from tab1
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL> declare
2 vCountTab number;
3 begin
4 select count(1)
5 into vCountTab
6 from user_tables
7 where table_name = 'TAB1';
8
9 if vCountTab = 1 then
10 execute immediate 'insert into TAB1 values (1, 2)';
11 end if;
12 end;
13 /
PL/SQL procedure successfully completed.
If the table exists, the insert will be done:
SQL> create table tab1(a number, b number);
Table created.
SQL> declare
2 vCountTab number;
3 begin
4 select count(1)
5 into vCountTab
6 from user_tables
7 where table_name = 'TAB1';
8
9 if vCountTab = 1 then
10 execute immediate 'insert into TAB1 values (1, 2)';
11 end if;
12 end;
13 /
PL/SQL procedure successfully completed.
SQL> select * from tab1;
A B
---------- ----------
1 2
SQL>

Oracle pivoting unknown number of column before execution time

I have something like this:
id cod
1 a
1 b
1 c
2 d
2 e
3 f
3 g
and i need something like this:
id cod 1 cod 2 cod 3
1 a b c
2 d e
3 f g
you understand that there is no way to know how many column oracle will have to generate before the execution time.
You can use procedure p_pivot, code below. It dynamically builds view v_test based on your table.
Then you can select from this view like here:
Connected to Oracle Database 10g Release 10.2.0.4.0
SQL> execute p_pivot;
PL/SQL procedure successfully completed
SQL> select * from v_test;
ID COD1 COD2 COD3
---------- ----- ----- -----
1 a b c
2 d e
3 f g
Procedure (please change table name from test to your table name in code):
create or replace procedure p_pivot is
v_cols number;
v_sql varchar2(4000);
begin
select max(cnt) into v_cols
from (select count(1) cnt from test group by id);
v_sql :=
'create or replace view v_test as
with t as (select row_number() over (partition by id order by cod) rn, test.* from test)
select id';
for i in 1..v_cols
loop
v_sql := v_sql || ', max(decode(rn, '||i||', cod)) cod'||i;
end loop;
v_sql := v_sql || ' from t group by id';
execute immediate v_sql;
end p_pivot;
There is NO way how to do that. Oracle HAS to know the number of columns at the moment when it is compiling the query.
Imagine that you create a view using such a query. The view would have different number of columns each time you look at it.
Also there should be no way how to fetch data from such a query. Because you do not know how many columns have until you evaluate all the data in the table.
Hi You can use this query also.
--Creating test data
create table test_me(id_num number,val varchar2(10));
insert all
into test_me
values(1,'a')
into test_me values (1,'b')
into test_me values (1,'c')
into test_me values (2,'d')
into test_me values (2,'e')
into test_me values (3,'f')
into test_me values (3,'g')
select 1 from dual;
select * from
(
select id_num,val,row_number() over (partition by id_num order by val) rn
from test_me )
pivot (max(val) for id_num in(1 as val_1,2 as val_2,3 as val_3));
The SQLFiddle demo is here
http://sqlfiddle.com/#!4/4206f/1

using cursor attributes in a CURSOR FOR LOOP

I am running the following in the Scott schema:
SET serveroutput ON;
BEGIN
FOR c_Emp IN (SELECT * FROM emp)
LOOP
dbms_output.put_line('The record processed by the cursor ' || c_Emp%rowcount);
END LOOP;
end;
This gives the error:
cursor attribute may not be applied to non-cursor 'C_EMP'
However if this is done using an explicit cursor it works fine:
set serveroutput on ;
DECLARE
emp_record emp%ROWTYPE;
count_variable NUMBER;
CURSOR c IS
SELECT * FROM emp;
BEGIN
OPEN c;
loop
fetch c INTO emp_record;
exit WHEN c%notfound;
dbms_output.put_line ('The record count is ' || c%rowcount);
END loop;
close c;
end;
Just want to understand : whether while using the CURSOR FOR LOOP, is the index variable not a cursor attribute, if so why? could someone plz expalin this....
c_Emp is not the cursor, its a record with felds for each column in the SELECT statment
c_Emp is similar to the emp_record from your second example.
Even while using a FOR loop the cursor has to be explicitly defined.
A sample use of FOR loop with a cursor would look like below:
declare
cursor c1 is select a from table;
begin
FOR b in c1
loop
<required logic>
end loop;
end;
To get the index in a for loop, you can add the rownum pseudocolumn in the select clause of implicit cursor.
SET serveroutput ON;
BEGIN
FOR c_Emp IN (SELECT e.*, rownum FROM emp e)
LOOP
dbms_output.put_line('The record processed by the cursor ' || c_Emp.rownum);
END LOOP;
end;
Try this:
SET serveroutput ON;
DECLARE
x NUMBER :=0 ;
BEGIN
FOR c_Emp IN (SELECT * FROM emp)
LOOP
x := x+1;
dbms_output.put_line('The record processed by the cursor ' || x);
END LOOP;
-----
IF x>0 THEN
dbms_output.put_line('Cursr was opened');
ELSE
dbms_output.put_line('Cursr was not opened');
END IF;
end;
SQL> create table product(
2 product_id number(4) not null,
3 product_description varchar2(20) not null
4 );
Table created.
SQL>
SQL> insert into product values (1,'Java');
1 row created.
SQL> insert into product values (2,'Oracle');
1 row created.
SQL> insert into product values (3,'C#');
1 row created.
SQL> insert into product values (4,'Javascript');
1 row created.
SQL> insert into product values (5,'Python');
1 row created.
SQL> create table company(
2 product_id number(4) not null,
3 company_id NUMBER(8) not null,
4 company_short_name varchar2(30) not null,
5 company_long_name varchar2(60)
6 );
Table created.
SQL> insert into company values(1,1001,'A Inc.','Long Name A Inc.');
1 row created.
SQL> insert into company values(1,1002,'B Inc.','Long Name B Inc.');
1 row created.
SQL> insert into company values(1,1003,'C Inc.','Long Name C Inc.');
1 row created.
SQL> insert into company values(2,1004,'D Inc.','Long Name D Inc.');
1 row created.
SQL> insert into company values(2,1005,'E Inc.','Long Name E Inc.');
1 row created.
SQL> insert into company values(2,1006,'F Inc.','Long Name F Inc.');
1 row created.
SQL> DECLARE
2 CURSOR cursorValue IS
3 SELECT h.product_description,o.company_short_name FROM company o,product h
4 WHERE o.product_id =h.product_id
5 ORDER by 2;
6 num_total_rows NUMBER;
7 BEGIN
8
9 FOR idx IN cursorValue LOOP
10 dbms_output.put_line(rpad(idx.product_description,20,' ')||' '||
11 rpad(idx.company_short_name,30,' '));
12
13 num_total_rows :=cursorValue%ROWCOUNT;
14 END LOOP;
15 IF num_total_rows >0 THEN
16 dbms_output.new_line;
17 dbms_output.put_line('Total Organizations = '||to_char(num_total_rows));
18 END IF;
19 END;
20 /
Java A Inc.
Java B Inc.
Java C Inc.
Oracle D Inc.
Oracle E Inc.
Oracle F Inc.
Total Organizations = 6
PL/SQL procedure successfully completed.
SQL> drop table product;
Table dropped.
SQL> drop table company;
Table dropped.
enter code here

Resources