#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 .