Reputation: 768
My following code is not working as expected. I am getting the following error:
Error at line 1
ORA-06550: line 20, column 18:
PLS-00201: identifier 'I' must be declared
ORA-06550: line 20, column 9:
PL/SQL: Statement ignored
My code:
DECLARE
process_limit CONSTANT SIMPLE_INTEGER := 500;
CURSOR c1 IS
SELECT * FROM cdtx cx
WHERE EXISTS (SELECT 1 FROM clmx c
WHERE c.cid = cx.cid);
TYPE t_c1 IS TABLE OF c1%ROWTYPE;
v_c1 t_c1;
BEGIN
OPEN c1;
LOOP
FETCH c1 BULK COLLECT INTO v_c1 LIMIT process_limit;
EXIT WHEN v_c1.COUNT = 0;
BEGIN
FORALL i IN 1..v_c1.COUNT
INSERT INTO cdx
(cid,
oind,
tind,
ot_date)
VALUES (v_c1(i).cid,
v_c1(i).new_oind,
v_c1(i).new_tind,
v_c1(i).ot_date);
COMMIT;
IF v_c1(i).new_t_amt IS NOT NULL
AND v_c1(i).new_t_date IS NOT NULL
THEN
INSERT INTO ctx
(cid,
t_amt,
t_date,
t_id)
VALUES
(v_c1(i).cid,
v_c1(i).new_t_amt,
v_c1(i).new_t_date,
'1');
COMMIT;
END IF;
END;
END LOOP;
END;
The cursor returns close to 10k records. I initially had working code without oracle's BULK COLLECT
feature but I figured BULK COLLECT
would be faster. I have tried several variations for the IF
condition on line 20 without success. I need to evaluate columns new_t_amt
and new_t_date
and check if they are NOT NULL
. Only if they are NOT NULL
should the INSERT
happen. Thanks for your help.
Upvotes: 0
Views: 600
Reputation: 95101
PL/SQL is a programming language directly installed in your DBMS. However, The PL/SQL engine must still "talk" to the SQL engine in order to have the SQL statements executed. So it's always faster to run an SQL statement direcly rather than calling a PL/SQL function. For this reason Boneist's suggestion to use a mutli table insert, should be faster than your approach.
In some rare situations the classic approach with single inserts could prove even faster. That would particularily apply with covering indexes:
CREATE INDEX idx_for_cdx ON cdtx(cid, new_oind, new_tind, ot_date);
CREATE INDEX idx_for_ctx ON cdtx(cid, new_t_amt, new_t_date);
The column order in the second index may matter, so you could add
CREATE INDEX idx_for_ctx2 ON cdtx(new_t_amt, new_t_date, cid);
and see which index gets used.
INSERT INTO cdx (cid, oind, tind, ot_date)
SELECT cid, new_oind, new_tind, ot_date
FROM cdtx
WHERE cid IN (SELECT cid FROM clmx);
INSERT INTO ctx (cid, t_amt, t_date, t_id)
SELECT cid, new_t_amt, new_t_date, '1'
FROM cdtx
WHERE cid IN (SELECT cid FROM clmx)
AND new_t_amt IS NOT NULL
AND new_t_date IS NOT NULL;
COMMIT;
You could also put this in an anonymous PL/SQL block, if you preferred so:
BEGIN
INSERT INTO cdx ...
INSERT INTO ctx ...
COMMIT;
END;
The sole difference would be that you would have less round trips from your machine to the database machine, as these would be moved to inside the database machine between the PL/SQL engine and the SQL engine.
Upvotes: 1
Reputation: 23588
If you were using a cursor for loop, then it's possible you would see an improvement by moving to bulk collect, bearing in mind the caveats mentioned by Thorsten in the comments on your question.
However, you can easily do this in a single insert statement, assuming you don't run into any of the restrictions (and I'm hoping there isn't a foreign key from ctx.cid to cdx.cid), using a multitable insert like so:
INSERT ALL
WHEN new_t_amt IS NOT NULL
AND new_t_date IS NOT NULL THEN
INTO ctx (cid, t_amt, t_date, t_id) VALUES (cid, new_t_amt, new_t_date, '1')
WHEN 1 = 1 THEN INTO cdx (cid, oind, tind, ot_date) VALUES (cid, new_oind, new_tind, ot_date)
SELECT *
FROM cdtx cx
WHERE EXISTS (SELECT 1
FROM clmx c
WHERE c.cid = cx.cid);
Upvotes: 1