Using Cursor in oracle SQL - sql

I want to perform the delete operation in 2 tables with a given id here is sudo code
declare
cursor del_id is
select person_id from table_1 where termination is true
begin
for id_x in del_id
delete from table_X where id=id_x
delete from tabele_Y where id=id_x
How to do that ? i can't directly use my cursor please help.
I just try to print my id
begin
for id in del_id
LOOP
dbms_output.put_line(id);
END LOOP;
end;
Getting this error
Error report -
ORA-06550: line 11, column 3:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'

To print the values from a cursor, you need to explicitly write the columns you want; dbms_output.put_line can not handle a row that may contain many columns with different types, so you need to pass it a string.
SQL> declare
2 cursor del_id is select 1 as one, 2 as two from dual;
3 begin
4 FOR id IN del_id
5 LOOP
6 dbms_output.put_line(id.one || ' - ' || id.two);
7 END LOOP;
8 end;
9 /
1 - 2
PL/SQL procedure successfully completed.
If you need to use the values from a cursor in some statement, a DELETE in your question, you need to do the same, by explicitly writing the column name; for example:
declare
cursor del_id is select 1 as one, 2 as two from dual;
begin
FOR id IN del_id
LOOP
delete from someTable where someColumn = id.one;
END LOOP;
end;

You do not need a cursor:
BEGIN
DELETE FROM table_X
WHERE id IN ( select person_id from table_1 where termination is true );
DELETE FROM table_Y
WHERE id IN ( select person_id from table_1 where termination is true );
END;
/
You could also use a collection:
CREATE TYPE id_type IS TABLE OF INTEGER;
/
DECLARE
ids id_type;
BEGIN
SELECT person_id
BULK COLLECT INTO ids
FROM table_1
WHERE termination is true;
DELETE FROM table_X
WHERE id MEMBER OF ids;
DELETE FROM table_Y
WHERE id MEMBER OF ids;
END;
/

Related

How to check if row exists before SELECT INTO statement in Oracle SQL

I'm using Oracle SQL and have a procedure that is doing some operations on tables. During the procedure there is a "SELECT x INTO y FROM TABLE z WHERE..." statement inside a loop. Unfortunatly during the procedure I can't guarante that there is always a row to the corresponding where condition because it changes dynamically.
Is it possible to check if a row exists before the statement? I was thinking of sth like "if exists(select ...) then SELECT X INTO y..."
Thanks for the help!
Jack
Well, there's no point in checking it first, and re-using the same statement again.
You could handle the exception (possibly in an inner BEGIN-EXCEPTION-END block):
declare
y number;
begin
begin --> inner block starts here
select x into y from z where ...
insert into ...
exception
-- handle it, somehow; I chose not to do anything
when no_data_found then
null;
end; --> inner block ends here
end;
Or, if you used cursor FOR loop, you wouldn't have to handle it because - if select returns x, insert would run. Otherwise, nothing in that loop would ever be executed:
begin
for cur_r in (select x from z where ...) loop
insert into ...
end loop;
end;
An exception handler as in Littlefoot's answer is the most correct and explicit approach, however just for completeness you might also consider using an aggregate.
Value 'X' exists in the table:
declare
p_someparam varchar2(1) := 'X';
l_somevalue varchar2(1);
l_check number;
begin
select max(dummy), count(*) into l_somevalue, l_check
from dual d
where d.dummy = p_someparam;
dbms_output.put_line('Result: '||l_somevalue);
dbms_output.put_line(l_check||' row(s) found');
end;
Result: X
1 row(s) found
Value 'Z' does not exist in the table:
declare
p_someparam varchar2(1) := 'Z';
l_somevalue varchar2(1);
l_check number;
begin
select max(dummy), count(*) into l_somevalue, l_check
from dual d
where d.dummy = p_someparam;
dbms_output.put_line('Result: '||l_somevalue);
dbms_output.put_line(l_check||' row(s) found');
end;
Result:
0 row(s) found
You can add logic to handle the cases where the count check is 0 or greater than 1.
If you are having procedure then I should say use if statement and then write the sql:
select some_column into some_variable from tablename where condition
IF somevariable not in (<list of values separated by comma>)THEN
{statements to execute }
END IF;

How do I run a count of rows over a database link?

