Update using loop in Oracle - oracle

BEGIN
FOR J IN (select DISTINCT S_NUMBER from WMWHSE3.NUMBER_SHIPTO)
LOOP
UPDATE wmwhse3.orders
SET
orders.susr4 = (select S_NUMBER from WMWHSE3.NUMBER_SHIPTO where S_NUMBER = J)
where orders.c_company = (select SHIPTO from WMWHSE3.NUMBER_SHIPTO where S_NUMBER = J)
and orders.orderkey in (SELECT a.orderkey FROM wmwhse3.orders a INNER JOIN wmwhse3.wavedetail b ON a.orderkey = b.orderkey where b.wavekey = '0000000086');
END LOOP;
END;

Looks like you're overcomplicating things (and making everything slower than it should be, as row-by-row processing (in a loop) is usually much slower than set processing).
Therefore, how about a single update, without PL/SQL? Something like this:
update orders o set
o.susr4 = (select n.s_number
from number_shipto n
where n.shipto = o.c_company
)
where exists (select null from wavedetail b
where b.orderkey = o.orderkey
and b.wavekey = '0000000086'
);

Related

UPDATE query is slow in combination with RETURNING INTO clause

I have update query which returns updated rows ID. Execution time of query is about 90 seconds. When i remove Returning clause, then execution time is 1ms.
Table update_table has 39000 rows.
Query updates 0 rows in this case. When updates 3 rows- execution time is same.
DECLARE
type intTable IS TABLE OF INTEGER;
idCol intTable;
BEGIN
UPDATE
update_table
SET
prop1 = 3, prop2 = NULL
WHERE EXISTS (
SELECT null FROM update_table f
INNER JOIN rel_table1 u ON f.ID= u.ID
INNER JOIN rel_table2 VP ON f.another_ID = VP.another_ID
WHERE (u.prop1 = 3)
AND VP.prop1 = 1
AND (u.prop2 = 75)
AND f.ID = update_table.ID
)
ReTURNING ID BULK COLLECT INTO idCol;
.
.
.
END;
Why returning clause slows down query?
A good part of using Oracle is knowing what is "supposed" to happen and what isn't.
Adding a RETURNING INTO clause is not "supposed" to make your update run more slowly. When something happens that isn't supposed to happen, check Oracle's support site to see whether it is a known bug.
In your case, it looks like you are encountering:
Bug 27131648 - SUB OPTIMAL PLAN ON UPDATE STATEMENT WITH RETURNING INTO
I am not sure if there is a patch, but there is a simple workaround: use the UNNEST hint. In your case, that would be:
UPDATE
update_table
SET
prop1 = 3, prop2 = NULL
WHERE EXISTS (
SELECT /*+ UNNEST */ null FROM update_table f
INNER JOIN rel_table1 u ON f.ID= u.ID
INNER JOIN rel_table2 VP ON f.another_ID = VP.another_ID
WHERE (u.prop1 = 3)
AND VP.prop1 = 1
AND (u.prop2 = 75)
AND f.ID = update_table.ID
)
ReTURNING ID BULK COLLECT INTO idCol;
I would recommend splitting it into two parts, first BULK COLLECT and next FORALL collected ID's, both extremely fast and you'll keep being able to further reference updated ID's from idCol.
DECLARE
type intTable IS TABLE OF INTEGER;
idCol intTable;
BEGIN
SELECT f.id
BULK COLLECT INTO idCol
FROM update_table f
INNER JOIN rel_table1 u ON f.ID= u.ID
INNER JOIN rel_table2 VP ON f.another_ID = VP.another_ID
WHERE (u.prop1 = 3)
AND VP.prop1 = 1
AND (u.prop2 = 75);
FORALL indx IN 1..idCol.COUNT
UPDATE update_table
SET prop1 = 3, prop2 = NULL
WHERE id = idCol(indx);
.
.
.
END;
I hope I helped!

Cursor loop update Table by many values

DECLARE
CURSOR contacts
IS
SELECT SUM (budget) AS budget
FROM et_bp_gl_account a, et_bp_fact f
WHERE f.gl_account_id = a.gl_account_id
AND total_flag = 0
GROUP BY month_id, org_unit_id;
BEGIN
FOR r IN contacts
LOOP
UPDATE et_bp_fact
SET budget = r.budget
WHERE gl_account_id IN (SELECT total_element
FROM et_bp_gl_account g, et_bp_fact f
WHERE f.gl_account_id = g.gl_account_id);
END LOOP;
END;
I want to update the table ET_BP_FACT by many values example(25,50,75)
returned from Cursor but when i execute the table updated by (25,25,25)
I think there an issue in the loop
That's because the UPDATE's WHERE clause doesn't "reference" cursor's values. Something like this:
DECLARE
CURSOR contacts
IS
SELECT month_id, org_unit_id, --> include additional columns here ...
SUM (budget) AS budget
FROM et_bp_gl_account a, et_bp_fact f
WHERE f.gl_account_id = a.gl_account_id
AND total_flag = 0
GROUP BY month_id, org_unit_id;
BEGIN
FOR r IN contacts
LOOP
UPDATE et_bp_fact
SET budget = r.budget
WHERE gl_account_id IN
(SELECT total_element
FROM et_bp_gl_account g, et_bp_fact f
WHERE f.gl_account_id = g.gl_account_id
--
AND f.org_unit_id = r.org_unit_id --> ... and reference them here ...
AND g.month_id = r.month_id);
--> ... or, possibly, here
END LOOP;
END;
I'd suggest you to always precede column names with table aliases, because - looking at your code, there's no way to guess which table(s) MONTH_ID and ORG_UNIT_ID belong to, so my code might (or might not) work, but I hope you got the general idea.

