我在Oracle中有一个表有几个约束。当我插入新记录并且并非所有约束都有效时,Oracle仅引发“第一个”错误。如何获取所有违反记录的行为?
CREATE TABLE A_TABLE_TEST (
COL_1 NUMBER NOT NULL,
COL_2 NUMBER NOT NULL,
COL_3 NUMBER NOT NULL,
COL_4 NUMBER NOT NULL
);
INSERT INTO A_TABLE_TEST values (1,null,null,2);
ORA-01400: cannot insert NULL into ("USER_4_8483C"."A_TABLE_TEST"."COL_2")
我想得到这样的东西:
Column COL_2: cannot insert NULL
Column COL_3: cannot insert NULL
This would be also sufficient:
Column COL_2: not valid
Column COL_3: not valid
当然我可以编写一个触发器并单独检查每一列,但我更喜欢约束而不是触发器,它们更容易维护,不需要手动编写代码。
有什么想法吗?
答案 0 :(得分:6)
没有直接的方法来报告所有可能的约束违规。因为Oracle在第一次违反约束时偶然发现,无法进一步评估,语句失败,除非该约束被延迟一个或者log errors
子句已包含在DML语句中。但应该注意的是log errors
子句将无法捕获所有可能的约束违规,只记录第一个。
其中一种可能的方法是:
exceptions
表。可以通过执行ora_home/rdbms/admin/utlexpt.sql
脚本来完成。表的结构非常简单; exceptions into <<exception table name>>
子句启用所有约束。如果您执行了utlexpt.sql
脚本,那么将存储表异常的名称为exceptions
。测试表:
create table t1(
col1 number not null,
col2 number not null,
col3 number not null,
col4 number not null
);
尝试执行insert
语句:
insert into t1(col1, col2, col3, col4)
values(1, null, 2, null);
Error report -
SQL Error: ORA-01400: cannot insert NULL into ("HR"."T1"."COL2")
禁用所有表的约束:
alter table T1 disable constraint SYS_C009951;
alter table T1 disable constraint SYS_C009950;
alter table T1 disable constraint SYS_C009953;
alter table T1 disable constraint SYS_C009952;
尝试再次执行先前失败的insert
语句:
insert into t1(col1, col2, col3, col4)
values(1, null, 2, null);
1 rows inserted.
commit;
现在,在exceptions
表中启用表的约束并存储异常(如果有的话):
alter table T1 enable constraint SYS_C009951 exceptions into exceptions;
alter table T1 enable constraint SYS_C009950 exceptions into exceptions;
alter table T1 enable constraint SYS_C009953 exceptions into exceptions;
alter table T1 enable constraint SYS_C009952 exceptions into exceptions;
检查exceptions
表:
column row_id format a30;
column owner format a7;
column table_name format a10;
column constraint format a12;
select *
from exceptions
ROW_ID OWNER TABLE_NAME CONSTRAINT
------------------------------ ------- ------- ------------
AAAWmUAAJAAAF6WAAA HR T1 SYS_C009951
AAAWmUAAJAAAF6WAAA HR T1 SYS_C009953
违反了两个限制条件。要查找列名称,只需参考user_cons_columns
数据字典视图:
column table_name format a10;
column column_name format a7;
column row_id format a20;
select e.table_name
, t.COLUMN_NAME
, e.ROW_ID
from user_cons_columns t
join exceptions e
on (e.constraint = t.constraint_name)
TABLE_NAME COLUMN_NAME ROW_ID
---------- ---------- --------------------
T1 COL2 AAAWmUAAJAAAF6WAAA
T1 COL4 AAAWmUAAJAAAF6WAAA
上面的查询为我们提供了列名和有问题记录的rowid。手头有rowid,找到导致约束违规的记录,修复它们,再次重新启用约束应该没有问题。
以下是用于生成alter table
语句以启用和禁用约束的脚本:
column cons_disable format a50
column cons_enable format a72
select 'alter table ' || t.table_name || ' disable constraint '||
t.constraint_name || ';' as cons_disable
, 'alter table ' || t.table_name || ' enable constraint '||
t.constraint_name || ' exceptions into exceptions;' as cons_enable
from user_constraints t
where t.table_name = 'T1'
order by t.constraint_type
答案 1 :(得分:3)
你必须实现一个before-insert触发器来遍历你关心的所有条件。
从数据库的角度考虑这种情况。当你执行insert
时,数据库基本上可以做两件事:成功完成插入或由于某种原因失败(通常是违反约束)。
数据库希望尽可能快地进行,而不是做不必要的工作。一旦发现第一次投诉违规,它就知道该记录不会进入数据库。因此,引擎明智地返回错误并停止检查进一步的约束。引擎没有理由获得完整的违规列表。
答案 2 :(得分:3)
与此同时,我找到了使用延迟约束的精益解决方案:
CREATE TABLE A_TABLE_TEST (
COL_1 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_2 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_3 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED,
COL_4 NUMBER NOT NULL DEFERRABLE INITIALLY DEFERRED
);
INSERT INTO A_TABLE_TEST values (1,null,null,2);
DECLARE
CHECK_CONSTRAINT_VIOLATED EXCEPTION;
PRAGMA EXCEPTION_INIT(CHECK_CONSTRAINT_VIOLATED, -2290);
CURSOR CheckConstraints IS
SELECT TABLE_NAME, CONSTRAINT_NAME, COLUMN_NAME
FROM USER_CONSTRAINTS
JOIN USER_CONS_COLUMNS USING (TABLE_NAME, CONSTRAINT_NAME)
WHERE TABLE_NAME = 'A_TABLE_TEST'
AND DEFERRED = 'DEFERRED'
AND STATUS = 'ENABLED';
BEGIN
FOR aCon IN CheckConstraints LOOP
BEGIN
EXECUTE IMMEDIATE 'SET CONSTRAINT '||aCon.CONSTRAINT_NAME||' IMMEDIATE';
EXCEPTION
WHEN CHECK_CONSTRAINT_VIOLATED THEN
DBMS_OUTPUT.PUT_LINE('Column '||aCon.COLUMN_NAME||' violated');
END;
END LOOP;
END;
它适用于任何检查约束(不仅仅是NOT NULL
)。检查FOREIGN KEY
约束也应该有效。
添加/修改/删除约束不需要任何进一步的维护。