開発者が PL/SQL プログラムでテーブルから COUNT(*) を選択する理由はいくつかあります。
1) 彼らは、テーブルに何行あるかを本当に知る必要があります。
この場合、選択の余地はありません。COUNT(*) を選択し、結果を待ちます。これは多くのテーブルではかなり高速ですが、大きなテーブルでは時間がかかる場合があります。
2) 行が存在するかどうかを知る必要があるだけです。
これは、テーブル内のすべての行を数えることを保証するものではありません。いくつかのテクニックが可能です:
a) 明示カーソル方式:
DECLARE
CURSOR c IS SELECT '1' dummy FROM mytable WHERE ...;
v VARCHAR2(1);
BEGIN
OPEN c;
FETCH c INTO v;
IF c%FOUND THEN
-- A row exists
...
ELSE
-- No row exists
...
END IF;
END;
b) SELECT INTO メソッド
DECLARE
v VARCHAR2(1);
BEGIN
SELECT '1' INTO v FROM mytable
WHERE ...
AND ROWNUM=1; -- Stop fetching if 1 found
-- At least one row exists
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- No row exists
END;
c) ROWNUM メソッドを使用した SELECT COUNT(*)
DECLARE
cnt INTEGER;
BEGIN
SELECT COUNT(*) INTO cnt FROM mytable
WHERE ...
AND ROWNUM=1; -- Stop counting if 1 found
IF cnt = 0 THEN
-- No row found
ELSE
-- Row found
END IF;
END;
3) 複数の行が存在するかどうかを知る必要があります。
(2)作業のテクニックのバリエーション:
a) 明示カーソル方式:
DECLARE
CURSOR c IS SELECT '1' dummy FROM mytable WHERE ...;
v VARCHAR2(1);
BEGIN
OPEN c;
FETCH c INTO v;
FETCH c INTO v;
IF c%FOUND THEN
-- 2 or more rows exists
...
ELSE
-- 1 or 0 rows exist
...
END IF;
END;
b) SELECT INTO メソッド
DECLARE
v VARCHAR2(1);
BEGIN
SELECT '1' INTO v FROM mytable
WHERE ... ;
-- Exactly 1 row exists
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- No row exists
WHEN TOO_MANY_ROWS THEN
-- More than 1 row exists
END;
c) ROWNUM メソッドを使用した SELECT COUNT(*)
DECLARE
cnt INTEGER;
BEGIN
SELECT COUNT(*) INTO cnt FROM mytable
WHERE ...
AND ROWNUM <= 2; -- Stop counting if 2 found
IF cnt = 0 THEN
-- No row found
IF cnt = 1 THEN
-- 1 row found
ELSE
-- More than 1 row found
END IF;
END;
どちらの方法を使用するかは、主に好みの問題です (そして宗教的な熱意もあります!)、Steven Feuerstein は常に暗黙的カーソル (SELECT INTO およびカーソル FOR ループ) よりも明示的カーソルを好んでいました。Tom Kyte は暗黙のカーソルを支持しています (私も彼に同意します)。
重要な点は、ROWCOUNT を制限せずに COUNT(*) を選択するとコストがかかるため、本当にカウントが必要な場合にのみ実行する必要があるということです。
明示的なカーソルを使用してこれを書き直す方法に関する補足の質問については、次のとおりです。
CREATE OR REPLACE PROCEDURE do_sth ( emp_id_in IN emp.emp_id%TYPE )
IS
v_rows INTEGER;
BEGIN
...
SELECT COUNT(*) INTO v_rows
FROM emp
WHERE emp_id = emp_id_in;
IF v_rows > 0 THEN
/* do sth */
END;
/* more statements */
...
END do_sth;
それは次のようになります。
CREATE OR REPLACE PROCEDURE do_sth ( emp_id_in IN emp.emp_id%TYPE )
IS
CURSOR c IS SELECT 1
FROM emp
WHERE emp_id = emp_id_in;
v_dummy INTEGER;
BEGIN
...
OPEN c;
FETCH c INTO v_dummy;
IF c%FOUND > 0 THEN
/* do sth */
END;
CLOSE c;
/* more statements */
...
END do_sth;
しかし、実際には、あなたの例では、主キーを選択していて、Oracleは一度だけフェッチする必要があることを十分に認識しているため、良くも悪くもありません。