2013-12-02 3 views
0

Это очень простой вопрос, но как лучше всего написать FORELSE в неявный курсор в Oracle PL/SQL? Некоторые языки программирования допускают такой синтаксис, выполняя блок FORELSE, когда курсор не возвращает никаких строк.Oracle неявный курсор: for-else

То, что я хочу достичь что-то по следующим направлениям (псевдо-код):

DECLARE 
    CURSOR test_cur IS 
     SELECT 'a' 
     FROM table_with_zero_or_more_data; 
BEGIN 
    FOR r_ IN test_cur LOOP 
     Dbms_Output.Put_Line ('One extra row found'); 
    FORELSE 
     Dbms_Output.Put_Line ('No data found'); 
    END LOOP; 
END; 

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

DECLARE 
    CURSOR test_cur IS 
     SELECT 'a' 
     FROM table_with_zero_or_more_data; 
BEGIN 
    BEGIN 
     FOR r_ IN test_cur LOOP 
      Dbms_Output.Put_Line ('One extra row found'); 
     END LOOP; 
    EXCEPTION 
     WHEN NO_DATA_FOUND THEN 
      Dbms_Output.Put_Line ('No data found'); 
    END; 
END; 

Следующие действия, конечно, (и есть аналогичные варианты, я не сомневаюсь). Но я просто нахожу этот способ немного неуклюжим. Когда код становится несколько более реальным и подробным, не так очевидно/интуитивно понятно, что оператор IF связан с циклом FOR. Это также заставляет ввести фиктивную переменную и просто не синтаксически красива.

DECLARE 
    CURSOR test_cur IS 
     SELECT 'a' 
     FROM table_with_zero_or_more_data; 
    i NUMBER := 0; 
BEGIN 
    FOR r_ IN test_cur LOOP 
     i := i + 1; 
     Dbms_Output.Put_Line ('One extra row found'); 
    END LOOP; 
    IF i = 0 THEN 
     Dbms_Output.Put_Line ('No data found'); 
    END IF; 
END; 

Мне просто интересно, если есть лучший способ, который является более интуитивным и интегрирует FOR и IF условия?

EDIT

В случае, если я не был достаточно ясен, точка это сделать это внутри неявного курсора. Я знаю, что могу использовать EXplicit cursors и проверить на %NOTFOUND и т. Д. (На самом деле %ROWCOUNT лучше).

+1

При использовании курсора для цикла, 'no_data_found' исключение никогда не будет поднят, если явный или неявный курсор * пусто * - это будет неявно открыто и безоговорочно закрыты, это все. То, что вы пытаетесь достичь, немного расплывчато, подумал. Вы хотите напечатать сообщение «нет данных найдено» только при открытии курсора для запроса, который не возвращает строк, и только в этой ситуации? «Дело в том, чтобы сделать это внутри курсора IMPLICIT». Вероятно, вы хотели сказать внутри явного курсора для цикла (вы явно указали свой курсор). Если это так, тогда вы не сможете этого достичь. –

ответ

3

AFAIK, потому что PL/SQL отсутствует forelse конструкцию и нет никакого способа, чтобы свернуть свои собственные конструкции, лучшее, что вы можете надеяться на то, используя дополнительную переменную состояния, чтобы удовлетворить случае неявный курсор for -loop обработанную нет строк.

Однако вы можете структурировать ваш код таким образом, что это более очевидным, что происходит:

declare 
    cursor test_c is 
    with data_ as (
     select 1 as id, 'foo' as str from dual union all 
     select 2 as id, 'bar' as str from dual 
    ) 
    select str from data_ where id > 2; 

    -- isolate the data processing into a dedicated subroutine/package 
    -- pass all required information as parameters 
    procedure process_data(p_data in test_c%rowtype) is 
    begin 
    dbms_output.put_line('processing: ' || p_data.str); 
    end; 
begin 
    -- more things can take place here ... 

    -- isolate the data processing into a dedicated block or subroutine 
    -- with a block level comment like: processing all foos and bars to 
    -- conform business rule car. (or even better: name the subroutine 
    -- accordingly !) 
    declare 
    v_has_data boolean := false; 
    begin 
    for d in test_c loop 
     v_has_data := true; 
     process_data(d); 
    end loop; 

    if not v_has_data then 
     dbms_output.put_line('no data processed'); 
    end if; 
    end; 

    -- more things can take place here ... 
end; 
/

Даже я в значительной степени договориться о Эээ ... clunkiness этого подхода помнить, что все языки программирования имеют компромиссные.

+0

Это не тот ответ, который я хотел. Я хотел бы услышать, что существует существующая конструкция PL/SQL, о которой я до сих пор не знал, и которая бы соответствовала моим потребностям. Но ваш самый честный ответ, и вы наверняка прочитали вопрос, прежде чем пытаться ответить на него! Для этого вы получите +1, и я отмечу это как принятый ответ, если я не получу ничего лучше в течение нескольких дней. – cartbeforehorse

