Oracle Ref Cursor Vs Выберите в с обработкой исключений - PullRequest
3 голосов
/ 29 апреля 2009

У меня есть пара сценариев:

  1. Необходимо прочитать значение столбца из трех разных таблиц в предварительно определенном порядке, и только 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 Exception. Я не хочу использовать двухэтапный процесс запроса, который у нас есть в настоящее время.

Ответы [ 6 ]

5 голосов
/ 29 апреля 2009

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

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? , где я демонстрирую, что использование исключений работает лучше, чем подсчет, чтобы увидеть, есть ли какие-либо данные.

4 голосов
/ 29 апреля 2009
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 голос
/ 10 марта 2011

Усовершенствованная версия опции MIN "Dave Costa" ...

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

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

1 голос
/ 27 января 2010
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;
/
1 голос
/ 29 апреля 2009

То, как вы сейчас работаете со сценарием 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
0 голосов
/ 29 апреля 2009

Используйте форму цикла "for row in cursor", и цикл просто не будет обрабатываться, если нет данных:

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;
...