PL SQL - Resembling (!a?0:a) functionality - oracle

I have a simple query in a cursor
Cursor some_cursor IS
select
sum(some_field)
from some_table table_1
where
table_1.TYPE =1
AND TO_CHAR(table_1.date,'YYYYMMDD') = '20090905'
AND table_1.f2 = 962
AND table_1.f3 = 41813;
Then i do
fetch some_cursor into some_var;--some_var is of type Number, cursor is open
When I run this query, there's the chance that some_var will be NULL. In that case, id like it to take the 0 value;
Something like
--C like pseudocode of what I want
(some_cursor!=Null?(fetch some_cursor into some_var):(some_var:=0))
Is there a way to do this? I was thinking of rewriting the above query to
Cursor some_cursor IS
select
sum(some_field),count(*)
from some_table table_1
where
table_1.TYPE =1
AND TO_CHAR(table_1.date,'YYYYMMDD') = '20090905'
AND table_1.f2 = 962
AND table_1.f3 = 41813;
and then writing
fetch some_cursor into some_var,some_counter;
if (some_counter = 0) then
begin
some_var :=0;
end
but this implies rewriting 10 cursors (yes, not so many). Maybe plsql has a cleaner way.
Thanks in advance

Try:
SELECT NVL(SUM(some_field),0)
from some_table table_1
where table_1.TYPE =1
AND TO_CHAR(table_1.date,'YYYYMMDD') = '20090905'
AND table_1.f2 = 962
AND table_1.f3 = 41813;

You might correct a couple of other issues while you're there:
Use implicit cursors instead of explicit
Don't apply a function to a table column if you can avoid it.
thus:
Declare
some_var Number;
Begin
select Coalesce(sum(some_field),0)
into some_var
from some_table table_1
where table_1.TYPE = 1
AND table_1.date >= date '2009-09-05'
AND table_1.date < date '2009-09-06'
AND table_1.f2 = 962
AND table_1.f3 = 41813;
End;

Related

Procedure to update column from function

I have a problem.
I need to create a procedure that fetches or_id loop from table1.
This id from table1 is a parameter calling function that returns two values d1 and d2.
The values d1 and d2 must be updated in table1.
How to do it? The function works correctly but I do not know how to loop it.
CURSOR rec_cur IS
SELECT s.or_id from table1 s;
id number;
a_BASKET_ID varchar(20);
a_ORDER_ID varchar (20);
BEGIN open rec_cur; loop
fetch rec_cur into number;
EXIT WHEN rec_cur%NOTFOUND;
SELECT cut(v_param,1,'#') a_BASKET_ID,
cut(v_param,2,'#') a_ORDER_ID
FROM (SELECT function1(or_id) v_param FROM dual);
UPDATE table1 b
SET BASKET_ID = a_BASKETID,
ORDER_ID = a_ORDERTYPE
WHERE b.or_id = s.or_id;
END LOOP;
This is one of the ugliest codes I've recently seen. Please, for your own sake (as well as ours), learn how to properly format & indent code and make it easier to read and follow.
Furthermore, it is invalid - lacks in DECLARE, there's no END, you declared some variables (a_basket_id, a_order_id) but used another ones (a_basketid, a_ordertype) ... quite a mess.
As of your question: as far as I understood, everything can be done in a single UPDATE statement, no PL/SQL is needed:
update table1 set
basket_id = cut(function1(or_id), 1, '#'),
order_id = cut(function1(or_id), 2, '#');
If you insist on PL/SQL, have a look at this: I've used cursor FOR loop as it is simpler to maintain than explicit cursor (as you don't have to create cursor variable(s), open the cursor, worry about exiting the loop, close the cursor - Oracle does it for you). Although you don't need local variables at all (nor PL/SQL, as I've already said), I let them be.
declare
a_basket_id table1.basket_id%type;
a_order_id table1.order_id%type;
begin
for cur_r in (select or_id from table1) loop
a_basket_id := cut(function1(cur_r.or_id), 1, '#');
a_order_id := cut(function1(cur_r.or_id), 2, '#');
update table1 set
basket_id = a_basket_id
order_id = a_order_id
where or_id = cur_r.or_id;
end loop;
end;

