Код работает отлично;он просто не выполняет то, что вы ожидаете.
Когда код повторяется, он будет перезаписывать коллекцию lv_coll
каждый раз, так что она будет содержать не более 10 элементов.
Когда он выбираетНа 91-100-й строке он заполняет коллекцию 10 элементами и обрабатывает ее, но, поскольку он не пытался прочитать другую строку, он не знает, что в курсоре больше не осталось строк, и еще не достиг условия c1%NOTFOUND
длязавершить цикл.
Когда он повторяет цикл, после этого он обнаружит, что курсор уже исчерпан и будет читать ноль строк.Итак, в этом последнем цикле коллекция lv_coll
не является NULL
, но представляет собой коллекцию, содержащую ноль элементов, и именно это будет возвращено.
Сравните это с тем, когда в строке только 99 строккурсорКогда цикл пытается прочитать 91-ю и 100-ю строки, он прочитает 91-ю и 99-ю строки и попытается прочитать 100-ю, но обнаружит, что курсор исчерпан, и значение c1%NOTFOUND
будет истинным, если он выйдет из цикла и вызовет сбор данных.возвращается только с 9 элементами.
Если вы хотите вернуть все элементы из курсора, вам нужно будет использовать вторую коллекцию для их агрегирования, поскольку заполненная из курсора будет перезаписываться в каждом цикле:
CREATE OR REPLACE FUNCTION LOOP_TEST
RETURN NUMBERTABLETYPE
IS
lv_coll NUMBERTABLETYPE;
all_items NUMBERTABLETYPE := NUMBERTABLETYPE();
LV_COUNT NUMBER:=0;
ids VARCHAR2(30);
CURSOR c1
IS
SELECT ID FROM TEST_TAB WHERE ROWNUM<=100;
BEGIN
OPEN c1;
LOOP
dbms_output.put_line('BEFORE FETCH CURSOR COUNT '||C1%ROWCOUNT);
FETCH c1 bulk collect INTO lv_coll limit 10;
dbms_output.put_line('AFTER FETCH CURSOR COUNT '||C1%ROWCOUNT);
EXIT WHEN c1%NOTFOUND;
LV_COUNT:=LV_COUNT+1;
all_items := all_items MULTISET UNION ALL lv_coll;
dbms_output.put_line(' BELOW NOT FOUND '||LV_COUNT);
dbms_output.put_line('COLLECTION COUNT '||lv_coll.count);
-- SELECT LISTAGG(COLUMN_VALUE,',') WITHIN GROUP( ORDER BY ROWNUM )
-- INTO ids
-- FROM TABLE(lv_coll);
-- dbms_output.put_line('IDS: '||ids);
END LOOP;
CLOSE c1;
RETURN all_items;
END;
/
Который можно вызвать с помощью:
DECLARE
ids numbertabletype;
vals VARCHAR2(3000);
BEGIN
ids := LOOP_TEST();
DBMS_OUTPUT.PUT_LINE( 'NUMBER OF IDs: ' || CASE WHEN ids IS NULL THEN 'NULL' ELSE TO_CHAR( ids.COUNT ) END );
SELECT LISTAGG(COLUMN_VALUE,',') WITHIN GROUP( ORDER BY ROWNUM )
INTO vals
FROM TABLE(ids);
DBMS_OUTPUT.PUT_LINE( 'values: ' || vals );
END;
/
db <> fiddle здесь