-2

我需要定义一个我想应用于表格列的触发器。触发器应限制用户输入重复值而不是空值。或者你可以说,我需要知道主键的逻辑。

4

3 回答 3

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(*)它将逐渐变慢。如果你确实有一个索引,那么为什么不首先让它成为一个唯一的索引呢?

最后,来自触发器设计指南

不要创建重复数据库功能的触发器。

例如,如果您可以对约束执行相同操作,请不要创建触发器来拒绝无效数据(请参阅“触发器和约束的不同之处”)。

于 2013-07-12T14:44:03.497 回答
2

“我想学习,如何制作主键(当然是触发器)”

没有关于它的“当然”。约束不是触发器。它是一个内部过程,它使用索引和大量低级活动以可靠和有效的方式强制执行关系约束。

如果你想学习规则很简单:不为空,唯一性,序列化。因此,只需尝试在触发器中实现主键。你会发现你不能(剧透警告!)因为“变异表”问题。如果你不明白这意味着什么,那么有一个很好的话题可以阅读。


有一个问题“不可能定义一个触发器,它在插入之前检查值,它不应该是空值和唯一的吗?”

这个问题的答案是,不。好吧,您可以编写基于触发器的实现,但与其他“变异表”解决方法一样,它需要一个包和 AFTER 语句触发器(因此从技术上讲不是在插入之前)。

但说真的,这有什么意义呢?您不会学到任何有关主键实际工作方式的知识。变异表几乎总是指向一个糟糕的数据模型,这里肯定就是这种情况。

于 2013-07-12T09:59:35.860 回答
1

主键不是触发器。它是一个键,因为它标识了整行,这就是为什么它应该是唯一的(并且隐含地不为空)。它是“主要的”,因为它是最合适的候选键 - 根据您的决定 - 作为您的表的主要参考键。您可以将其添加为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约束应该放在列上。

于 2013-07-12T08:39:34.593 回答