Oracle RefカーソルとSelect into with例外処理
質問
いくつかのシナリオがあります:
-
3つの異なるテーブルから列の値を事前に定義された順序で読み取る必要があり、1つのテーブルのみにデータが含まれます
-
指定された条件にレコードが存在する場合、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例外をキャッチするために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例外ブロックを持つものを見つけたいと思いました。現在ある2段階のクエリプロセスは使いたくありません。
解決
なぜ例外を避けたいと思っているのか分かりませんか?問題点:
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;
マルチユーザー環境。前者の場合、誰かが存在を確認するポイントとデータを読み取る時点の間でテーブルを更新できます。
パフォーマンスに関しては、どちらが優れているかわかりませんが、最初のオプションでは2つのコンテキストスイッチがsqlエンジンに切り替えられ、2番目のオプションでは1つのコンテキストスイッチのみが実行されます。
現在のシナリオ1の処理方法は良くありません。 1つで十分な場合に2つのクエリを実行するだけでなく、Erikが指摘したように、2つのクエリ間でデータが変更される可能性を開きます(読み取り専用またはシリアル化可能なトランザクションを使用しない限り)。
この場合、データは正確に3つのテーブルのうちの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...
しかし、3つの例外ハンドラーを持っているよりも良い理由は本当にありません。
2番目のシナリオでは、両方のテーブルにデータがあり、table1のデータが存在する場合はそれを使用し、それ以外の場合はテーブル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」のMINオプションの拡張バージョン...
SELECT COUNT(1), MIN(data) INTO v_rowcount, v_data FROM table2;
Now v_rowcount
で値0、> 1(1より大きい)を確認できます。通常の選択クエリでは NO_DATA_FOUND
または TOO_MANY_ROWS
がスローされます。例外。値" 1"正確に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;