我读过一本名为"Oracle PL SQL Programming"(第2版)的书,由Steven Feuerstein和Bill Pribyl撰写.在页99,有一点建议
除非你真的需要知道"命中"的总数,否则不要从表中"选择COUNT(*)".如果您只需要知道是否有多个匹配,只需使用显式游标获取两次.
你能举个例子向我解释这一点吗?谢谢.
正如Steven Feuerstein和Bill Pribyl建议我们不要使用SELECT COUNT()来检查表中的记录是否存在,任何人都可以帮我编辑下面的代码,以避免使用显式游标而不是使用SELECT COUNT(*)吗?此代码是在Oracle存储过程中编写的.
我有一个表emp(emp_id,emp_name,...),所以检查提供的员工ID是否正确:
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;
Tony Andrews.. 22
开发人员可以从PL/SQL程序中的表执行选择COUNT(*)的原因有很多:
1)他们真的需要知道表中有多少行.在这种情况下,没有选择:选择COUNT(*)并等待结果.这在许多桌子上会非常快,但在大桌子上可能需要一段时间.
2)他们只需要知道是否存在行.这不保证计算表中的所有行.许多技术都是可能的:
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;
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;
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)工作技术的变化:
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;
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;
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喜欢隐含的游标(我同意他的观点).
重要的是,选择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非常聪明,知道它只需要获取一次.
开发人员可以从PL/SQL程序中的表执行选择COUNT(*)的原因有很多:
1)他们真的需要知道表中有多少行.在这种情况下,没有选择:选择COUNT(*)并等待结果.这在许多桌子上会非常快,但在大桌子上可能需要一段时间.
2)他们只需要知道是否存在行.这不保证计算表中的所有行.许多技术都是可能的:
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;
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;
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)工作技术的变化:
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;
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;
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喜欢隐含的游标(我同意他的观点).
重要的是,选择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非常聪明,知道它只需要获取一次.
如果你有两个感兴趣,试试吧
SELECT 'THERE ARE AT LEAST TWO ROWS IN THE TABLE' FROM DUAL WHERE 2 = ( SELECT COUNT(*) FROM TABLE WHERE ROWNUM < 3 )
它比使用手动光标方法所需的代码更少,并且可能更快.
rownum技巧意味着一旦有两个行就停止获取行.
如果您没有对计数(*)进行某种限制,则可能需要很长时间才能完成,具体取决于您拥有的行数.在这种情况下,使用游标循环来手动从表中读取2行会更快.