This code, works. It runs a row count the way you'd expect, I want to tweek it, mostly to do a count over a db_link for tables dictated as I see fit.
declare
n number;
begin
for i in (select table_name from user_tables) loop
execute immediate' select count(*) from '||i.table_name into n;
dbms_output.put_line('Table Name: '||i.table_name||' Count of Row''s: '||n);
end loop;
end;
/
So, this is the adapted code... it includes a variable with the name of the link. (The link works fine) But how to reference it is probably where I'm coming unstuck.
declare
l_dblink varchar2(100) := 'DB1';
n number;
begin
for i in (select table_name from my_tables) loop
execute immediate' select count(*) from '||i.table_name#||l_dblink into n;
dbms_output.put_line('Table Name: '||i.table_name||' Count of Row''s: '||n);
end loop;
end;
/
Can someone please have a look and tell me where I'm going wrong? I just want the SQL to pick up the table names from a local table, and then use the names to count the rows in those tables, which reside in the remote database.
Monkey is on the wrong tree and can't eat a banana.
SQL> create table my_tables (table_name varchar2(20));
Table created.
SQL> insert into my_tables values ('dual');
1 row created.
SQL> set serveroutput on
SQL> declare
2 l_dblink varchar2(100) := 'db1';
3 n number;
4 begin
5 for i in (select table_name from my_tables) -- has to be like this
6 loop -- vvv
7 execute immediate' select count(*) from '||i.table_name || '#' || l_dblink into n;
8 dbms_output.put_line('Table Name: '||i.table_name||' Count of Row''s: '||n);
9 end loop;
10 end;
11 /
Table Name: dual Count of Row's: 1
PL/SQL procedure successfully completed.

Oracle11g variable declaration

