Триггер в Oracle — увеличение столбца, на который ссылается внешний ключ

#oracle #plsql #triggers

#Oracle #plsql #триггеры

Вопрос:

Мне нужно создать триггер в Oracle SQL. У меня есть 2 объекта с этими атрибутами:

армия

  • army_name VARCHAR(50) ПЕРВИЧНЫЙ КЛЮЧ
  • number_of_soliders ЦЕЛОЕ ЧИСЛО

solider

  • personal_number ЦЕЛОЧИСЛЕННЫЙ ПЕРВИЧНЫЙ КЛЮЧ
  • solider_name VARCHAR(50) NOT NULL
  • переменная army_name(50) ССЫЛАЕТСЯ на армию (army_name)

Теперь мне нужно создать триггер для number_of_soliders. Значение по умолчанию равно 0, и мне нужно увеличивать это значение на 1 каждый раз, когда вставляется solider, для конкретной армии. Таким образом, если вставлен солдат и ссылается на «Армию США», их количество солидеров автоматически увеличивается на единицу.

Большое спасибо

Ответ №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.
 

У вас есть несколько других проблем с дизайном, но это касается только непосредственного вопроса.

Комментарии:

1. Хранение избыточных данных, подобных этому, почти всегда является плохой идеей, потому что трудно полностью защитить его правильность. Наилучшие альтернативы: создать представление или материализованное представление. Если вам действительно нужно его сохранить, лучше создавать пакеты или процедуры, которые управляют данными в таблицах (которые могут, например, всегда блокировать армию перед обновлением солдат), чем делать это через триггеры. В течение 15 лет я управлял моделью данных с сотнями таблиц с «умными» триггерами на ней, и, поверьте мне, это будет байт вам в задницу.

2. Вы правы, но это часть школьного проекта, и ожидается, что в базе данных будут триггеры, а не представления (не спрашивайте меня, почему)

3. Обычно в образовательном проекте вы создаете нормализованную модель и используете денормализованную модель только на практике, когда вы видите в ней сильную потребность. @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>
 

Комментарии:

1. Большое спасибо! Работает, как и ожидалось 🙂

2. @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")
 

Комментарии:

1. Последовательности никогда не гарантируются без пробелов. И что происходит, когда «солдат» удаляется из «армии»?

2. Вопрос заключался в том, как поддерживать number_of_soldiers уровень армии на основе вставок (и, предположительно, удалений) в таблице Soldier.

3. Пробелы не являются проблемой, поскольку приращение было требованием. Кроме того, вы никогда не удаляете такую запись, но помечаете ее как удаленную (неактивную). Подсчет солдат в армии будет включать в себя предложение where = active .