我需要定义一个我想应用于表格列的触发器。触发器应限制用户输入重复值而不是空值。或者你可以说,我需要知道主键的逻辑。
3 回答
仅仅因为您似乎打算看到这个失败,并且不想从 APC 的观点中拿走任何东西,只要它是一个before
触发器,这乍一看似乎是有效的:
create table t42 (id number);
create trigger trig42
before insert or update on t42
for each row
declare
c number;
begin
if :new.id is null then
raise_application_error(-20001, 'ID is null');
end if;
select count(*) into c from t42 where id = :new.id;
if c > 0 then
raise_application_error(-20002, 'ID is not unique');
end if;
end;
/
它会编译,如果你插入数据,你会得到你想要的行为:
insert into t42 values (1);
1 rows inserted.
insert into t42 values (1);
Error starting at line 20 in command:
insert into t42 values (1)
Error report:
SQL Error: ORA-20002: ID is not unique
ORA-06512: at "STACKOVERFLOW.TRIG42", line 9
ORA-04088: error during execution of trigger 'STACKOVERFLOW.TRIG42'
insert into t42 values (null);
Error starting at line 22 in command:
insert into t42 values (null)
Error report:
SQL Error: ORA-20001: ID is null
ORA-06512: at "STACKOVERFLOW.TRIG42", line 5
ORA-04088: error during execution of trigger 'STACKOVERFLOW.TRIG42'
select * from t42;
ID
----------
1
这似乎做你想做的事。但如果您有多个会话,则不会。我没有在本次会议上承诺;在另一个会话中,我可以这样做:
insert into t42 values (1);
1 row created.
select * from t42;
ID
----------
1
1 row selected.
嗯,这很奇怪。好吧,也许它被推迟了......让我们同时提交它们:
commit;
select * from t42;
ID
----------
1
1
2 rows selected.
哎呀。一旦会话看不到另一个会话的未提交数据,所以这永远不会起作用。
此外,当我们在单个语句中插入多行时,变异表问题就会出现:
SQL> insert into t42 select level+1 from dual connect by level <= 5;
insert into t42 select level+1 from dual connect by level <= 5
*
ERROR at line 1:
ORA-04091: table STACKOVERFLOW.T42 is mutating, trigger/function may not see it
ORA-06512: at "STACKOVERFLOW.TRIG42", line 7
ORA-04088: error during execution of trigger 'STACKOVERFLOW.TRIG42'
SQL>
双重哎呀。
即使有一个after
触发器和一个包来解决变异表问题,你仍然会遇到这个问题(我认为),除非你为每次插入或更新锁定整个表。正如 APC 所说,约束是在数据库的内部深处实现的,而不是在这个级别。
是不是不可能定义一个触发器,它在插入之前检查值,它不应该是空的并且是唯一的?
不是当你有不止一个会话时,不。即使在一个会话中,除非您在列上有索引,否则性能将无法扩展,因为count(*)
它将逐渐变慢。如果你确实有一个索引,那么为什么不首先让它成为一个唯一的索引呢?
最后,来自触发器设计指南:
不要创建重复数据库功能的触发器。
例如,如果您可以对约束执行相同操作,请不要创建触发器来拒绝无效数据(请参阅“触发器和约束的不同之处”)。
“我想学习,如何制作主键(当然是触发器)”
没有关于它的“当然”。约束不是触发器。它是一个内部过程,它使用索引和大量低级活动以可靠和有效的方式强制执行关系约束。
如果你想学习规则很简单:不为空,唯一性,序列化。因此,只需尝试在触发器中实现主键。你会发现你不能(剧透警告!)因为“变异表”问题。如果你不明白这意味着什么,那么有一个很好的话题可以阅读。
有一个问题“不可能定义一个触发器,它在插入之前检查值,它不应该是空值和唯一的吗?”
这个问题的答案是,不。好吧,您可以编写基于触发器的实现,但与其他“变异表”解决方法一样,它需要一个包和 AFTER 语句触发器(因此从技术上讲不是在插入之前)。
但说真的,这有什么意义呢?您不会学到任何有关主键实际工作方式的知识。变异表几乎总是指向一个糟糕的数据模型,这里肯定就是这种情况。
主键不是触发器。它是一个键,因为它标识了整行,这就是为什么它应该是唯一的(并且隐含地不为空)。它是“主要的”,因为它是最合适的候选键 - 根据您的决定 - 作为您的表的主要参考键。您可以将其添加为ALTER TABLE your_table_name ADD CONSTRAINT PK_your_table_name PRIMARY KEY (your_key_column)
.
如果您不想添加这样的主键(这是一个坏主意),但想为该表添加唯一索引:CREATE UNIQUE INDEX UQ_IX_your_table_your_column ON your_table_name (unique_column_name)
. NOT NULL
约束应该放在列上。