【问题标题】:Trigger in Oracle - increment column referenced with foreign key在 Oracle 中触发 - 增加用外键引用的列
【发布时间】:2021-12-04 20:17:31
【问题描述】:

我需要在 Oracle SQL 中创建一个触发器。我有 2 个具有这些属性的实体:

军队

  • army_name VARCHAR(50) 主键
  • number_of_soliders 整数

士兵

  • personal_number 整数主键
  • solider_name VARCHAR(50) NOT NULL
  • army_name VARCHAR(50) REFERENCES Army(army_name)

现在我需要为 number_of_soliders 创建一个触发器。默认值为 0,每次插入士兵时,我都需要将此值增加 1,对于特定的军队。因此,如果插入一个士兵并引用“美国陆军”,他们的士兵数量会自动增加一。

非常感谢

【问题讨论】:

    标签: oracle plsql triggers


    【解决方案1】:

    这是一个特别糟糕的主意,尽管显然很受欢迎。如果您尝试存储可以在运行时计算的内容,那么存储的值不正确只是时间问题。相信我。您根本不应该存储“number_of_soldiers”。你总是可以计算出来的

    SQL> show user
    USER is "SCOTT"
    SQL> -- create the tables
    SQL>  CREATE TABLE "SCOTT"."ARMY"
      2     (    "ARMY_NAME" VARCHAR2(20 BYTE) NOT NULL ENABLE,
      3           CONSTRAINT "ARMY_PK" PRIMARY KEY ("ARMY_NAME")
      4    USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
      5    TABLESPACE "USERS"  ENABLE
      6     ) SEGMENT CREATION DEFERRED
      7    PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
      8   NOCOMPRESS LOGGING
      9    TABLESPACE "USERS" ;
    
    Table created.
    
    SQL> --
    SQL>  CREATE TABLE "SCOTT"."SOLDIER"
      2     (    "COLUMN1" NUMBER(*,0) NOT NULL ENABLE,
      3          "SOLDIER_NAME" VARCHAR2(20 BYTE) NOT NULL ENABLE,
      4          "ARMY_NAME" VARCHAR2(20 BYTE) NOT NULL ENABLE,
      5           CONSTRAINT "SOLDIER_PK" PRIMARY KEY ("COLUMN1")
      6    USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
      7    TABLESPACE "USERS"  ENABLE,
      8           CONSTRAINT "SOLDIER_FK1" FOREIGN KEY ("ARMY_NAME")
      9            REFERENCES "SCOTT"."ARMY" ("ARMY_NAME") ENABLE
     10     ) SEGMENT CREATION DEFERRED
     11    PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
     12   NOCOMPRESS LOGGING
     13    TABLESPACE "USERS" ;
    
    Table created.
    
    SQL> -- load tables
    SQL> -- load tables
    SQL> insert into army values ('US ARMY');
    
    1 row created.
    
    SQL> insert into army values ('Canadian Army');
    
    1 row created.
    
    SQL> insert into soldier values (1,'Jody','US ARMY');
    
    1 row created.
    
    SQL> insert into soldier values (2,'Fred','US ARMY');
    
    1 row created.
    
    SQL> insert into soldier values (3,'Bob','US ARMY');
    
    1 row created.
    
    SQL> insert into soldier values (4,'Pierre','Canadian Army');
    
    1 row created.
    
    SQL> insert into soldier values (5,'Rocky','Canadian Army');
    
    1 row created.
    SQL> -- Do the query
    SQL> select army_name,
      2         count(*)
      3  from soldier
      4  group by army_name
      5  order by army_name;
    
    ARMY_NAME              COUNT(*)
    -------------------- ----------
    Canadian Army                 2
    US ARMY                       3
    
    2 rows selected.
    
    SQL> -- clean up
    SQL> drop table  soldier purge;
    
    Table dropped.
    
    SQL> drop table army purge;
    
    Table dropped.
    

    您还有其他几个设计问题,但这只是解决眼前的问题。

    【讨论】:

    • 像这样存储冗余数据几乎总是一个坏主意,因为很难完全保证它的正确性。最佳选择:制作视图或物化视图。如果确实需要存储它,最好制作包或程序来管理表中的数据(例如,可以在更新士兵之前始终锁定军队),而不是通过触发器来做到这一点。 15 年来,我管理了一个数据模型,其中包含数百个带有“智能”触发器的表,相信我,它让你大吃一惊。
    • 你说得有道理,但这是学校项目的一部分,我们应该在数据库中而不是视图中有触发器(不要问我为什么)
    • 通常在 education 项目中,您会构建一个 normalized model,而您仅在实践中使用 denormalized 模型,在这种情况下,您认为非常需要它。 @Vaclav 询问你的导师他/她是否可以评论
    【解决方案2】:

    触发器:

    SQL> create or replace trigger trg_ai_sol
      2    after insert or delete on soldier
      3    for each row
      4  begin
      5    if inserting then
      6       update army a set
      7         a.number_of_soldiers = a.number_of_soldiers + 1
      8         where a.army_name = :new.army_name;
      9    elsif deleting then
     10       update army a set
     11         a.number_of_soldiers = a.number_of_soldiers - 1
     12         where a.army_name = :old.army_name;
     13    end if;
     14  end;
     15  /
    
    Trigger created.
    

    测试:

    SQL> select * From soldier;
    
    no rows selected
    
    SQL> select * from army;
    
    ARMY_NAME    NUMBER_OF_SOLDIERS
    ------------ ------------------
    US Army                       0
    Another Army                  0
    
    SQL> insert into soldier values (1, 'Little', 'US Army');
    
    1 row created.
    
    SQL> insert into soldier
      2    select 2, 'Foot', 'Another Army' from dual union all
      3    select 3, 'Oracle', 'US Army' from dual;
    
    2 rows created.
    
    SQL> select * From soldier;
    
    PERSONAL_NUMBER SOLDIER_NAME         ARMY_NAME
    --------------- -------------------- --------------------
                  1 Little               US Army
                  2 Foot                 Another Army
                  3 Oracle               US Army
    
    SQL> select * from army;
    
    ARMY_NAME    NUMBER_OF_SOLDIERS
    ------------ ------------------
    US Army                       2
    Another Army                  1
    

    SQL> delete from soldier where personal_number in (2, 3);
    
    2 rows deleted.
    
    SQL> select * From soldier;
    
    PERSONAL_NUMBER SOLDIER_NAME         ARMY_NAME
    --------------- -------------------- --------------------
                  1 Little               US Army
    
    SQL> select * from army;
    
    ARMY_NAME    NUMBER_OF_SOLDIERS
    ------------ ------------------
    US Army                       1
    Another Army                  0
    
    SQL>
    

    【讨论】:

    • 非常感谢!按预期工作:)
    • @Vaclav 这会在士兵换军时给出不一致的值。
    【解决方案3】:

    您可以在 Oracle 中创建序列,例如

    CREATE SEQUENCE soldier_seq
        MINVALUE 1
        START WITH 1
        INCREMENT BY 1
        CACHE 10;
    

    在表中创建新行时,引用 key0 字段作为序列的增量

    insert into soldier
        (personnel_number, soldier_name, army_name)
        values (soldier_seq.nextval, "first and last Name", "USMC")
    

    【讨论】:

    • 序列永远不能保证没有间隙。当“士兵”从“军队”中移除时会发生什么?
    • 问题是如何根据士兵表中的插入(可能还有删除)在陆军级别维护number_of_soldiers
    • 差距不是问题,因为增量是要求。此外,您永远不会删除这样的记录,而是将其标记为已删除(非活动)。计算军队中的士兵人数将包括 where 子句 = active。
    猜你喜欢
    • 2010-12-16
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-29
    • 2010-09-23
    • 1970-01-01
    相关资源
    最近更新 更多