oracle ora-01779 批量更新异常处理办法

本文介绍Oracle数据库中ORA-01779错误的原因及解决方法。此错误通常出现在尝试更新子查询时,由于数据源表中的数据不唯一导致。文章提供了两种解决方案:一是确保数据源表中的数据唯一;二是通过使用BYPASS_UJVC提示强制执行更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转贴:http://rainbowbridg.itpub.net/post/23663/295750

 

 

Oracle中试图对一个子查询进行更新时可能会出现ORA-01779错误。该错误的内容为:

ORA-01779: cannot modify a column which maps to a non-key-preserved table

例如,使用以下的更新查询就会出现该错误。

 

 

CREATE TABLE test1 ( id integer primary key, num integer ); 
INSERT INTO test1 VALUES (1,0);
INSERT INTO test1 VALUES (2,0);
INSERT INTO test1 VALUES (3,0);
INSERT INTO test1 VALUES (4,0); 

 

 

CREATE TABLE test2 ( id integer, num integer, upd integer );
INSERT INTO test2 VALUES (1,10, 0);
INSERT INTO test2 VALUES (2,20, 1);

 

 

UPDATE 
( 
  SELECT t1.id id1, t1.num num1, t2.id id2, t2.num num2 
  FROM test1 t1, test2 t2 
  WHERE t1.id=t2.id AND t2.upd=1
) 
SET num1=num2;
 

 ORA-01779: cannot modify a column which maps to a non-key-preserved table

 

这个错误的意思是,子查询的结果中,更新数据源(test2)的内容不唯一,导致被更新对象(test1)中的一行可能对应数据源(test2)中的多行。 本例中,test2表的id不唯一,因此test2表中可能存在id相同但是num不相同的数据,这种数据是无法用来更新 test1 的。

 

解决方法就是保证数据源的唯一性,例如本例中可以为test2.id创建一个唯一索引:

 

CREATE UNIQUE INDEX test2_idx_001 ON test2 (id);

之后上面的更新就可以执行了。

 

另外也可以强制 Oracle 执行,方法是加上 BYPASS_UJVC 注释。

 

UPDATE 
( 
SELECT /*+ BYPASS_UJVC */ t1.id id1, t1.num num1, t2.id id2, t2.num num2 
FROM test1 t1, test2 t2
WHERE t1.id=t2.id AND t2.upd=1 
) 
SET num1=num2;
 

BYPASS_UJVC的作用是跳过Oracle的键检查。

 

这样虽然能够执行了,但是如果test2中存在不唯一的数据,test1就会被更新多次而导致意想不到的结果。

 

