Курсор Oracle проходит через последний элемент дважды - PullRequest
9 голосов
/ 22 мая 2009

У меня есть курсорный цикл, который строит строку путем объединения содержимого таблицы вместе, используя код в следующих строках:

OPEN cur_t;
LOOP
    FETCH cur_t INTO v_texttoadd;

    v_string := v_string || v_texttoadd;
EXIT WHEN cur_t%notfound;
END LOOP;

Проблема, конечно, в том, что последний элемент добавляется дважды, потому что система проходит через него еще раз, прежде чем понять, что больше ничего не найти.

Я пытался поиграть с чем-то вроде

OPEN cur_t;
WHILE cur_t%found;
LOOP
    FETCH cur_t INTO v_texttoadd;

    v_string := v_string || v_texttoadd;
END LOOP;

Но это, похоже, ничего не возвращало.

Какой синтаксис я должен использовать, чтобы каждая строка появлялась в результирующей строке только один раз?

Ответы [ 5 ]

24 голосов
/ 22 мая 2009

Вы можете попробовать это:

OPEN cur_t;
LOOP
  FETCH cur_t INTO v_texttoadd;
  EXIT WHEN cur_t%notfound;
  v_string := v_string || v_texttoadd;
END LOOP;

Это работает, потому что% notfound устанавливается при выполнении FETCH и больше нет строк для извлечения. В вашем примере вы проверили% notfound после объединения, и в результате у вас был дубликат в конце.

3 голосов
/ 22 мая 2009

Правильные ответы уже даны, но немного уточняю.

Имитация вашей текущей ситуации:

SQL> declare
  2    cursor cur_t
  3    is
  4    select ename
  5      from emp
  6     where deptno = 10
  7    ;
  8    v_texttoadd emp.ename%type;
  9    v_string    varchar2(100);
 10  begin
 11    open cur_t;
 12    loop
 13      fetch cur_t into v_texttoadd;
 14      v_string := v_string || v_texttoadd;
 15      exit when cur_t%notfound;
 16    end loop
 17    ;
 18    dbms_output.put_line(v_string);
 19  end;
 20  /
CLARKKINGMILLERMILLER

PL/SQL-procedure is geslaagd.

Здесь MILLER напечатан дважды. Просто переключив оператор EXIT и присвоение v_string, вы получите желаемый результат:

SQL> declare
  2    cursor cur_t
  3    is
  4    select ename
  5      from emp
  6     where deptno = 10
  7    ;
  8    v_texttoadd emp.ename%type;
  9    v_string    varchar2(100);
 10  begin
 11    open cur_t;
 12    loop
 13      fetch cur_t into v_texttoadd;
 14      exit when cur_t%notfound;
 15      v_string := v_string || v_texttoadd;
 16    end loop
 17    ;
 18    dbms_output.put_line(v_string);
 19  end;
 20  /
CLARKKINGMILLER

PL/SQL-procedure is geslaagd.

Однако ваш код PL / SQL становится проще при использовании курсора для цикла. Затем вы можете пропустить переменную v_texttoadd, и количество строк в цикле уменьшится:

SQL> declare
  2    cursor cur_t
  3    is
  4    select ename
  5      from emp
  6     where deptno = 10
  7    ;
  8    v_string    varchar2(100);
  9  begin
 10    for r in cur_t
 11    loop
 12      v_string := v_string || r.ename;
 13    end loop
 14    ;
 15    dbms_output.put_line(v_string);
 16  end;
 17  /
CLARKKINGMILLER

PL/SQL-procedure is geslaagd.

Вы также можете использовать прямой SQL для выполнения этой работы. Пример с предложением модели SQL, если вы используете версию 10g или выше:

SQL> select string
  2    from ( select string
  3                , rn
  4             from emp
  5            where deptno = 10
  6            model
  7                  dimension by (rownum rn)
  8                  measures (ename, cast(null as varchar2(100)) string)
  9                  ( string[any] order by rn desc = ename[cv()] || string[cv()+1]
 10                  )
 11         )
 12   where rn = 1
 13  /

STRING
-----------------------------------------------------------------------------------
CLARKKINGMILLER

1 rij is geselecteerd.

С уважением, Роб.

2 голосов
/ 22 мая 2009

% notfound устанавливается, когда при извлечении не удается получить новую строку.

другой возможный способ (этот способ избегает "if" s и "exit когда" s):

OPEN cur_t;
FETCH cur_t INTO v_texttoadd;
WHILE cur_t%found LOOP
    v_string := v_string || v_texttoadd;
    FETCH cur_t INTO v_texttoadd;
END LOOP;
0 голосов
/ 26 июня 2016

Я сделал много основанных на курсоре решений в Microsoft SQL, которые всегда работали идеально (старые добрые времена! Я так хочу вернуть свой SQL Server!), Однако все ответы на этот вопрос оказались неверными для меня, самый последний Строка курсора ВСЕГДА выполняется дважды, независимо от того, насколько тщательно я следовал предложенному ими методу.

Забудьте "non-while" loop и забудьте exit, это то, что вы ДОЛЖНЫ сделать, чтобы избежать двойного выполнения (почти то же, что и в T-SQL!).

  cursor c_mm is select a, b, c, d from mytable;

  begin
    open c_mm;
    fetch c_mm into a, b, c, d;
    while (not c_mm%notfound) loop 
        -- do what you have to do here

        fetch c_mm into a, b, c, d;
    end loop;
    close c_mm;
  end;

Что я действительно не понимаю, так это то, почему все статьи базы знаний Oracle и сообщения на форумах (и stackoverflow) продвигают это решение «выход в цикл», которое, очевидно, неверно!

0 голосов
/ 22 мая 2009

Простой ответ, хотя, возможно, и не самый лучший:

OPEN cur_t;
LOOP
    FETCH cur_t INTO v_texttoadd;
    IF cur_t%found THEN
        v_string := v_string || v_texttoadd;
    END IF;
EXIT WHEN cur_t%notfound;
END LOOP;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...