Oracle Ref Cursor vs는 예외 처리로 선택합니다
문제
몇 가지 시나리오가 있습니다.
사전 정의 된 순서로 세 개의 다른 테이블에서 열 값을 읽어야하며 1 개의 테이블 만 데이터가 있습니다.
주어진 기준에 대해 표 2의 데이터를 읽은 기준에 대한 레코드가있는 경우 표 1의 데이터 읽기
Oracle 저장 절차에서
이것들이 지금 처리되는 방식은 먼저 주어진 쿼리에 대한 수를 변수로 가져 오는 것입니다. count> 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 예외를 포착하려면 3 개의 예외 처리기 블록이 필요합니다.
현재 나는 다음과 같은 것을 가지고 있도록 커서로 이것을 다시 구현하고 있습니다.
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
퍼포먼스 관점에서 어떤 것이 더 나은지, 커서가있는 것 또는 변수로 선택하고 3 개의 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을 처리하는 방식은 이제 좋지 않습니다. 충분할 때 두 가지 쿼리를 할뿐만 아니라 Erik이 지적했듯이 두 쿼리간에 데이터 변경 가능성을 열어줍니다 (읽기 전용 또는 직렬화 가능한 트랜잭션을 사용하지 않는 한).
이 경우 데이터가 정확히 세 테이블 중 하나에있을 것이라고 말하면 어떻게됩니까?
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
"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"은 정확한 하나의 행이 존재하며 우리의 목적에 도움이 될 것임을 나타냅니다.
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;