Fetch subquery value from cursor to parameter in PL/SQL - oracle

I have below query to select the count of subquery:
SELECT COUNT(*) FROM (select crs_cust.CUSTOMER_ID,
subset.NEW_REFERENCE_ID FROM CRS_CUSTOMERS crs_cust INNER JOIN DAY0_SUBSET subset ON crs_cust.CUSTOMER_ID=subset.CURRENT_CUSTOMER_ID);
Above query is executed from a cursor in PL/SQL, how can I fetch columns from subquery (CUSTOMER_ID and NEW_REFERENCE_ID) into respective parameter?
The cursor is expected to have multiple records.Similiar like the following:
p_Count := SELECT COUNT(*) FROM DAY0_SUBSET;
OPEN c1;
LOOP
FETCH c1 into p_Current_CustomerId,p_New_Cust_Ref_ID; -->query from cursor's subquery
EXIT WHEN c1%NOTFOUND;
EXIT WHEN (c1%ROWCOUNT <> p_Count);
FOR i IN c1 LOOP
<do manipulation of subquery values>
END LOOP;
END IF;
CLOSE c1;

The columns of sub queries are not projected so you can't reference them. If you want the CUSTOMER_ID and NEW_REFERENCE_ID in your program you will have to select them in the top level SELECT clause.
The easiest answer to your solution is to just open a cursor for your
subquery itself, for eample:
BEGIN
FOR cur IN (SELECT crs_cust.CUSTOMER_ID, subset.NEW_REFERENCE_ID
FROM CRS_CUSTOMERS crs_cust
INNER JOIN DAY0_SUBSET subset ON
crs_cust.CUSTOMER_ID=subset.CURRENT_CUSTOMER_ID)
LOOP
DBMS_OUTPUT.PUT_LINE(cur.customer_id || ', ' || cur.new_reference_id);
END LOOP;
END;
/

Related

Find all join referenced tables in SQL

I need to extract from V$SQL all the objects (tables or views) that are referenced in all the joins of the sql.
For example: if i have this sql in the V$SQL
select * from ADANPT
join BTABP on column1=column2
left outer join HSTUDI on column3=column4
full outer join TERW on column5=column6;
i need to extract this:
BTABP
HSTUDI
TERW
I i have just one join in my sql, it is quite simple, using substr and instr functions (so that i extract the string from JOIN to ON words).
But i can't find a way if i have 2 or more joins. Maybe with loop?
You could use the SQL_ID and take advantage of the explain plan functionality:
DECLARE
l_buff VARCHAR2(32000);
BEGIN
FOR sql_query IN (SELECT sql_ID
FROM v$sql
)
LOOP
l_buff := NULL;
dbms_output.put_line('---------------'||sql_query.sql_ID||'---------------');
FOR text_line IN (SELECT sql_text
FROM v$sqltext
WHERE sql_id = sql_query.sql_id
ORDER BY piece ASC)
LOOP
l_buff := l_buff || text_line.sql_text;
END LOOP;
dbms_output.put_line(l_buff);
EXECUTE IMMEDIATE 'explain plan for '|| l_buff;
dbms_output.put_line('---------------TABLES---------------');
FOR table_name IN (SELECT DISTINCT object_name
FROM plan_table
WHERE object_type = 'TABLE')
LOOP
dbms_output.put_line(table_name.object_name);
END LOOP;
ROLLBACK;
END LOOP;
END;

How to get the number of rows in ROWTYPE variable

I have two tables Table1 an dTable2 that have identical columns. I need to check if a particular id is in one of them and return the row of data from whichever table.
I have the following PL/SQL code:
v_result Table1%ROWTYPE;
BEGIN
SELECT a.*
INTO v_result
FROM Table1 a
WHERE a.id = 123;
EXCEPTION
WHEN NO_DATA_FOUND THEN -- when record not found
SELECT b.*
INTO v_result
FROM Table2 b
WHERE b.id = 123;
END;
The issue is that the exception does not get thrown, so v_result returns no data. How can I check v_result for the number of rows?
For cursor I can use ROWCOUNT but v_result is not a cursor.
I also tried using count property but it errored out.
I changed my code to:
v_result Table1%ROWTYPE;
BEGIN
SELECT a.*
INTO v_result
FROM Table1 a
WHERE a.id = 123;
if v_result.count =0 then
SELECT b.*
INTO v_result
FROM Table2 b
WHERE b.id = 123;
end if;
EXCEPTION
WHEN NO_DATA_FOUND THEN -- when record not found
SELECT b.*
INTO v_result
FROM Table2 b
WHERE b.id = 123;
END;
And got an error component 'count' must be declared
What am I doing wrong?
You may use only a single row in a record variable. If you want to store and count multiple rows, you may define a collection of records and use BULK COLLECT INTO to load all of them at once and it won't raise a NO_DATA_FOUND. The count function works on collections.
DECLARE
TYPE type_tab1 IS TABLE OF Table1%ROWTYPE;
TYPE type_tab2 IS TABLE OF Table2%ROWTYPE;
v_result1 type_tab1;
v_result2 type_tab2;
BEGIN
SELECT a.*
BULK COLLECT INTO v_result1
FROM Table1 a
WHERE a.id = 123;
if v_result1.count = 0 then
SELECT b.* BULK COLLECT
INTO v_result2
FROM Table2 b
WHERE b.id = 123;
end if;
DBMS_OUTPUT.PUT_LINE('v_result1 ='|| v_result1.count);
DBMS_OUTPUT.PUT_LINE('v_result2 ='|| v_result2.count);
END;
/
Output for the Demo
v_result1 =0
v_result2 =1
If your intention is to simply check if a row exists, then a simpler and efficient approach would be to use EXISTS
SELECT
CASE WHEN
EXISTS (
SELECT 1
FROM table1
WHERE id = 123
) THEN 1
ELSE 0
END
INTO v_count
FROM dual;
IF v_count = 0
THEN
..
..

