Reputation: 4518
I have a store procedure that will
Here is my store procedure code
PROCEDURE process_data_out (
o_rt_cursor OUT SYS_REFCURSOR
) IS
v_limit NUMBER;
l_data_ids VARCHAR2(32000);
BEGIN
v_limit := 500; -- limit 500
l_data_ids := '';
-- Create loop to get data
FOR i IN (
SELECT *
FROM
(
SELECT id FROM
TBL_DATA a
WHERE
a.created_at BETWEEN SYSDATE - 0.5 AND SYSDATE + 0.1
AND a.status = 0
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_1)
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_2 WHERE IS_DENY = 1)
ORDER BY
priority
)
WHERE
ROWNUM <= v_limit
) LOOP
BEGIN
-- Build string of ids like id1,id2,id3,
l_data_ids := l_data_ids
|| i.id
|| ',';
-- update row status to prevent future repeat
UPDATE TBL_DATA
SET
status = 1
WHERE
id = i.id;
END;
END LOOP;
COMMIT;
-- If string of ids length >0 open cursor to take data
IF ( length(l_data_ids) > 0 )
THEN
-- Cut last comma id1,id2,id3, --> id1,id2,id3
l_data_ids := substr(l_data_ids,1,length(l_data_ids) - 1);
-- open cursor
OPEN o_rt_cursor FOR
SELECT
id,
phone
FROM
TBL_DATA a
WHERE
a.id IN (
SELECT
to_number(column_value)
FROM
XMLTABLE ( l_data_ids )
);
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END process_data_out;
I want to optimize this performance and here is my question
in
by exists
Replace
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_1)
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_2 WHERE IS_DENY = 1)
by
AND NOT Exists (SELECT phone FROM TBL_BIG_TABLE_1 where TBL_BIG_TABLE_1.phone = a.phone)
AND NOT Exists (SELECT phone FROM TBL_BIG_TABLE_2 WHERE TBL_BIG_TABLE_2.phone = a.phone and IS_DENY = 1)
Is there a better way than
Save a string of ids like id1,id2,id3
after update row status
Open cursor by select from string of ids
I appreciate for any suggestion.
Thank for your concern
Upvotes: 0
Views: 98
Reputation: 35910
Row-by-row processing is always slower and you are also creating the string of ids, which again takes time so overall performance is going down.
You can use the collection DBMS_SQL.NUMBER_TABLE
to store the updated ids from the UPDATE
statement using the RETURNING
clause and use it in the cursor query.
Also, I have changed your update
statement so that it does not use NOT IN
and uses the LEFT JOINS
and ROW_NUMBER
analytical function for increasing the performance as follows:
CREATE OR REPLACE PROCEDURE PROCESS_DATA_OUT (
O_RT_CURSOR OUT SYS_REFCURSOR
) IS
V_LIMIT NUMBER;
L_DATA_IDS DBMS_SQL.NUMBER_TABLE;
BEGIN
V_LIMIT := 500; -- limit 500
UPDATE TBL_DATA A
SET A.STATUS = 1
WHERE A.ID IN (
SELECT ID
FROM ( SELECT ID,
ROW_NUMBER() OVER(ORDER BY PRIORITY) AS RN
FROM TBL_DATA B
LEFT JOIN TBL_BIG_TABLE_1 T1 ON T1.PHONE = B.PHONE
LEFT JOIN TBL_BIG_TABLE_2 T2 ON T2.IS_DENY = 1 AND T2.PHONE = B.PHONE
WHERE B.CREATED_AT BETWEEN SYSDATE - 0.5 AND SYSDATE + 0.1
AND B.STATUS = 0
AND T1.PHONE IS NULL
AND T2.PHONE IS NULL)
WHERE RN <= V_LIMIT ) RETURNING ID BULK COLLECT INTO L_DATA_IDS;
OPEN O_RT_CURSOR FOR SELECT ID, PHONE
FROM TBL_DATA A
WHERE A.ID IN (SELECT COLUMN_VALUE FROM TABLE ( L_DATA_IDS ));
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END PROCESS_DATA_OUT;
/
Upvotes: 2