Существует ряд причин, по которым разработчики могут выполнить выбор COUNT (*) из таблицы в программе на PL / SQL:
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;
б) метод 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;
в) ВЫБЕРИТЕ СЧЕТЧИК (*) методом ROWNUM
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) Им нужно знать, существует ли более 1 строки.
Вариации на приемы для (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) ВЫБРАТЬ В метод
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
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;
Какой метод вы используете, во многом зависит от предпочтений (и некоторого религиозного фанатизма!) Стивен Фюрштайн всегда предпочитал явные курсоры, а не неявные (циклы SELECT INTO и cursor FOR); Том Кайт предпочитает неявные курсоры (и я с ним согласен).
Важным моментом является то, что выбор COUNT (*) без ограничения ROWCOUNT является дорогостоящим и поэтому должен выполняться только тогда, когда счет действительно необходим.
Что касается вашего дополнительного вопроса о том, как переписать это с явным курсором:
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 достаточно умен, чтобы знать, что его нужно получить только один раз.