Почему не срабатывает условие на `sql%notfound`?

Вот интересно, почему этот блок кода не работает. В таблице нет ни одной записи сid=1:

DECLARE
    VAR_NAME VARCHAR2(100);
    VAR_ID  NUMBER := 1;
BEGIN
    SELECT NAME INTO VAR_NAME
    FROM SUPPLIER
    WHERE ID = VAR_ID;
  
    IF SQL%NOTFOUND THEN
        DBMS_OUTPUT.PUT_LINE('DATA NOT FOUND');
    ELSIF SQL%FOUND THEN
        DBMS_OUTPUT.PUT_LINE('DATA FOUND');
    END IF;    
END;

Получаю ошибку:

ORA-01403: no data found

Но почему не срабатывает условие на sql%notfound?


Свободный перевод вопроса pl sql %NOTFOUND от участника @user1050619


Ответы (1 шт):

Автор решения: 0xdb

Хотя атрибуты неявного курсора SQL%FOUND, SQL%NOTFOUND или SQL%ROWCOUNT доступны после выполнения SELECT ... INTO, но проверять их значения не имеет никакого смысла.

Статическикий запрос SELECT ... INTO завершится без ошибок, только, если его результат - одна единственная запись. Значения атрибутов неявного курсора в этом случае будут одни и те же.
В противном случае, вызывается исключение:

  • NO_DATA_FOUND - если запрос не возвращает ни одной записи
  • TOO_MANY_ROWS - если запрос возвращает более одной записи

Поэтому, SELECT ... INTO следует заключить в отдельный блок с веткой обработки исключений:

<<my>> 
declare
    name varchar2 (100);
    id number := 1;
begin
    begin
        select name into name 
        from supplier
        where id = my.id;

        dbms_output.put_line('one row found');
    exception 
        when no_data_found then
            dbms_output.put_line ('data not found');
        when too_many_rows then
            dbms_output.put_line ('too many rows found');
    end;
    dbms_output.put_line ('sql%found/notfound/rowcount='||
        case sql%found when true then 'true' else 'false' end||'/'||
        case sql%notfound when true then 'true' else 'false' end||'/'||sql%rowcount); 
end;    
/
data not found  
sql%found/notfound/rowcount=false/true/0
→ Ссылка