PLS 00357 Error- Table, View or Sequence "txt.col1" not allowed in the context

I have created one Stored Procedure. In that Stored Proc I want if the value of col1 & col2 match with employee then insert the unique record of the employee. If not found then match the value of col1, col2 & col3 with employee match then insert the value. If also not found while match all these column then insert the record by using another column.
Also one more thing that i want find list of values like emp_id by passing the another column value and if a single record can not match then make emp_id as NULL.
create or replace procedure sp_ex
AS
empID_in varchar2(10);
fname_in varchar2(20);
lname_in varchar2(30);
---------
type record is ref cursor return txt%rowtype; --Staging table
v_rc record;
rc rc%rowtype;
begin
open v_rc for select * from txt;
loop
fetch v_rc into rc;
exit when v_rc%notfound;
loop
select col1 from tbl1
Where EXISTS (select col1 from tbl1 where tbl1.col1 = rc.col1);
IF txt.col1 = rc.col1 AND txt.col2 = rc.col2 THEN
insert into main_table select distinct * from txt where txt.col2 = rc.col2;
ELSIF txt.col1 = rc.col1 AND txt.col2 = rc.col2 AND txt.col3 = rc.col3 THEN
insert into main_table select distinct * from txt where txt.col2 = rc.col2;
ELSE
insert into main_table select * from txt where txt.col4 = rc.col4;
end if;
end loop;
close v_rc;
end sp_ex;
I found an error while compile this Store Procedure PLS-00357: Table,View Or Sequence reference not allowed in this context. How to resolve this issue and how to insert value from staging to main table while using CASE or IF ELSIF statement. Could you please help me so that i can compile the Stored Proc.
Since I don't have your database to work with it's difficult to be 100% certain, but to my eye the line which reads
rc rc%rowtype;
should say
rc txt%rowtype;
You've defined the cursor v_rc as returning txt%rowtype, and your SQL statement used with this cursor is select * from txt, but that data type is at odds with the definition of rc. Thus, it appears you need to change rc as shown.
It also looks like the LOOP statement which comes immediately after exit when v_rc%notfound; should be removed, as there's nothing after that which would terminate that loop.
In addition, you have many references to columns in the txt table, e.g. IF txt.col1 = rc.col1. You can't refer to values in a table in this manner. I'm not quite sure what you're trying to do here so I can't really suggest anything.
Also, the statement
select col1 from tbl1
Where EXISTS (select col1 from tbl1 where tbl1.col1 = rc.col1);
is selecting a column from the database, but isn't putting it anywhere. This should be either a singleton SELECT (SELECT..INTO) or a cursor.
One more thing: you can't use distinct *. You need to use a column list with distinct.
Perhaps the following would be close to what you're trying to do:
create or replace procedure sp_ex
AS
begin
FOR rc IN (SELECT * FROM TXT)
LOOP
FOR t1 IN (SELECT *
FROM TBL1
WHERE TBL1.COL1 = rc.COL1)
LOOP
IF t1.COL1 = rc.COL1 AND
t1.COL2 = rc.COL2
THEN
insert into main_table
select *
from txt
where txt.col2 = rc.col2;
ELSIF t1.col1 = rc.col1 AND
t1.col2 = rc.col2 AND
t1.col3 = rc.col3
THEN
insert into main_table
select *
from txt
where txt.col2 = rc.col2;
ELSE
insert into main_table
select *
from txt
where txt.col4 = rc.col4;
END IF;
END LOOP; -- t1
END LOOP; -- rc
end sp_ex;
Best of luck.

PL/SQL alias/variable to existing SQL query