Oracle SQL Compiles, but seems to break out of loop when select loop returns nothing/empty

I am still quite new to pl/sql... but here I have a script that works well when data exists...But I also need it to work when the select statement does NOT have any value to return (is empty). When the select statement is empty, dbms_output.put_line does not show anything, even when I write a complete string there, so I think the loop is exiting when nothing is found. Online, I stumbled upon Union and Exception...but nothing that seems to work accurately in a loop.
WHAT I AM ASKING IS: How to run this script even when data DOES NOT EXIST??? AND How do I tell which value is empty? Is it the postal_code or the fipcode, or BOTH?
DECLARE
F33_35 VARCHAR2(3) := rpad('0',3,'0');
BEGIN
FOR tabVals IN (SELECT DISTINCT pa.postal_code, fip.fipcode
FROM voucher v
INNER JOIN cc_authorization cca
ON cca.cc_authorization_id = v.cc_authorization_id
INNER JOIN episode e
ON cca.subsidy_id = e.episode_id
INNER JOIN case c
ON c.case_id = e.case_id
INNER JOIN invoice_item ii
ON ii.voucher_id = v.voucher_id
INNER JOIN invoice i
ON i.invoice_id = ii.invoice_id
INNER JOIN svcperiod_info s
ON s.svcperiod_id = i.svcperiod_id
INNER JOIN party_contact_mech pcm
ON pcm.party_id = c.identifying_party
INNER JOIN postal_address pa
ON pcm.contact_mech_id = pa.contact_mech_id
INNER JOIN VT_FIPS_MAP fip
ON substr(pa.POSTAL_CODE,1,5) = substr(fip.ZIPCODE,1,5)
WHERE ii.status = 8 AND
ii.net_amount > 0 AND
s.end_dt >= DATE'2017-04-01' AND
s.end_dt < DATE'2017-04-30' AND
c.case_id = 104570 AND
pcm.contact_mech_purpose_cd = 4 AND
pcm.current_ind = 1
ORDER BY POSTAL_CODE, FIPCODE)
LOOP
F33_35 := substr(F33_35 || tabVals.fipcode,-3);
DBMS_OUTPUT.PUT_LINE('F String: 50' || F33_35);
--- RESET certain variables for loop
F33_35 := rpad('0',3,'0');
END LOOP;
END;
you are correct when you say
...I think the loop is exiting when nothing is found...
the cursor for loop can be considered a special case of regular loop.
eg.
for a in (<select statement>) LOOP
--do stuff here
END LOOP;
is effectively just shorthand for
declare
cursor c is
<select statement>;
a c%ROWTYPE;
begin
open c;
LOOP
FETCH c INTO a;
EXIT WHEN c%NOTFOUND;
--do stuff here
END LOOP;
so you can no see that no code would be executed if the query returns 0 rows.
possible the best solution for running code if your statement returns 0 rows is to set a flag inside the loop and check it afterwards. (as suggested by #tony-Andrews )
a second option is to rewrite as the long version of the loop above and move the EXIT WHEN statement to after the "do stuff"

Oracle Merge statement error in procedure package body

I'm struggling trying to make this procedure to work, I have the following code inside my package body:
PACKAGE BODY PKG_DM_TRANS_DIMENSIONES AS
PROCEDURE SP_DM_TRANS_DIM_CUENTA AS
vNumRegistrosDimCuentas NUMBER;
BEGIN
SELECT COUNT(*) INTO vNumRegistrosDimCuentas
FROM DIM_CUENTAS;
IF (vNumRegistrosDimCuentas <> 0) THEN
MERGE INTO DIM_CUENTAS DIMC
USING (
SELECT * FROM (
SELECT
DIM.FNT_CUENTA_ID AS DIM_CUENTA_ID,
C.CUE_ID AS FNT_CUENTA_ID,
R.REG_REGION AS REGION,
P.PAI_PAIS AS PAIS,
E.EDI_NOMBRE_EDIFICIO AS EDIFICIO,
C.CUE_CUENTA,
TIC.TIC_TIPO_CONTACTO,
C.CUE_STATUS,
CASE
WHEN DIM.FNT_CUENTA_ID IS NULL THEN 1
WHEN
R.REG_REGION <> DIM.REGION OR
P.PAI_PAIS <> DIM.PAIS OR
E.EDI_NOMBRE_EDIFICIO <> DIM.EDIFICIO OR
C.CUE_CUENTA <> DIM.CUENTA OR
TIC.TIC_TIPO_CONTACTO <> DIM.TIPO_CONTACTO
THEN 2
ELSE 0
END AS TIPO_FILA
FROM STA_EDIFICIOS_EXTRACCION E
LEFT JOIN
STA_PAISES_EXTRACCION P ON E.EDI_PAI_ID = P.PAI_ID
LEFT JOIN
STA_REGIONES_EXTRACCION R ON P.PAI_REG_ID = R.REG_ID
LEFT JOIN
EUB_EDIFICIO_UBICACION EUB ON EUB.EUB_EDI_ID = E.EDI_ID
LEFT JOIN
STA_CUENTAS_EXTRACCION C ON C.CUE_EUB_ID = EUB.EUB_ID
LEFT JOIN
STA_TIPOS_CONTACTO_EXTRACCION TIC ON TIC.TIC_ID = C.CUE_TIC_ID
LEFT JOIN
DIM_CUENTAS DIM ON
(C.CUE_ID = DIM.FNT_CUENTA_ID AND DIM.CUENTA_STATUS = 1)
)
) Q
ON (DIMC.FNT_CUENTA_ID = Q.TIPO_FILA)
WHEN MATCHED THEN
INSERT (DIMC.REGION, DIMC.PAIS, DIMC.EDIFICIO, DIMC.CUENTA, DIMC.TIPO_CONTACTO, DIMC.CUENTA_FECHA_CREACION, DIMC.FNT_CUENTA_ID)
VALUES (Q.REGION, Q.PAIS, Q.EDIFICIO, Q.CUE_CUENTA, Q.TIC_TIPO_CONTACTO, TO_TIMESTAMP(sysdate, 'MM/DD/YYYY HH24:MI:SS'), Q.FNT_CUENTA_ID)
WHEN NOT MATCHED THEN
UPDATE SET DIMC.CUENTA_STATUS = 0 WHERE DIMC.CUENTA_STATUS = 1 -- <- dummy update stmt
ELSE ..... -- else statement code working fine...
END IF;
END SP_DM_TRANS_DIM_CUENTA;
END PKG_DM_TRANS_DIMENSIONES;
I'm getting erros at the line
MERGE INTO DIM_CUENTAS DIMC
Saying "Statement ignored"
and then, another error at:
INSERT (DIMC.REGION, DIMC.PAIS, DIMC.EDIFICIO, DIMC.CUENTA, DIMC.TIPO_CONTACTO, DIMC.CUENTA_FECHA_CREACION, DIMC.FNT_CUENTA_ID)
VALUES (Q.REGION, Q.PAIS, Q.EDIFICIO, Q.CUE_CUENTA, Q.TIC_TIPO_CONTACTO, TO_TIMESTAMP(sysdate, 'MM/DD/YYYY HH24:MI:SS'), Q.FNT_CUENTA_ID)
saying "missing keyword". Is it possible to use the merge statement in a SP? I'm new to Oracle so I really don't know if what I'm trying to do is possible or if there's something wrong with my code.
Thanks for any help, I would really appreaciate it.
I think that you swapped commands - after when matched you should put update statement and after not matched - insert.
Similar example worked for me, but after swapping statements I got ORA-00905 missing keyword. So correct version is:
merge into t1
using (select * from t2) t2 on (t1.id = t2.id)
when matched then update set t1.name = t2.name
when not matched then insert (id, name) values (t2.id, t2.name)

Copy data from a column in some rows to another column in other rows in oracle

quite the same question as here : Copy data from one existing row to another existing row in SQL?
but in Oracle, where update ... from and update t1, t2 are not supported.
I'll repeat it here in my own words ;
I have a table T, which looks like this :
and as the arrow shows it, I want to copy everything from r where c = 1 to e where c = 2, with t matching.
I have the select statement to get what I want to copy :
select
told.t,
told.r
from
T told
inner join
T tnew
on
told.t= tnew.t
where
told.c = 1
and
tnew.c = 2
I just don't know how to put this together in an update. An Oracle update, specifically.
try this:
update T tnew
set tnew.e = (select told.r from T told where told.c = 2 and told.t = tnew.t)
where tnew.c = 1
Sounds like the time for a bulk collects! Not as pretty as AB Cade's solution but more efficient.
declare
c_data is
select t1.rowid as rid, t2.r
from my_table t1
join my_table t2
on t1.t = t2.t
where t1.c = 2
and t2.c = 1
;
type t__data is table of c_data index by binary_integer;
t_data t__data;
begin
open c_data;
loop
fetch c_data bulk collect into t_data limit 25000;
exit when t_data.count = 0;
forall i in t_data.first .. t_data.last loop
update my_table
set e = t_data(i).r
where rowid = t_data(i).rid
;
commit;
end loop;
close c_data;
end;
/

Resources