oracle 触发检查重复项

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8482167/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 00:30:24  来源:igfitidea点击:

Trigger to check for duplicates

sqldatabaseoracletriggers

提问by Illyricum

I am writing a trigger and I have some problem. The trigger executes and compiles without errors but for some reasons it doesn't make the job I want to. If someone could help me.

我正在编写触发器,但遇到了一些问题。触发器执行和编译没有错误,但由于某些原因它不能完成我想要的工作。如果有人可以帮助我。

Here is the question:

这是问题:

Write a trigger to be executed on insert, update of a PROPERTY. Every property that gets entered gets checked against other properties of having the same: Agent(s), Owner(s), Address, if you find one then update Property Status to “Double” as a duplicate.

编写要在插入、更新属性时执行的触发器。输入的每个属性都会根据其他相同属性进行检查:代理、所有者、地址,如果找到,则将属性状态更新为“双重”作为副本。

I am inserting the same data and it let me do so, but it wouldn't normally!

我正在插入相同的数据,它让我这样做,但通常不会!

Here are my tables:

这是我的表:

create table Properties(  
            idProperties number(10) NOT NULL,
        Type varchar2(45) NOT NULL,
        SquareMeters varchar2(10) NOT NULL,
        Rooms number(10) NOT NULL,
        ConstructionDate date NOT NULL,
        FloorLocation varchar(20),
        Price number(10) NOT NULL,
        CityView varchar2(20),
        DateOfInsert date NOT NULL,
        DateOfExiration date NOT NULL,
        Address_FK number(20),
        Service_FK number(20),
        Ownership_FK number(20),
        Status_FK number(20),
        PropertyService_FK number(20))

create table Address(
          idAddress number(10) NOT NULL,
          address_name varchar2(20),
          City_FK number(20))


create table OwnerAgent(
           idOwnerAgent number(10) NOT NULL,
           Name varchar2(50)  NOT NULL,
           LastName varchar2(50)  NOT NULL,
           PhoneNr number(20),  
           Email varchar2(20),
           Sex varchar2(10),
           Profesion varchar2(20),
           Birthdate date,
           LastLogInDate date NOT NULL,
           Status varchar2(20),
           Address_FK number(20))

create table Ownership(
            idOwnership number(10) NOT NULL,
            PercentageOwed number(10)NOT NULL,
            RequiredPercentage number(10) NOT NULL,
        OwnerAgent_FK number(20))

Here is my trigger:

这是我的触发器:

CREATE OR REPLACE TRIGGER Check_Duplicate
before insert or update on properties
FOR each ROW

declare
v_dup number;

begin
    select count(idProperties) INTO v_dup from properties where Address_FK=:NEW.Address_FK and 
     Ownership_FK=:NEW.Ownership_FK;

 if v_dup > 0 then
   Raise_Application_Error (-20100, 'This property already exists. The insert is cancelled.');
end if;
end;

Thank you.

谢谢你。

回答by Justin Cave

You cannot, in general, enforce this sort of constraint in a trigger. You would need to use a constraint.

通常,您不能在触发器中强制执行此类约束。您将需要使用约束。

The problem you'll face if you try to use a trigger is that you'll generally encounter a "mutating table" exception. In general, a row-level trigger on table A (i.e. properties) cannot query table A. You can work around that problem by creating a package, creating a collection in that package, initializing the collection in a before statement trigger, writing the keys that are inserted or updated into the collection in a row-level trigger, and then iterating through the elements of the collection in an after statement trigger and issuing appropriate DML against the table. This, however, involves a whole lot of moving pieces and a whole lot of complexity (though the complexity is reduced if you're on 11g and can use a compound trigger instead).

如果您尝试使用触发器,您将面临的问题是您通常会遇到“变异表”异常。通常,表 A 上的行级触发器(即properties)不能查询表 A。您可以通过创建包、在该包中创建集合、在 before 语句触发器中初始化集合、编写键来解决该问题在行级触发器中插入或更新到集合中,然后在 after 语句触发器中迭代集合的元素,并针对表发出适当的 DML。然而,这涉及大量移动部分和大量复杂性(尽管如果您使用 11g 并且可以使用复合触发器,复杂性会降低)。

Additionally, if you try to use a trigger, you'll encounter issues in multi-user environments. If user A inserts a row in one session and user B inserts a duplicate row in a different session before user A commits, neither session's trigger will detect the duplicate row. You can potentially work around this sort of problem by explicitly locking a row in the parent table in order to serialize inserts into the table (intentionally making the application slower and less scalable). But a constraint would be a much more efficient and practical solution.

此外,如果您尝试使用触发器,则会在多用户环境中遇到问题。如果用户 A 在一个会话中插入一行,而用户 B 在用户 A 提交之前在不同的会话中插入重复的行,则这两个会话的触发器都不会检测到重复的行。您可以通过显式锁定父表中的一行以将插入序列化到表中来潜在地解决此类问题(故意使应用程序变慢且可扩展性降低)。但是约束将是一个更有效和更实用的解决方案。

All that being said, if you do only single-row inserts using the INSERT ... VALUESsyntax and restrict yourself to a single session, your trigger does appear to work

话虽如此,如果您只使用INSERT ... VALUES语法进行单行插入并将自己限制在单个会话中,那么您的触发器似乎确实可以工作

SQL> ed
Wrote file afiedt.buf

  1  create table Properties(
  2          idProperties number(10) NOT NULL,
  3          Address_FK number(20),
  4          Ownership_FK number(20)
  5* )
SQL> /

Table created.

SQL> CREATE OR REPLACE TRIGGER Check_Duplicate
  2  before insert or update on properties
  3  FOR each ROW
  4
  5  declare
  6  v_dup number;
  7
  8  begin
  9      select count(idProperties) INTO v_dup from properties where Address_FK=
:NEW.Address_FK and
 10       Ownership_FK=:NEW.Ownership_FK;
 11
 12   if v_dup > 0 then
 13     Raise_Application_Error (-20100, 'This property already exists. The inse
rt is cancelled.');
 14  end if;
 15  end;
 16  /

Trigger created.

SQL> insert into properties values( 1, 10, 100 );

1 row created.

SQL> insert into properties values( 2, 10, 100 );
insert into properties values( 2, 10, 100 )
            *
ERROR at line 1:
ORA-20100: This property already exists. The insert is cancelled.
ORA-06512: at "SCOTT.CHECK_DUPLICATE", line 9
ORA-04088: error during execution of trigger 'SCOTT.CHECK_DUPLICATE'

回答by xQbert

Assignment says to change status to double if duplicate, not prevent it

作业说如果重复则将状态更改为双倍,而不是阻止它

CREATE OR REPLACE TRIGGER Check_Duplicate
before insert or update on properties
FOR each ROW

declare
v_dup number;

begin
    select count(idProperties) INTO v_dup from properties where Address_FK=:NEW.Address_FK and 
     Ownership_FK=:NEW.Ownership_FK;

 if v_dup > 0 then
   :New.Status :='DOUBLE'
end if;
end;