Как получить в коллекцию, используя ref курсор, когда количество записей в таблице кратно указанному количеству ограничений? - PullRequest
0 голосов
/ 04 июля 2019

создать таблицу со столбцом идентификатора

CREATE TABLE TEST_TAB
  (ID NUMBER
  );

создать тип

CREATE type numbertabletype IS TABLE OF NUMBER;

вставить 100 записей

  INSERT INTO TEST_TAB
  SELECT LL FROM
    (SELECT LEVEL LL FROM DUAL CONNECT BY LEVEL<=100
    );

Создайте функцию, которая перебирает 100 записей за 10 циклов, курсор будет извлекаться в коллекцию числовых типов с LIMIT 10.

CREATE OR REPLACE FUNCTION LOOP_TEST
RETURN NUMBERTABLETYPE
IS
  lv_coll NUMBERTABLETYPE ;
  LV_COUNT NUMBER:=0;
  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;
    dbms_output.put_line(' BELOW NOT FOUND '||LV_COUNT);
    dbms_output.put_line('COLLECTION COUNT '||lv_coll.count);
  END LOOP;
  CLOSE c1;
  RETURN lv_coll;
END;
/

Запустите sql как скрипт, он возвращает ноль, когда rownum = 100

CURSOR c1
IS
  SELECT ID FROM TEST_TAB WHERE ROWNUM<=100;

Запустите sql как скрипт, он возвращает значения, когда rownum = 99, замените курсор c1 в функции на курсор ниже.

CURSOR c1
IS
  SELECT ID FROM TEST_TAB WHERE ROWNUM<=99;

так Как вы справляетесь со сценарием, когда лимит кратен общему количеству записей для цикла.

Ответы [ 2 ]

1 голос
/ 04 июля 2019

Код работает отлично;он просто не выполняет то, что вы ожидаете.

Когда код повторяется, он будет перезаписывать коллекцию 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 здесь

0 голосов
/ 04 июля 2019

Я попытался запустить ваше дело в анонимном блоке, но его не удалось вывести в случае ROWNUM <= 100. </p>

Я немного изменил ваш код, и это сработало.

-- 
DECLARE
  lv_coll NUMBERTABLETYPE ;
  lv_col2 NUMBERTABLETYPE ; -- added this line to hold the data
  LV_COUNT NUMBER:=0;
  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;
    EXIT WHEN lv_coll.COUNT = 0; -- added this line
    lv_col2 := lv_coll; -- added this line
    --dbms_output.put_line('AFTER FETCH CURSOR COUNT '||C1%ROWCOUNT);
   -- EXIT
  --WHEN c1%NOTFOUND;
   -- LV_COUNT:=LV_COUNT+1;
    --dbms_output.put_line(' BELOW NOT FOUND '||LV_COUNT);
    --dbms_output.put_line('COLLECTION COUNT '||lv_coll.count);
  END LOOP;
  CLOSE c1;
  --RETURN lv_coll;
  FOR I IN 1..lv_col2.COUNT LOOP -- added this line
  dbms_output.put_line(lv_col2(I)); -- added this line
  END LOOP;
END;
/

db <> fiddle demo

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...