2009-04-29 3 views
3

У меня есть несколько сценариев:Oracle Ref Cursor Vs Выберите в с обработкой исключений

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

  2. Считывает данные из table1, если записи присутствуют критериев данных еще читать данные из Table2 по заданным критериям

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

ответ

5

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

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; 

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

См. How bad is ignoring Oracle DUP_VAL_ON_INDEX exception?, где я демонстрирую, что использование исключений выполняется лучше, чем подсчет, чтобы увидеть, есть ли какие-либо данные.

+0

Я думал, что Исключения имеют накладные расходы –

+0

Нет - см. http : //stackoverflow.com/questions/350860/how-bad-is-ignoring-oracle-dupvalonindex-exception, где я демонстрирую, что использование исключений работает лучше, чем подсчет, чтобы увидеть, есть ли какие-либо данные. –

+0

Конечно, когда я говорю «нет», я имею в виду отсутствие значительных * накладных расходов, что означает, что их нужно избегать! –

4
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-движка, а второй имеет только один контекстный переключатель.

+0

Поскольку я собираюсь использовать это в среде отчетности, доступ к нескольким пользователям не имеет большого значения. Если бы это был только один доступ к таблице, то механизм исключений был бы в порядке, но просто думал об использовании исключений, когда у меня было близко к 3 таблицам для доступа аналогичным образом. –

+0

Я бы сделал это в любом случае. Не стоит много исходных строк, и ваши намерения ясны. – erikkallen

0

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

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; 
+0

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

+0

Мой код не иллюстрировал того, что я имел в виду - связался с переформатированием и потерял основную идею. Если только одна из таблиц содержит данные, а порядок предопределен, тогда вы просто добавляете проверку для предшествующего логического флага перед выполнением следующего цикла курсора. Вы будете выполнять только столько выборок, сколько необходимо, и не должны обрабатывать никакие execeptions данных. – dpbradley

1

вы обработка сценария 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

Я действительно думал о Union All, но остался в стороне от него, потому что в настоящее время это будет намного хуже, чем First sceanrio, о котором я описал, поскольку одна из таблиц доступна через ссылку db, для обоих случаев я хотел избежать дополнительных ударов к другим таблицам, когда данных не существует, с объединением всех, моя производительность ухудшится –

+0

Опция MIN выглядит резонирующей, хотя я не уверен, что я ее получу, –

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; 
/
1

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

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

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