私は昨日いくつかのタスクを書いていましたが、pl/sqlを使用しているときにテーブルに行が存在するかどうかを確認することについて適切で受け入れられた方法がわからないことに気付きました。
例のためにテーブルを使用しましょう
PERSON(ID, Name);
明らかに私はできない(秘密の方法がない限り)のようなもの:
BEGIN
IF EXISTS SELECT id FROM person WHERE ID = 10;
-- do things when exists
ELSE
-- do things when doesn't exist
END IF;
END;
したがって、私の標準的な解決方法は次のとおりです。
DECLARE
tmp NUMBER;
BEGIN
SELECT id INTO tmp FROM person WHERE id = 10;
--do things when record exists
EXCEPTION
WHEN no_data_found THEN
--do things when record doesn't exist
END;
しかし、それを行う方法が受け入れられているかどうか、またはチェックするより良い方法があるかどうかはわかりませんが、誰かが私と彼らの知恵を共有できるかどうか本当に感謝します:)
乾杯。
通常のコードを例外ブロックにプッシュしません。条件を満たす行が存在するかどうかを確認し、そこから続行します。
declare
any_rows_found number;
begin
select count(*)
into any_rows_found
from my_table
where rownum = 1 and
... other conditions ...
if any_rows_found = 1 then
...
else
...
end if;
テーブルに行が存在するかどうかを確認するために使用されるスタンドアロンSELECTを備えたIMOコードは、データベースを適切に活用していません。あなたの例では、ハードコードされたID値を持っていますが、それはアプリが「現実の世界」で動作する方法ではありません(少なくともmy worldではそうではありません-あなたは異なるかもしれません:-)。典型的なアプリでは、カーソルを使用してデータを検索します。つまり、請求書データを参照し、顧客が存在するかどうかを知る必要があるアプリがあるとします。アプリの本体は次のようになります
FOR aRow IN (SELECT * FROM INVOICES WHERE DUE_DATE < TRUNC(SYSDATE)-60)
LOOP
-- do something here
END LOOP;
-- do something here
で、顧客が存在するかどうかを確認し、そうでない場合はエラーメッセージを出力します。
これを行う1つの方法は、次のように、ある種のシングルトンSELECTを配置することです。
-- Check to see if the customer exists in PERSON
BEGIN
SELECT 'TRUE'
INTO strCustomer_exists
FROM PERSON
WHERE PERSON_ID = aRow.CUSTOMER_ID;
EXCEPTION
WHEN NO_DATA_FOUND THEN
strCustomer_exists := 'FALSE';
END;
IF strCustomer_exists = 'FALSE' THEN
DBMS_OUTPUT.PUT_LINE('Customer does not exist!');
END IF;
しかし、IMOは比較的遅く、エラーが発生しやすいです。これを行うIMO a Better Way(tm)は、メインカーソルに組み込むことです。
FOR aRow IN (SELECT i.*, p.ID AS PERSON_ID
FROM INVOICES i
LEFT OUTER JOIN PERSON p
ON (p.ID = i.CUSTOMER_PERSON_ID)
WHERE DUE_DATA < TRUNC(SYSDATE)-60)
LOOP
-- Check to see if the customer exists in PERSON
IF aRow.PERSON_ID IS NULL THEN
DBMS_OUTPUT.PUT_LINE('Customer does not exist!');
END IF;
END LOOP;
このコードは、PERSON.IDがPERSONのPRIMARY KEYとして宣言されている(または少なくともNOT NULLである)ことを前提としています。ロジックは、PERSONテーブルがクエリに外部結合され、PERSON_IDがNULLになった場合、PERSON.IDには値が必要であるため(つまり、少なくともNOTヌル)。
共有してお楽しみください。
この猫の皮をむく多くの方法。各テーブルのパッケージに簡単な関数を配置します...
function exists( id_in in yourTable.id%type ) return boolean is
res boolean := false;
begin
for c1 in ( select 1 from yourTable where id = id_in and rownum = 1 ) loop
res := true;
exit; -- only care about one record, so exit.
end loop;
return( res );
end exists;
小切手を本当にきれいにします...
IF pkg.exists(someId) THEN
...
ELSE
...
END IF;
select nvl(max(1), 0) from mytable;
このステートメントは、行がない場合は0、そのテーブルに少なくとも1つの行がある場合は1になります。 select count(*)を実行するよりもはるかに高速です。オプティマイザーは、質問に答えるために1行のみをフェッチする必要があることを「認識」します。
以下に、(冗長な)小さな例を示します。
declare
YES constant signtype := 1;
NO constant signtype := 0;
v_table_has_rows signtype;
begin
select nvl(max(YES), NO)
into v_table_has_rows
from mytable -- where ...
;
if v_table_has_rows = YES then
DBMS_OUTPUT.PUT_LINE ('mytable has at least one row');
end if;
end;
select max( 1 )
into my_if_has_data
from MY_TABLE X
where X.my_field = my_condition
and rownum = 1;
すべてのレコードを反復処理していません。
MY_TABLEにデータがない場合、my_if_has_dataはnullに設定されます。
明示カーソルを使用している場合、次のようになります。
DECLARE
CURSOR get_id IS
SELECT id
FROM person
WHERE id = 10;
id_value_ person.id%ROWTYPE;
BEGIN
OPEN get_id;
FETCH get_id INTO id_value_;
IF (get_id%FOUND) THEN
DBMS_OUTPUT.PUT_LINE('Record Found.');
ELSE
DBMS_OUTPUT.PUT_LINE('Record Not Found.');
END IF;
CLOSE get_id;
EXCEPTION
WHEN no_data_found THEN
--do things when record doesn't exist
END;
Select 'YOU WILL SEE ME' as ANSWER from dual
where exists (select 1 from dual where 1 = 1);
Select 'YOU CAN NOT SEE ME' as ANSWER from dual
where exists (select 1 from dual where 1 = 0);
Select 'YOU WILL SEE ME, TOO' as ANSWER from dual
where not exists (select 1 from dual where 1 = 0);