I'm trying to increment a sequence by the grater value from an id column (numeric type but not int). For this I've tried to declare an int variable and selecting into it the max value from the table like this:
declare
last_id int;
begin
select max(id) into last_id from my_table;
alter sequence my_table_seq increment by last_id;
end;
However, I'm getting this error
ORA-06550: line 2, column 19:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
:= . ( # % ; not null range default character
I'm using Oracle 11g. Here's a screenshot
declare
last_id number;
begin
select max(id) into last_id from my_table;
execute immediate 'alter sequence my_table_seq increment by ' || to_char(last_id);
end;
Use any of oracle Numeric Data Types instead of int
Use execute immediate for any DDN in anonymous block
Created similar objects and it works as expected.
Suggest you to try the below:
1.Change your column to from ID to something_id.
2.Remove double quotes from schema and table name (just give schema.table_name)
3.Ensure you do not miss a semi colon at the end of statement and all variables declared properly.
If still doesn't work then go for trial and error method.
Just take simple block and try printing the ID value alone.
Then keep adding your logic (assign value to last_id and try print last_id).
Then finally add your execute immediate statement and see if it works.
Create table general_discount_type
(id number);
-- Table created.
Begin
insert into general_discount_type values(101);
insert into general_discount_type values(102);
insert into general_discount_type values(103);
insert into general_discount_type values(104);
End;
-- Statement processed.
Create sequence general_discount_type_seq
start with 1
increment by 1
NOCACHE
NOCYCLE;
-- Sequence created.
Declare
last_id number;
Begin
select max(id) into last_id from MAHI_SCHEMA.general_discount_type;
execute immediate 'alter sequence MAHI_SCHEMA.general_discount_type_seq increment by '
|| to_char(last_id);
dbms_output.put_line('Value in last_id is : ' || last_id);
End;
-- Value in last_id is : 104
Output

How to set a default value for a query that returns no rows?

I have created a trigger with a select statement and I want to say if this select statement does not return any rows then put a 0 in the variable "auxiliar". I tried to use NVL(auxiliar,0) but it does not work. How can I do this?
SELECT NVL(salary,0) INTO auxiliar FROM BILL WHERE code=:NEW.code;
[UPDATED] My trigger code:
IF preCondicio THEN
KMpendents:=coalesce(SELECT rev_pendent_km
INTO KMpendents
FROM REV_PENDENT
WHERE rev_pendent_vehicle_codi=:NEW.lloguer_vehicle_codi,0);
IF KMtotals+KMpendents>=15000 THEN
SELECT venedor_codi
INTO venedorCodi
FROM venedor
WHERE venedor_alta=(
SELECT MAX(venedor_alta)
FROM venedor
WHERE venedor_delegacio_codi=(
SELECT venedor_delegacio_codi
FROM venedor
WHERE venedor_codi=:NEW.lloguer_venedor_codi));
INSERT INTO REVISIONS VALUES(:NEW.lloguer_vehicle_codi,:NEW.lloguer_dataf,KMtotals+KMpendents,venedorCodi);
IF KMpendents!=0 THEN
DELETE FROM REV_PENDENT
WHERE rev_pendent_vehicle_codi=:NEW.lloguer_vehicle_codi;
END IF;
ELSE
IF KMpendents!=0 THEN
UPDATE REV_PENDENT SET rev_pendent_km=KMtotals+KMpendents WHERE rev_pendent_vehicle_codi=:NEW.lloguer_vehicle_codi;
ELSE INSERT INTO REV_PENDENT VALUES(:NEW.lloguer_vehicle_codi,KMtotals,:NEW.lloguer_dataf);
END IF;
END IF;
END IF;
The variable KMpendents is equivalent to the variable auxiliar which I told before the updated. But Oracle shows me these errors:
PLS-00103: Encountered the symbol "," when expecting one of the
following: . ( * # % & - + ; / at for mod remainder rem and or group
having intersect minus order start union where connect || indicator
multiset
If the select clause contains only aggregate functions then there will always be at least one row returned.
SELECT coalesce(sum(salary),0)
INTO auxiliar
FROM BILL
WHERE code=:NEW.code;
BEGIN
SELECT NVL(salary,0) INTO auxiliar FROM BILL WHERE code=:NEW.code;
EXCEPTION
WHEN NOTFOUND THEN
auxiliar := 0;
END;
Or you can use SQL%NOTFOUND too to check if the statement returned no rows.
CREATE or REPLACE TRIGGER trigger_name
BEFORE INSERT
ON table_name
[ FOR EACH ROW ]
DECLARE
-- variable declarations
BEGIN
....
....
BEGIN
SELECT NVL(salary,0) INTO auxiliar FROM BILL WHERE code=:NEW.code;
EXCEPTION
WHEN NOTFOUND THEN
auxiliar := 0;
END;
--Yes, this is possible and valid.
EXCEPTION
WHEN ...
-- exception handling
END;

How to return multiple rows from oracle stored procedure from multiple cursors?

I need to have stored procedure where I can run multiple cursors.
Loop over each cursor and then do some operation on each row.
This way I will have the desired result from these cursors. Result of such multiple cursors then needs to be union with some other rows and then filtered out and return those rows finally from the proc.
Please note that each cusror and another queries will have same columns.
I am not sure how to do this in the oracle.
Please help me out.
create or replace PROCEDURE test_proc
(
-- some inputs
hc_cursor OUT SYS_REFCURSOR
)
IS
cursor cursor_one is
SELECT * FROM table_one ;
BEGIN
FOR current_row in cursor_one
loop
-- do some modification on each row and return each modified row
end loop;
cursor cursor_two is
SELECT * FROM table_one ;
BEGIN
FOR current_row in cursor_two
loop
-- do some modification on each row and return each modified row
-- append to result from first cursor
end loop;
-- union results from both these cusrors with some another query
-- now filter these records on some criterais
-- return finally
END;
My suggestion is going to be insert the rows from your cursor into a temporary table. Then join the temporary table with your existing table for the filter criteria you mention. Psuedocode:
create or replace function my_func
return sysrefcursor
is
cursor cursor_one is
SELECT * FROM table_one ;
cursor cursor_two is
SELECT * FROM table_one ;
BEGIN
FOR current_row in cursor_one
loop
-- do some modification on each row and insert into temporary table
end loop;
FOR current_row in cursor_two
loop
-- do some modification on each row and insert into temporary table
end loop;
-- results from cursor 1 and 2 exist in temporary table
open out_cursor for
select t.* from
my_temp_table t
join
my_other_table tt
on (t.col1 = tt.col1) -- or whatever columns are appropriate
where t.col2 = 'some criteria' -- or whatever filter criteria you like.
return out_cursor;
END;
create type emp_obj AS object
(
empno NUMBER (4)
,ename VARCHAR2(10)
,sal number(7,2)
,job varchar2(9)
);
CREATE TYPE EMP_NT AS TABLE OF emp_OBJ;
create or replace package test_pkg
IS
TYPE abc_cur is REF CURSOR;
procedure test_proc
(
p_rec IN OUT abc_cur
);
END test_pkg;
/
create or replace package body test_pkg
IS
procedure test_proc
(
p_rec IN OUT abc_cur
)
IS
v_emp_nt emp_nt;
BEGIN
SELECT emp_obj(empno,ename,sal,job) BULK COLLECT INTO v_emp_nt FROM EMP;
FOR i in v_emp_nt.first..v_emp_nt.last
LOOP
IF v_emp_nt(i).job='CLERK' THEN
v_emp_nt(i).sal := v_emp_nt(i).sal +200;
ELSIF v_emp_nt(i).job='MANAGER' THEN
v_emp_nt(i).sal := v_emp_nt(i).sal +800;
END IF;
END LOOP;
open p_rec for select * from table(v_emp_nt);
END test_proc;
END test_pkg;
/
As you have seen the code,what i do ,is to get the desired result in nested table(what your cursor is doing) ,and do some manipulation based on the resultant records ,as well as update the nested table.
At the end i will create a cursor from this updated nested table and return the cursor after opening.
Now your question :How can you return append cursor ?
It is simple create two nested table ,do some manipulation on both the nested table
Suppose you have v_emp_nt1 as first nested table ,you do some manipulation on that .
you have another v_emp_nt2 as second nested table ,you do some manipulation on that .
Now your cursor will be like
open p_rec FOR (select * from v_emp_nt1 union select * from v_empnt2);
With this way you can achieve your desired output .
**Note:**The above code is for one nested table ,you need to create another nested table for your code to get complete
create
package my_pkg as
type my_rec is record
(
<list your fields here>
);
type my_rec_tab is table of my_rec;
function get_my_rows
return my_rec_tab pipelined;
end my_pkg;
create
package body my_pkg as
function get_my_rows
return my_rec_tab pipelined
as
begin
for c_cur in (select * from table_one)
loop
-- do some modification on the current row and return the modified row
pipe row (c_cur);
end loop;
for c_cur in (select * from table_one)
loop
-- do some modification on the current row and return the modified row
pipe row (c_cur);
end loop;
return;
end get_my_rows;
end my_pkg;
select * from table(my_pkg.get_my_rows);