+0

Для ясности я бы поднял исключение «NO_DATA_FOUND», чтобы обрабатывать его с другими исключениями. Я бы использовал это исключение для обработки моего пустого курсора, не беспокоясь, но в моем случае неявный курсор, содержащий агрегированные вызовы функций, никогда не повышает его. Благодаря! – rafaelbattesti

1

Попробуйте использовать простой цикл и тест с атрибутом курсора %NOTFOUND, как это:

DECLARE 
    CURSOR test_cur IS 
     SELECT 'a' 
     FROM table_with_zero_or_more_data; 
    r_test test_cur%ROWTYPE; 
BEGIN 
    OPEN test_cur; 

    FETCH test_cur INTO r_test; 

    IF test_cur%NOTFOUND THEN 
     dbms_output.put_line('No data found'); 
    ELSE 
     LOOP 
      FETCH test_cur INTO r_test; 
      EXIT WHEN test_cur%NOTFOUND; 
      dbms_output.put_line('One extra row found'); 
      -- 
      -- other routine code if there are rows found. 
      -- 
     END LOOP; 
    END IF; 
END; 
/

См Oracle Docs для получения более подробной информации о FETCH.

Также см. SQL-скрипт Николая Краснова в комментарии ниже. Это очень полезно.

+0

Собственно, это не работает. Вы ВСЕГДА будете нажимать условие «% NOTFOUND» в последней строке данных, независимо от того, есть ли в таблице данные. Другими словами, вы всегда будете печатать 'Нет данных '. – cartbeforehorse

+0

Я немного изменил свой ответ и проверил его. Он работает. '% NOTFOUND' не является условием, а атрибутом курсора, который задан как истинный, если вы пытаетесь получить выбор после того, как была выбрана последняя строка или вы выбрали из пустого курсора. Замените 'table_with_zero_or_more_data'' double' в вашем запросе и подтвердите это в конце. – Rachcha

+1

Это может быть, как я думаю, [упрощенным] (http://sqlfiddle.com/#!4/09752/5) немного. Количество операторов 'IF' может быть уменьшено до единицы. –

0

Аналогичным методом является использование атрибута% ROWCOUNT курсора. Для ссылки на другие атрибуты курсора, пожалуйста, используйте ссылку http://docs.oracle.com/cd/B12037_01/appdev.101/b10807/13_elems011.htm Реализация с использованием ROWCOUNT приведена ниже.

DECLARE 
CURSOR test_cur IS 
    SELECT 'a' 
    FROM table_with_zero_or_more_data; 
i NUMBER := 0; 
r_test test_cur%ROWTYPE; 
BEGIN 
    OPEN test_cur; 

LOOP 
    FETCH test_cur INTO r_test; 
    IF NOT test_cur%ROWCOUNT > 0 THEN 
     dbms_output.put_line("No data found"); 
     EXIT; 
    ELSE 
     dbms_output.put_line("extra row found. Row count is now "||test_cur%ROWCOUNT); 
    END IF; 
    ... 
    ... -- other routine code if there are rows found. 
    ... 
END LOOP; 
END; 
/
+0

Это работает (ish). Однако перед вашей бизнес-логикой вам нужно поставить условие «EXIT WHEN test_cur% NOTFOUND» (т. Е. Перед оператором «добавленная строка»). Вам также не нужен оператор 'ELSE'. Просто «END IF;» (после первого условия «EXIT;»), за которым следует условие «% NOTFOUND EXIT», за которым следует ваша бизнес-логика. Однако этот ответ терпит неудачу, используя явные курсоры. Сожалею! – cartbeforehorse

+1

PS: Вам не кажется, что 'cur% ROWCOUNT = 0' проще, чем' NOT cur% ROWCOUNT> 0'? Кажется, вы используете двойное отрицание программирования! – cartbeforehorse

+0

point duely отметил :-) –

0
DECLARE 
    CURSOR CEMP 
    IS 
    SELECT *FROM EMP ; 
    V_EMP CEMP%ROWTYPE; 
BEGIN 
    OPEN CEMP; 
    LOOP 
     FETCH CEMP INTO V_EMP; 
     IF CEMP%ROWCOUNT=0 or CEMP%ROWCOUNT IS NULL 
     THEN 
      DBMS_OUTPUT.PUT_LINE('No Data Found'); 
     END IF; 
     EXIT WHEN CEMP%NOTFOUND; 
     DBMS_OUTPUT.PUT_LINE(V_EMP.ENAME); 
    END LOOP; 
    CLOSE CEMP; 
END; 
/
+0

Это плохой ответ. Вы должны проверить наличие данных в каждом цикле вашего курсора, что является большой тратой ресурсов, если ваша таблица содержит большое количество строк. Я только хочу проверить один раз! – cartbeforehorse

Смежные вопросы