I am very new to PL/SQL and would like to be able to take advantage of the procedural options of oracle SQL.
I have a query that I would like to transform into a PL/SQL query that takes the SKU you want and inputs whenever you have the word SKU in your query.
Example:
SELECT A.*, CT.CUSTOMER_ID, CT.ORDERS
FROM CUSTOMER_TABLE CT
RIGHT JOIN
(
SELECT OT.COLUMN_ID, OT.SKU, OT.ORDERS
FROM ORDERS_TABLE OT
WHERE OT.SKU = 123
)A
ON CT.ORDERS = OT.ORDERS
AND CT.SKU IS > 0
This is strictly an example so I know that what I am asking is pointless, however, if I had a lengthier query that contains more than 2 or 3 "SKU" inputs then I would like to have a variable/parameter for it.
My ideal code would be:
DECLARE
SKUS INTEGER := 123;
BEGIN
SELECT A.*, CT.CUSTOMER_ID
FROM CUSTOMER_TABLE CT
RIGHT JOIN
(
SELECT OT.COLUMN_ID, OT.SKU, OT.ORDERS
FROM ORDERS_TABLE OT
WHERE #SKUS
)A
ON CT.ORDERS = OT.ORDERS
AND #SKUS IS > 0
END;
/
I am not sure if that is proper syntax but I hope the idea makes sense of what I am asking.
Thank you!
PL/SQL handles such situations very easily. Keep your SQL statement exactly the same and replace literals with your variable, or even a cursor parameter. I moved your SELECT into an explicit cursor to show how you can easily declare a record based on the cursor as well:
DECLARE
c_sku CONSTANT INTEGER := 123;
CURSOR my_cur (sku_in IN INTEGER)
IS
SELECT a.*, ct.customer_id, ct.orders
FROM customer_table ct
RIGHT JOIN (SELECT ot.column_id, ot.sku, ot.orders
FROM orders_table ot
WHERE ot.sku = sku_in) a
ON ct.orders = ot.orders AND sku_in > 0;
l_info my_cur%ROWTYPE;
BEGIN
OPEN my_cur (c_sku);
FETCH my_cur INTO l_info;
CLOSE my_cur;
END;

Are there any impact of update statement on for loop statement in oracle?

I have nested for loop which iterates same table. In inner loop I update a column in same table. But in for loop condition I check that updated column and I need to check this column not in the beginning but dynamically, so my for loop iterations will maybe greatly decrease.
Am I doing this correct or is for statement will not see updated column?
declare
control number(1);
dup number(10);
res varchar2(5);--TRUE or FALSE
BEGIN
dup :=0;
control :=0;
FOR aRow IN (SELECT MI_PRINX, geoloc,durum, ROWID FROM ORAHAN where durum=0)
LOOP
FOR bRow IN (SELECT MI_PRINX, geoloc, ROWID FROM ORAHAN WHERE ROWID>aRow.ROWID AND durum=0)
LOOP
BEGIN
--dbms_output.put_line('aRow' || aRow.Mi_Prinx || ' bRow' || bRow.Mi_Prinx);
select SDO_GEOM.RELATE(aRow.geoloc,'anyinteract', bRow.Geoloc,0.02) into res from dual;
if (res='TRUE')
THEN
Insert INTO ORAHANCROSSES values (aRow.MI_PRINX,bRow.MI_PRINX);
UPDATE ORAHAN SET DURUM=1 where rowid=bRow.Rowid;
control :=1;
--dbms_output.put_line(' added');
END IF;
EXCEPTION
WHEN DUP_VAL_ON_INDEX
THEN
dup := dup+1;
--dbms_output.put_line('duplicate');
--continue;
END;
END LOOP;
IF(control =1)
THEN
UPDATE ORAHAN SET DURUM=1 WHERE rowid=aRow.Rowid;
END IF;
control :=0;
END LOOP;
dbms_output.put_line('duplicate: '||dup);
END ;
Note: I use oracle 11g and pl/sql developer
Sorry my english.
Yes, the FOR statement will not see the updated DURUM column because the FOR statement will see all data as they were when the query was started! This is called read consistency and Oracle accomplishes this by using the generated UNDO data. That means it'll have more and more work to do (==run slower) as your FOR loop advances and the base table is updated!
It also means that your implementation will eventually run into a ORA-01555: snapshot too old error when the UNDO tablespace is exhausted.
You'll be probably better off using a SQL MERGE statement which should also run much faster.
e.g.:
Merge Into ORAHANCROSSES C
Using (Select aROW.MI_PRINX aROW_MI_PRIX,
aROW.GEOLOC aROW_GEOLOC,
bROW.MI_PRINX bROW_MI_PRIX,
bROW.GEOLOC bROW_GEOLOC,
SDO_GEOM.RELATE(aRow.geoloc,'anyinteract', bRow.Geoloc,0.02) RES
From ORAHAN aROW,
ORAHAN bROW
Where aROW.ROWID < bROW.ROWID
) Q
On (C.MI_PRIX1 = Q.aROW_MI_PRIX
and C.MI_PRIX2 = Q.bROW_MI_PRIX)
When Matched Then
Delete Where Q.RES = 'FALSE'
When Not Matched Then
Insert Values (Q.aROW_MI_PRIX, Q.bROW_MI_PRIX)
Where Q.RES = 'TRUE'
;
I'm not sure what you're trying to accomplish by ROWID>aRow.ROWID though
To use a certain order (in this case MI_PRINX) use the following technique:
Merge Into ORAHANCROSSES C
Using (With D as (select T.*, ROWNUM RN from (select MI_PRINX, GEOLOC from ORAHAN order by MI_PRINX) T)
Select aROW.MI_PRINX aROW_MI_PRIX,
aROW.GEOLOC aROW_GEOLOC,
bROW.MI_PRINX bROW_MI_PRIX,
bROW.GEOLOC bROW_GEOLOC,
SDO_GEOM.RELATE(aRow.geoloc,'anyinteract', bRow.Geoloc,0.02) RES
From D aROW,
D bROW
Where aROW.RN < bROW.RN
) Q
On (C.MI_PRIX1 = Q.aROW_MI_PRIX
and C.MI_PRIX2 = Q.bROW_MI_PRIX)
When Matched Then
Delete Where Q.RES = 'FALSE'
When Not Matched Then
Insert Values (Q.aROW_MI_PRIX, Q.bROW_MI_PRIX)
Where Q.RES = 'TRUE'
;
In case the query is taking too long, you might select * from v$session_longops where seconds_remaining >0 to find out when it'll be finished.

