Oracle Ref Cursor против выбора с обработкой исключений

StackOverflow https://stackoverflow.com/questions/801805

  •  03-07-2019
  •  | 
  •  

Вопрос

У меня есть несколько сценариев:

  1. Необходимо прочитать значение столбца из трех разных таблиц в заранее определенном порядке, и только одна таблица будет содержать данные.

  2. Считайте данные из таблицы 1, если записи присутствуют для заданных критериев, иначе прочтите данные из таблицы 2 для заданных критериев.

В хранимых процедурах Oracle

Способ, которым они обрабатываются прямо сейчас, заключается в том, чтобы сначала получить счетчик для данного запроса в переменную, и если счетчик > 0, мы выполняем тот же запрос для чтения фактических данных, как в:

select count(*) from table1 into v_count
if v_count > 0
then
    select data into v_data from table1
end if;

Return v_data

Это делается для того, чтобы избежать исключения no_data_found, иначе мне потребовалось бы три блока обработчика исключений, чтобы перехватить исключение no_data_found для каждого доступа к таблице.

В настоящее время я переопределяю это с помощью курсоров, чтобы у меня было что-то вроде этого:

cursor C1 is
    select data from table1;
Open C1
Fetch C1 into v_data
if C1%FOUND
then
    Close C1
    Return v_data
End If

Я хотел выяснить, какой из них лучше с точки зрения производительности — тот, что с курсорами, или тот, который выполняет выбор в переменную и имеет три блока исключений no_data_found.Я не хочу использовать двухэтапный процесс запроса, который у нас есть сейчас.

Это было полезно?

Решение

Я не знаю, почему вы так стремитесь избежать исключения?Что не так с:

begin
    begin
        select data into v_data from table1;
    exception
        when no_data_found then
        begin
            select data into v_data from table2;
        exception
            when no_data_found then
            begin
               select data into v_data from table3;
            exception
                when no_data_found then
                    v_data := null;
            end;
        end;
    end;
    return v_data;
end;

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

Видеть Насколько плохо игнорировать исключение Oracle DUP_VAL_ON_INDEX? где я демонстрирую, что использование исключений работает лучше, чем подсчет наличия каких-либо данных.

Другие советы

select count(*) from table1 into v_count
if v_count > 0 then
    select data into v_data from table1;
else
    v_data := null;
end if;
return v_data;

НЕ эквивалентно

begin
    select data into v_data from table1;
    return v_data;
exception
    when no_data_found then
        return null;
end;

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

С точки зрения производительности я понятия не имею, что лучше, но я знаю, что первый вариант выполняет два переключения контекста для движка sql, а второй — только одно переключение контекста.

То, как вы сейчас справляетесь со сценарием 1, не очень хорошо.Вы не только выполняете два запроса, когда одного будет достаточно, но, как отметил Эрик, это открывает возможность изменения данных между двумя запросами (если вы не используете транзакцию только для чтения или сериализуемую транзакцию).

Учитывая, что вы говорите, что в этом случае данные будут находиться ровно в одной из трех таблиц, как насчет этого?

SELECT data
  INTO v_data FROM
  (SELECT data FROM table1
   UNION ALL
   SELECT data FROM table2
   UNION ALL
   SELECT data FROM table3
  )

Еще один «трюк», который вы можете использовать, чтобы избежать написания нескольких обработчиков, для которых данные не найдены:

SELECT MIN(data) INTO v_data FROM table1;
IF v_data IS NOT NULL THEN
   return v_data;
END IF;

SELECT MIN(data) INTO v_data FROM table2;
...etc...

но я действительно не вижу причин, которые были бы лучше, чем иметь три обработчика исключений.

Я думаю, что для вашего второго сценария вы имеете в виду, что в обеих таблицах могут быть данные, и вы хотите использовать данные из таблицы 1, если они есть, в противном случае используйте данные из таблицы 2.Опять же, вы можете сделать это с помощью одного запроса:

SELECT data
  INTO v_data FROM
  (SELECT data FROM
    (SELECT 1 sort_key, data FROM table1
     UNION ALL
     SELECT 2 sort_key, data FROM table2
    )
   ORDER BY sort_key ASC
  )
  WHERE ROWNUM = 1

Усовершенствованная версия опции MIN «Дэйва Косты»…

SELECT COUNT(1), MIN(data) INTO v_rowcount, v_data FROM table2;

Сейчас v_rowcount можно проверить на значения 0,> 1 (больше 1), где обычный запрос выбора выдаст NO_DATA_FOUND или TOO_MANY_ROWS исключение.Значение «1» будет указывать, что существует ровно одна строка и будет служить нашей цели.

DECLARE
    A VARCHAR(35);
    B VARCHAR(35);
BEGIN
    WITH t AS
    (SELECT OM_MARCA, MAGAZIA FROM ifsapp.AKER_EFECTE_STOC WHERE (BARCODE = 1000000491009))
    SELECT
    (SELECT OM_MARCA FROM t) OM_MARCA,
    (SELECT MAGAZIA FROM t) MAGAZIA
    INTO A, B
    FROM DUAL;
    IF A IS NULL THEN
       dbms_output.put_line('A este null');
    END IF;
    dbms_output.put_line(A);
    dbms_output.put_line(B);
END;
/

Используйте форму цикла «для строки в курсоре», и цикл просто не будет обрабатываться, если нет данных:

declare cursor
t1Cur is
 select ... from table1;
t2Cur is
 select ... from table2;
t3Cur is
 select ... from table3;
t1Flag boolean FALSE;
t2Flag boolean FALSE;
t3Flag boolean FALSE;
begin
for t1Row in t1Cur loop
  ... processing, set t1Flag = TRUE
end loop;
for t2Row in t2Cur loop
  ... processing, set t2Flag = TRUE
end loop;
for t3Row in t3Cur loop
  ... processing, set t3Flag = TRUE
end loop;
... conditional processing based on flags
end;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top