· 8 years ago · Aug 23, 2018, 07:20 AM
1-- 1. Да Ñе направи така, че при добавÑне
2-- на нов ÐºÐ»Ð°Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾ да Ñе Ð´Ð¾Ð±Ð°Ð²Ñ Ð¸
3-- нов кораб ÑÑŠÑ Ñъщото име и Ñ Ð³Ð¾Ð´Ð¸Ð½Ð° на
4-- пуÑкане на вода = null.
5use ships;
6go
7create trigger t1
8on classes
9after insert
10as
11 insert into ships(name, class)
12 select class, class
13 from inserted;
14
15-- теÑтване:
16insert into classes
17values ('Test 1', 'bb', 'Bulgaria', 20, 20, 50000),
18 ('Test 2', 'bc', 'Bulgaria', 18, 21, 45000);
19select * from ships where name like 'Test %';
20drop trigger t1;
21
22-- 2. При изтриване на кораб автоматично да Ñе изтрива и неговиÑÑ‚ клаÑ, ако
23-- нÑма повече кораби от този клаÑ.
24create trigger t2_1
25on ships
26after delete
27as
28 delete from classes
29 where class not in (select class from ships);
30go
31-- Забележка: ако преди това е имало клаÑове без кораби - да не Ñе пипат!
32create trigger t2
33on ships
34after delete
35as
36 delete from classes
37 where class not in (select class from ships)
38 and class in (select class from deleted);
39go
40-- Забележка: ÐºÐ»Ð°Ñ Ð±ÐµÐ· кораби може да Ñе получи и при update
41-- Забележка: ако не използвахме deleted,
42-- а направо ships, щÑхме да изтрием и
43-- клаÑове, които Ñа били празни и преди
44-- ÑÑŠÐ¾Ñ‚Ð²ÐµÑ‚Ð½Ð¸Ñ delete
45
46-- теÑтване:
47-- в предишната задача вече Ñме добавили нов ÐºÐ»Ð°Ñ Ð¸ нов кораб, ще използваме Ñ‚ÑÑ…
48delete from ships
49where name like 'Test %';
50select *
51from classes
52where class like 'Test %';
53drop trigger t2;
54go
55
56-- леко - иÑкаме при изтриване на ÐºÐ»Ð°Ñ Ð´Ð° Ñе изтриват и
57-- вÑички кораби - ами може Ñ on delete cascade
58
59
60-- 3. Да Ñе направи така, че ако при
61-- добавÑне или обновÑване на кораб
62-- годината му на пуÑкане е по-голÑма от
63-- текущата година, то годината му да бъде
64-- променена на null. (това е задача 3-Рот 12.exerciseTriggers.pdf)
65
66-- в MSSQL нÑма BEFORE тригери, затова ще търÑим друг начин
67create trigger t3
68on ships
69after insert, update
70as
71 update ships
72 set launched = null
73 where name in (select name
74 from inserted
75 where launched > year(getdate()));
76-- OR: where launched > year(getdate()) and name in (select name from inserted);
77
78-- теÑтване:
79insert into ships values('Test','Iowa',2250);
80select * from ships where name='Test';
81delete from ships where name='Test';
82drop trigger t3;
83go
84-- горното решение ще гръмне, ако има check(launched <= year(getdate()) в ships!
85
86-- ако трÑбва в Ñ‚Ñлото на тригер да разграничим дали е бил извикан при insert или при update,
87-- можем да проверим if exists (select * from deleted)
88
89-- ако беше Ñамо за insert, можеше да направим instead of insert и вÑичко щеше да е наред
90create trigger t
91on ships
92instead of insert
93as
94insert into ships(name, class, launched)
95select name, class, case
96 when launched > year(getdate()) then null
97 else launched
98 end
99from inserted;
100
101-- вариант без case:
102-- insert into ships
103-- select * from inserted where launched <= year(getdate())
104-- union all
105-- select name, class, null from inserted where launched > year(getdate());
106drop trigger t;
107go
108
109-- 4. При промÑна на черно-бÑл филм на цветен ÑъответниÑÑ‚ продуцент да получава $100000.
110-- Ðко в една UPDATE заÑвка Ñа били променени нÑколко филма на един продуцент, той да получи
111-- Ñамо веднъж 100000.
112use movies;
113go
114
115create trigger t4
116on movie
117after update
118as
119 update movieexec
120 set networth = networth + 100000
121 where cert# in (select i.producerc#
122 from deleted d
123 join inserted i on d.title = i.title and d.year = i.year
124 where d.inColor = 'N' and i.inColor = 'Y');
125
126select * from movie;
127
128update movie
129set incolor = 'Y';
130
131select * from movieexec;
132
133update movie set incolor = 'N' where year = 2001;
134
135drop trigger t4;
136go
137
138-- СледващиÑÑ‚ пример е за Ð²Ð°Ð»Ð¸Ð´Ð°Ñ†Ð¸Ñ Ð½Ð° данни. Ð’Ñички задачи от такъв тип може да Ñе решат аналогично.
139
140-- 5. Да не Ñе допуÑка добавÑнето на ред
141-- в OUTCOMES, който да указва, че даден
142-- кораб е учаÑтвал в битка, преди да бъде
143-- пуÑнат на вода.
144
145-- Aко Ñе добавÑÑ‚ нÑколко реда и поне един от Ñ‚ÑÑ… нарушава уÑловието за коректноÑÑ‚,
146-- цÑлата Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ñ‰Ðµ бъде отменена.
147use ships;
148go
149
150create trigger t5
151on outcomes
152after insert
153as
154 if exists (select *
155 from inserted
156 join ships on ship = ships.name
157 join battles on battle = battles.name
158 where launched > year(battles.date))
159 begin
160 raiserror('Error: ship is launched after the battle', 16, 10); -- има Ñамо едно "е"
161 rollback;
162 end;
163
164-- проверка:
165insert into outcomes(ship, battle, result)
166values('Iowa', 'North Atlantic', 'sunk');
167
168select * from outcomes
169where ship='Iowa';
170
171drop trigger t5;
172go
173
174
175-- С помощта на INSTEAD OF тригерите може да Ñе изпълнÑват INSERT, UPDATE и DELETE заÑвки върху вÑеки изглед.
176
177-- 6. Да Ñе Ñъздаде изглед за вÑички
178-- потънали кораби (име на кораб и битка), Ñ‚.е. за вÑеки
179-- потънал кораб да казва в ÐºÐ¾Ñ Ð±Ð¸Ñ‚ÐºÐ° е потънал,
180-- който да позволÑва insert, update, delete.
181
182create view SunkShips
183as
184select ship, battle
185from outcomes
186where result = 'sunk';
187go
188
189-- UPDATE и DELETE могат да Ñе изпълнÑÑ‚ безпроблемно,
190-- но при INSERT новиÑÑ‚ ред би имал result = null, което не ни върши работа.
191
192create trigger t6
193on SunkShips
194instead of insert
195as
196 insert into outcomes(ship, battle, result)
197 select ship, battle, 'sunk'
198 from inserted;
199
200drop trigger t6;
201
202-- 7. -- Симулиране на ON DELETE SET NULLS
203-- MS SQL не поддържа ON DELETE SET NULLS. Да Ñе реализира Ñ
204-- тригери за Ð²ÑŠÐ½ÑˆÐ½Ð¸Ñ ÐºÐ»ÑŽÑ‡ movie.producerc#
205go
206use movies;
207go
208
209create trigger t
210on movieexec
211instead of delete --не може after trigger заради FK;
212-- в други СУБД има before тригери
213as
214begin
215 update movie
216 set producerc# = null
217 where producerc# in (select cert# from deleted);
218
219 -- Ñледващата Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ðµ тази, коÑто по принцип щеше да Ñе изпълни и без тригер
220 delete from movieexec
221 where cert# in (select cert# from deleted);
222end;
223
224-- ако имаме INSTEAD OF INSERT и иÑкаме да изпълним INSERT заÑвката, коÑто е била предвидена:
225-- INSERT INTO <table>
226-- SELECT * FROM INSERTED;
227
228go
229-- 12.exerciseTriggers.pdf
230
231-- Зад. 1. Да Ñе напише тригер за таблицата MovieExec, който не позволÑва
232-- Ñредната ÑтойноÑÑ‚ на Networth да е по-малка от 500 000 (ако при промени в
233-- таблицата тази ÑтойноÑÑ‚ Ñтане по-малка от 500 000, промените да бъдат
234-- отхвърлени).
235create trigger t
236on movieexec
237after insert, update, delete
238as
239 if (select AVG(networth)
240 from movieexec) < 500000
241 begin
242 raiserror('Error: Average networth cannot be < 500000', 16, 10);
243 rollback;
244 end;
245
246-- 3 Д) - модифицирана верÑиÑ:
247-- При добавÑне на нов Ð·Ð°Ð¿Ð¸Ñ Ð² StarsIn, ако новиÑÑ‚ кортеж указва неÑъщеÑтвуващ
248-- филм или актьор, да Ñе добавÑÑ‚ липÑващите данни в Ñъответната таблица
249-- (неизвеÑтните данни да бъдат NULL):
250create trigger t
251on starsin
252instead of insert
253as
254begin
255 insert into moviestar(name)
256 select distinct starname
257 from inserted
258 where starname not in (select name from moviestar);
259
260 insert into movie(title, year)
261 select distinct movietitle, movieyear
262 from inserted
263 where not exists (select * from movie
264 where title = movietitle and YEAR = movieyear);
265
266 insert into starsin
267 select *
268 from inserted;
269end;
270
271-- 2 Б) Ðикой производител на компютри не може да произвежда и принтери;
272use pc;
273create trigger t
274on product
275after insert, update
276as
277if exists (select *
278 from Product p1
279 join Product p2 on p1.maker = p2.maker
280 where p1.type = 'PC' and p2.type = 'Printer')
281begin
282 raiserror('...', 16, 10);
283 rollback;
284end;
285
286-- 2 Г) При променÑне на данните в таблицата Laptop Ñе уверете, че Ñредната
287-- цена на лаптопите за вÑеки производител е поне 2000;
288create trigger t
289on laptop
290after update
291as
292if exists (select maker
293 from laptop
294 join Product on laptop.model = Product.model
295 group by maker
296 having AVG(price) < 2000)
297begin
298 raiserror('...', 16, 10);
299 rollback;
300end;
301
302-- 2 Д) - по-добре Ñ CHECK
303
304-- 3 Ð’) Ðикой ÐºÐ»Ð°Ñ Ð½Ðµ може да има повече от два кораба;
305create trigger t
306on ships
307after insert, update
308as
309if exists (select class
310 from ships
311 group by class
312 having COUNT(*) > 2)
313begin
314 raiserror('...', 16, 10);
315 rollback;
316end;
317
318-- 3 E) прилича на 2 Г) и 3 В)
319
320-- 3 Ж) подобно нещо вече направихме за outcomes, но трÑбва тригер и за battles
321
322create trigger t
323on outcomes
324after insert, update
325as
326if exists (select *
327 from outcomes o1
328 join battles b1 on o1.battle = b1.name
329 join outcomes o2 on o1.ship = o2.ship
330 join battles b2 on o2.battle = b2.name
331 where o1.result = 'sunk'
332 and b1.date < b2.date)
333begin
334 raiserror('...', 16, 10);
335 rollback;
336end;
337-- ÑъщиÑÑ‚ тригер и за battles, но Ñамо за update
338
339-- примерни теоретични въпроÑи...