Using Rownum in Cursor Bulk Collect Oracle

I'm trying to use the rownum to simulate a column autonumbered as I need to use it as an ID. Since it is an ID, I look at the final table if no record with MAX (ID).
The problem I have is when I want to do arithmetic operations within the cursor or when you invoke, or when you want to use a function. The ROWNUM (v_id) field is empty me when I want to print with DBMS_OUTPUT . Anyone have any idea how to solve it without using sequences ?
Here put the sample code.
declare
max_id number;
CURSOR INSRT(w_max number) IS
SELECT f_max_fact_sap(to_number(V_ID),w_max) AS V_ID,Seriei,serief
FROM (SELECT To_Char(ROWNUM) AS V_ID, A.*
FROM (SELECT DISTINCT a.matnr, a.seriei, a.serief,a.xblnr,a.fecha_sap, ((SERIEF-SERIEI)+1) AS rango
FROM SOPFUN.TT_ZMOVIMI_FACTURADAS a
WHERE 0 =(SELECT COUNT(1)
FROM PA_ZMOVIMI_FACTURADAS B
WHERE A.SERIEI = B.SERIEI
AND A.SERIEF = B.SERIEF
AND A.MATNR = B.MATNR
AND A.FECHA_SAP=B.FECHA_SAP)
AND A.FECHA_SAP IS NOT NULL) A);
TYPE T_INSRT IS TABLE OF INSRT%ROWTYPE INDEX BY PLS_INTEGER;
V_INSRT T_INSRT;
begin
SELECT Max(Nvl(ID,10000)) INTO MAX_ID-- To Proof because the table is empty
FROM PA_ZMOVIMI_FACTURADAS;
OPEN INSRT(MAX_ID);
LOOP
FETCH INSRT BULK COLLECT INTO V_INSRT LIMIT 1000;
FOR I IN 1 .. V_INSRT.Count loop
DBMS_OUTPUT.PUT_LINE('ID: ' ||V_INSRT(I).V_ID||' SI: '||V_INSRT(I).SERIEI||' SI: '||V_INSRT(I).SERIEF||' OPERACION: '||to_char(f_max_fact_sap(V_INSRT(I).V_ID,MAX_ID)));
end loop;
EXIT WHEN INSRT%NOTFOUND;
END LOOP;
end;

Resources