Insert within FORALL loop in PL/SQL

Is it possible to do something like this in pl/sql for bulk insert using FORALL?
TYPE c_type1 IS RECORD
(
column1 table1.column1%TYPE,
column2 table1.column2%TYPE,
client table2.client%TYPE
);
type1 c_type1;
CURSOR cur_t IS select * BULK COLLECT INTO recs from table3 ;
begin
FOR recs IN cur_t
LOOP
SELECT * INTO type1 FROM (select a.column1, a.column2,imm.client
...
from table1 a, table2 imm
WHERE
a.column1 = recs.column1
) WHERE ROWNUM=1;
INSERT INTO table2 values (recs.column1,type1.column2);
...
P.S : There are more 80 columns to be inserted.
Your question is not pretty clear but looking at your code I have the following. Check if this is what you were looking for.
declare
CURSOR cur_t IS
select t3.column1 , t1.column2
from table3 t3
inner join table1 t1
on t3.column1 = t1.column1;
type var_cur is table of cur_t%rowtype;
var var_cur;
begin
open cur_t;
LOOP
FETCH cur_t bulk collect into var limit 100;
EXIT WHEN cur_t%NOTFOUND;
FORALL i IN 1 .. var.count SAVE EXCEPTIONS
INSERT INTO TABLE2
VALUES var(i);
END LOOP;
CLOSE distinctUserIdCursor;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Error in Insertion of record' || '~~~~' || SQLERRM);
FOR indx IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
LOOP
DBMS_OUTPUT.put_line (SQL%BULK_EXCEPTIONS (indx).ERROR_INDEX|| ': '
|| SQL%BULK_EXCEPTIONS (indx).ERROR_CODE);
END LOOP;
end;

Putting cursor with 2 tables in a variable in Oracle PL/SQL

I'm still new to oracle pl/sql so please bear with me. How do I put a cursor with 2 tables inside a variable rowtype? Is it possible to put 2 table rowtypes inside a single variable?
PROCEDURE testproc IS
l_var table1%ROWTYPE;
CURSOR cur1 IS
Select *
From table1, table2
where table1.id = table2.id;
BEGIN
OPEN cur1;
LOOP
FETCH cur1 INTO l_var;
PROCEDURE testproc IS
CURSOR cur1 IS
SELECT table1.field1, table1.field2, table2.field1, table2.field2
FROM table1
INNER JOIN table2 on table1.id = table2.id;
l_var cur1%ROWTYPE;
BEGIN
OPEN cur1
WHILE cur1%ISOPEN LOOP
FETCH cur1 INTO l_var
etc.
Your l_var should be from the cur1 row type:
PROCEDURE testproc IS
CURSOR cur1 IS
select *
from table1, table2
where table1.id = table2.id;
l_var cur1%ROWTYPE;
BEGIN
OPEN cur1;
LOOP
FETCH cur1 INTO l_var;
Because there are two ID columns (one from TABLE1, one from TABLE2) you can't use SELECT *... in this cursor or you'll get something like
ORA-06550: line 7, column 9: PLS-00402: alias required in SELECT list of
cursor to avoid duplicate column names
(at least in Oracle 11.2).
Try the following:
PROCEDURE testproc IS
CURSOR cur1 IS
Select t1.ID, t1.COLx, t2.COLy
From table1 t1,
table2 t2
where table1.id = table2.id;
l_var cur1%ROWTYPE;
BEGIN
OPEN cur1;
LOOP
FETCH cur1 INTO l_var;
EXIT WHEN cur1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('ID=' || l_var.ID ||
'COLx=' || l_var.COLx ||
'COLy=' || l_var.COLy);
END LOOP;
CLOSE cur1;
END testproc;
I included columns COLx and COLy in the cursor SELECT list as I don't know what what columns you actually have on your tables - replace them with whatever makes sense.
Share and enjoy.

How to pass cursor values into variable?

I am trying to read values from two column1, column2 from table1 using cursor. Then I want to pass these values to another cursor or select into statement
so my PL/Sql script will use the values of these two columns to get data from another table called table2
Is this possible? And what's the best and fastest way to do something like that?
Thanks :)
Yes, it's possible to pass cursor values into variables. Just use fetch <cursor_name> into <variable_list> to get one more row from a cursor. After that you can use the variables in where clause of some select into statement. E.g.,
declare
cursor c1 is select col1, col2 from table1;
l_col1 table1.col1%type;
l_col2 table1.col2%type;
l_col3 table2.col3%type;
begin
open c1;
loop
fetch c1 into l_col1, l_col2;
exit when c1%notfound;
select col3
into l_col3
from table2 t
where t.col1 = l_col1 --Assuming there is exactly one row in table2
and t.col2 = l_col2; --satisfying these conditions
end loop;
close c1;
end;
If you use an implicit cursor, then it's even simpler:
declare
l_col3 table2.col3%type;
begin
for i in (select col1, col2 from table1)
loop
select col3
into l_col3
from table2 t
where t.col1 = i.col1 --Assuming there is exactly one row in table2
and t.col2 = i.col2; --satisfying these conditions
end loop;
end;
In these examples, it's more efficient to use a subquery
begin
for i in (select t1.col1
, t1.col2
, (select t2.col3
from table2 t2
where t2.col1 = t1.col1 --Assuming there is atmost one such
and t2.col2 = t1.col2 --row in table2
) col3
from table1 t1)
loop
...
end loop;
end;

Resources