no_data_found仅在select into中触发,update/delete不影响;需用嵌套块捕获以避免流程中断,且dbms_output不可靠,应按业务含义处理异常。
no_data_found只能在select into中触发,其他场景捕获它等于写了个摆设。
为什么UPDATE或DELETE后写WHEN NO_DATA_FOUND THEN没用
因为NO_DATA_FOUND不是通用“没查到”的钩子——它只绑定SELECT INTO的单行语义。执行UPDATE emp SET sal = 1000 WHERE empno = 9999时,哪怕影响0行,SQL%ROWCOUNT是0,但流程照常往下走,异常块根本不会进入。TOO_MANY_ROWS同理,只对SELECT INTO生效。常见错误是把DML和SELECT INTO混在一个BEGIN-END里,然后在末尾统一捕获,结果NO_DATA_FOUND永远不触发。
想捕获后继续执行,必须用嵌套BEGIN-END
PL/SQL中一旦异常抛出,当前BEGIN-END块立即终止。所以如果SELECT INTO失败,后续逻辑(比如日志、赋默认值、调用其他过程)全会跳过。解决办法是把它单独包一层:
DECLARE
v_name VARCHAR2(50);
BEGIN
BEGIN
SELECT ename INTO v_name FROM emp WHERE empno = 9999;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_name := 'UNKNOWN';
END; -- 这层结束,外层不受影响
<p>DBMS_OUTPUT.PUT_LINE('员工名:' || v_name); -- 这行能执行到
-- 后续其他操作也照常运行
END;</p>
不加这层嵌套,DBMS_OUTPUT.PUT_LINE就永远执行不到。
别只用DBMS_OUTPUT.PUT_LINE当处理逻辑
上线环境DBMS_OUTPUT默认关闭,日志不落盘、不报警、不重试,等于没处理。实际要根据业务含义决定动作:
- 是正常边界?比如查用户偏好没设 → 直接赋
v_pref := 'DEFAULT' - 需降级?比如主表查不到 → 在
WHEN NO_DATA_FOUND THEN里再SELECT INTO查快照视图 - 属数据不一致?比如订单ID反查客户不存在 → 用
RAISE_APPLICATION_ERROR(-20001, '客户主键丢失,订单ID: ' || v_order_id)中止并暴露问题
聚合函数如MAX()能绕过异常(空集返回NULL),但会强制全表扫描、掩盖TOO_MANY_ROWS,静默丢数据——这些副作用比异常本身更危险。











