Oracle Ref Cursor против выбора с обработкой исключений
Вопрос
У меня есть несколько сценариев:
Необходимо прочитать значение столбца из трех разных таблиц в заранее определенном порядке, и только одна таблица будет содержать данные.
Считайте данные из таблицы 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;