<think>嗯,用户问的是ORA-01403错误,这个错误在Oracle数据库中挺常见的。首先,我需要确认这个错误的具体含义。根据记忆,ORA-01403应该是“未找到数据”(NO DATA FOUND)。不过,我得再核实一下,确保自己记的是正确的。 接下来,用户可能是在执行PL/SQL代码或者某个查询时遇到了这个错误。这时候,我需要考虑不同的场景。比如,在PL/SQL中,当SELECT INTO语句没有返回任何行时,就会触发这个异常。而在SQL查询中,比如使用游标或者隐式游标,如果没有数据,可能也会出现这种情况。但需要区分是PL/SQL块中的异常还是单纯的查询结果为空。 然后,用户可能想知道如何解决这个问题。解决方案可能包括异常处理,比如在PL/SQL中使用EXCEPTION块来捕获NO_DATA_FOUND异常,或者在使用SELECT INTO时确保至少有一行数据返回。另外,用户可能需要检查查询条件是否正确,是否有拼写错误,或者数据是否真的存在。 我还需要考虑到用户可能的背景。如果用户是开发人员,可能需要详细的代码示例和异常处理方法。如果是DBA,可能需要更多的关于如何避免此类错误的建议,比如数据验证和约束的设置。不过,根据之前用户询问的问题(清空所有表分区),用户可能有一定的数据库管理经验,但也不排除是开发人员。 另外,ORA-01403和ORA-01422(TOO MANY ROWS)经常被一起提到,因为两者都涉及SELECT INTO语句返回的行数问题。可能需要提醒用户同时处理这两种情况,尤其是在隐式游标中。 用户可能需要具体的例子来理解如何处理这个错误。比如,在PL/SQL块中,当使用SELECT INTO时,如果没有数据,就会抛出异常,这时候可以用EXCEPTION WHEN NO_DATA_FOUND THEN ...来处理。或者,在调用存储过程时,如何避免因为空结果而导致的程序中断。 还有,用户可能想知道这个错误发生的常见场景,比如数据迁移后某些记录缺失,或者应用逻辑错误导致查询条件不正确。这时候,除了处理异常,还需要进行数据检查和业务逻辑验证。 另外,需要注意权限问题。如果用户没有访问某些表的权限,可能导致查询结果为空,但这通常会导致不同的错误,比如ORA-00942,所以这可能不是ORA-01403的原因,但需要确认。 最后,可能需要总结一下解决步骤:首先确认错误发生的上下文(PL/SQL还是SQL),检查查询条件,添加异常处理,验证数据是否存在,以及使用替代方法如聚合函数或显式游标来处理可能为空的情况。</think>在Oracle数据库中,**ORA-01403** 是一个常见错误,其完整描述为:**ORA-01403: no data found**(未找到数据)。以下是关于该错误的详细解析和解决方案: --- ### **1. 错误本质** - **触发场景**:当程序尝试从数据库检索数据但未找到匹配记录时触发。 - **典型场景**: - 使用 `SELECT...INTO` 语句查询结果为空 - 隐式游标(如 `FOR...LOOP`)未返回数据 - 引用未初始化的嵌套表或数组元素 --- ### **2. 常见原因** #### **(1) PL/SQL中SELECT INTO未返回数据** ```sql DECLARE v_emp_name VARCHAR2(100); BEGIN SELECT ename INTO v_emp_name FROM emp WHERE empno = 9999; -- 假设此员工号不存在 END; ``` **结果**:直接报错 `ORA-01403` #### **(2) 隐式游标未处理空结果** ```sql BEGIN FOR r IN (SELECT * FROM dept WHERE loc = 'Mars') -- 假设无此地点 LOOP DBMS_OUTPUT.PUT_LINE(r.dname); END LOOP; END; ``` **结果**:循环体不执行,但不会报错(需注意逻辑漏洞) --- ### **3. 解决方案** #### **(1) PL/SQL异常处理(推荐)** ```sql DECLARE v_salary NUMBER; BEGIN SELECT sal INTO v_salary FROM emp WHERE empno = 1234; EXCEPTION WHEN NO_DATA_FOUND THEN -- 显式捕获ORA-01403 DBMS_OUTPUT.PUT_LINE('未找到员工记录'); WHEN TOO_MANY_ROWS THEN -- 同时处理ORA-01422 DBMS_OUTPUT.PUT_LINE('找到多条记录'); END; ``` #### **(2) 使用聚合函数规避错误** ```sql DECLARE v_avg_sal NUMBER; BEGIN SELECT NVL(AVG(sal), 0) -- 若无数据返回0 INTO v_avg_sal FROM emp WHERE deptno = 99; END; ``` #### **(3) 显式游标控制** ```sql DECLARE CURSOR c_emp IS SELECT * FROM emp WHERE job = 'Astronaut'; r_emp c_emp%ROWTYPE; BEGIN OPEN c_emp; FETCH c_emp INTO r_emp; IF c_emp%NOTFOUND THEN DBMS_OUTPUT.PUT_LINE('无宇航员岗位员工'); END IF; CLOSE c_emp; END; ``` --- ### **4. 高级场景处理** #### **(1) 批量操作中的容错** ```sql BEGIN FORALL i IN 1..emp_id_list.COUNT SAVE EXCEPTIONS UPDATE emp SET sal = sal * 1.1 WHERE empno = emp_id_list(i); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('错误数量: ' || SQL%BULK_EXCEPTIONS.COUNT); END; ``` #### **(2) 动态SQL处理** ```sql DECLARE v_count NUMBER; BEGIN EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM non_exist_table' INTO v_count; EXCEPTION WHEN NO_DATA_FOUND THEN NULL; -- 动态SQL通常不需要处理此错误 WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('其他错误: ' || SQLERRM); END; ``` --- ### **5. 预防措施** 1. **数据校验前置**:在执行查询前检查关键参数 ```sql IF NOT is_valid_empno(p_empno) THEN RAISE_APPLICATION_ERROR(-20001, '无效员工号'); END IF; ``` 2. **使用默认值函数**: ```sql SELECT COALESCE(MAX(sal), 0) INTO v_max_sal FROM emp; ``` 3. **约束设计**:通过外键约束确保数据存在性 --- ### **6. 常见混淆点** - **与ORA-01422的区别**: - ORA-01403:`SELECT INTO` **无数据** - ORA-01422:`SELECT INTO` **返回多行** - 两者都需通过异常处理解决 - **与SQL查询空结果的区别**: 纯SQL查询返回空结果集是正常行为,**不会**抛出ORA-01403,该错误仅出现在PL/SQL程序逻辑中。 --- 如果需要进一步了解以下内容,请告知: - 游标编程最佳实践 - 异常处理高级技巧 - 批量操作性能优化 - Oracle错误代码分类解析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值