Тригерът на SQL Server е специален тип съхранени процедури, които се изпълняват автоматично, когато настъпи събитие в конкретен сървър на база данни. SQL Server ни предоставя два основни типа тригери:DML Тригери и DDL тригери. DDL тригерите ще бъдат задействани в отговор на различни събития на езика за дефиниране на данни (DDL), като изпълнение на оператори CREATE, ALTER, DROP, GRANT, DENY и REVOKE T-SQL. DDL тригерът може да реагира на DDL действията, като предотвратява въздействието на тези промени върху базата данни, извършва друго действие в отговор на тези DDL действия или записва тези промени, които се изпълняват спрямо базата данни.
DML тригерът на SQL Server е специален тип съхранени процедури, които са предназначени да изпълняват поредица от действия върху таблица на база данни, към която е прикачен тригерът, когато се случи събитие на езика за манипулиране на данни (DML), като INSERT, UPDATE или DELETE действие се извършва за промяна на съдържанието на таблиците или изгледите на базата данни, независимо от това дали редовете на таблицата са засегнати или не. Тригерите се различават от съхранените процедури по това, че тригерите се задействат автоматично, когато настъпи предварително дефинирана модификация на данни. Задействанията на DML могат да се използват за поддържане на целостта на данните и прилагане на бизнес правилата на компанията, същите като проверка на таблици и функционалност на ограниченията на външни ключове, чрез извършване на одитни процеси и други действия след DML. Можете да използвате тригерите на DML, за да отправяте заявки към други таблици и да изпълнявате сложни T-SQL заявки.
Ако тригерът се задейства, специален тип виртуални таблици, наречен Вмъкнати и Изтрити таблиците ще се използват за запазване на стойностите на данните преди и след модификацията. Инструкцията за задействане ще работи в обхвата на същата транзакция, която задейства това задействане. Това означава, че транзакцията няма да бъде ангажирана напълно, докато операторът за задействане не бъде завършен успешно. От друга страна, транзакцията ще бъде върната назад, ако операторът на тригера не успее.
Има два типа DML задействания:AFTER илиЗА тригер иВМЕСТО спусък. Тригерът AFTER ще бъде задействан и изпълнен след извършване на действието INSERT, UPDATE или DELETE, което го задейства успешно. Също така, всички референтни каскадни действия и проверки на ограничения трябва да успеят преди задействане на спусъка. Тригерът AFTER може да бъде дефиниран само на ниво таблица без възможност да се дефинира в изгледи. Тригерът INSTEAD OF се използва за отмяна на оператора на действието, което задейства тригера, с изявлението, предоставено в тригера, връщайки назад този израз след повдигане на грешка, когато някой се опитва да извърши действие, което нарушава конкретна политика, като например актуализиране критична финансова колона или записване на промяната в таблица за одит преди извършване на промяната. Тригерът INSTEAD OF ви позволява да ВМЕСВАТЕ, АКТУАЛИЗИРАТЕ или ИЗТРИВАТЕ данни от изгледите, които се позовават на данни от множество таблици, в допълнение към възможността да отхвърлите част от пакетна заявка и да изпълните успешно друга част от тази партида. Тригерът INSTEAD OF не може да се използва с обновяеми изгледи, които имат WITH CHECK OPTION и в таблиците с референтна връзка, която определя каскадни действия при DELETE или UPDATE.
След като обсъдим теоретически тригерите, ще започнем да показваме това, което обсъждаме на практика. В предстоящите демонстрации ще покажем различните ситуации, в които можем да се възползваме от тригерите на SQL Server.
СЛЕД... DML тригер
Да приемем, че трябва да проследим DML действията, които се извършват върху конкретна таблица и да запишем тези регистрационни файлове в таблица с история, където идентификаторът на вмъкнатия, актуализиран или изтрит запис и действието, което се извършва, ще бъдат записани в таблицата с историята. T-SQL изразите CREATE TABLE по-долу могат да се използват за създаване както на таблици с източник, така и на хронология:
CREATE TABLE TriggerDemo_Parent( ID INT IDENTITY (1,1) PRIMARY KEY, Emp_First_name VARCHAR (50), Emp_Last_name VARCHAR (50), Emp_Salary INT )GOCREATE TABLE TriggerDemo_History( ID INTRY INT 1) Parent PRINT , Извършено действие VARCHAR (50), )GO
За да проследим операцията INSERT, ще създадем DML тригер, който ще се задейства след извършване на операция INSERT върху родителската таблица. Този тригер ще извлече последната вмъкната стойност на ID към тази родителска таблица от виртуалната вмъкната таблица, както е в оператора CREATE TRIGGER T-SQL по-долу:
СЪЗДАВАНЕ НА TRIGGER AfterInsertTriggerON TriggerDemo_ParentAFTER INSERTASINSERT INTO TriggerDemo_History СТОЙНОСТИ ((ИЗБЕРЕТЕ ТОП 1 вмъкнат.ID FROM вмъкнат), 'Insert')GO
Проследяването на операцията DELETE може да се постигне чрез създаване на DML тригер, който се задейства след извършване на операцията DELETE върху родителската таблица. Отново тригерът ще извлече стойността на идентификатора на последния изтрит запис от тази родителска таблица от виртуалната изтрита таблица, както е в оператора CREATE TRIGGER T-SQL по-долу:
СЪЗДАВАНЕ НА TRIGGER AfterDeleteTriggerON TriggerDemo_ParentAFTER DELETEASINSERT INTO TriggerDemo_History VALUES ((ИЗБЕРЕТЕ ТОП 1 deleted.ID FROM deleted), 'Delete')GO
Накрая ще проследим и операцията UPDATE чрез създаване на DML тригер, който ще се задейства след извършване на операция UPDATE на родителската таблица. В рамките на този тригер ще извлечем последната актуализирана стойност на идентификатора от тази родителска таблица от виртуалната вмъкната таблица, като вземем предвид, че процесът UPDATE се изпълнява чрез изтриване на записа и вмъкване на нов запис с актуализираните стойности, както в CREATE TRIGGER T-SQL изявление по-долу:
СЪЗДАВАНЕ НА TRIGGER AfterUPDATETriggerON TriggerDemo_ParentAFTER UPDATEASINSERT INTO TriggerDemo_History STONS ((ИЗБЕРЕТЕ ТОП 1 вмъкнат.ID FROM вмъкнат), 'UPDATE')GO
Таблиците и тригерите вече са готови за нашето тестване. Ако се опитате да вмъкнете нов запис в родителската таблица, като използвате оператора INSERT INTO T-SQL по-долу:
INSERT INTO TriggerDemo_Parent VALUES ('AAA','BBB',500)
След това, като проверите плана за изпълнение, генериран чрез изпълнение на предишния оператор INSERT, ще видите, че ще бъдат извършени две операции за вмъкване, засягащи две таблици; родителската таблица със стойностите, посочени в оператора INSERT и таблицата на историята поради задействане на тригера AFTER INSERT, както е показано в плана за изпълнение по-долу:
Също така е ясно, когато проверите данните, вмъкнати както в родителската, така и в хронологията, като използвате операторите SELECT по-долу:
SELECT * FROM TriggerDemo_ParentGOSELECT * FROM TriggerDemo_History
Където стойностите, посочени в оператора INSERT, ще бъдат вмъкнати в родителската таблица, а регистрационният файл за вмъкване, който съдържа идентификатора на вмъкнатия запис и извършената операция, ще бъде вмъкнат в таблицата на историята, както е показано в резултата по-долу:
Сега, ако се опитате да актуализирате съществуващ запис в родителската таблица с помощта на оператора UPDATE T-SQL по-долу:
АКТУАЛИЗИРАНЕ TriggerDemo_Parent SET Emp_Salary=550 WHERE ID=1
И проверете плана за изпълнение, генериран чрез изпълнение на предишния оператор UPDATE, ще видите, че операцията за актуализиране ще бъде последвана от операция за вмъкване, засягаща две различни таблици; родителската таблица ще бъде актуализирана със стойността, посочена в оператора UPDATE и операцията вмъкване в таблицата на историята поради задействане на тригера AFTER UPDATE, както е показано в плана за изпълнение по-долу:
Проверка както на родителския запис, така и на записите в хронологията с помощта на операторите SELECT по-долу:
SELECT * FROM TriggerDemo_ParentGOSELECT * FROM TriggerDemo_History
Ще видите, че инструкцията за актуализация ще промени стойността Emp_Salary в родителската таблица със стойността, посочена в оператора UPDATE, а дневникът за актуализиране, който съдържа идентификатора на актуализирания запис и извършената операция, ще бъде вмъкнат в таблицата на историята, като показани в резултата по-долу:
В последния сценарий на тригера AFTER DML ще проследим изтриването на съществуващ запис от родителската таблица с помощта на оператора DELETE T-SQL по-долу:
ИЗТРИВАНЕ ОТ TriggerDemo_Parent WHERE ID=1
След това проверете плана за изпълнение, генериран чрез изпълнение на предишния оператор DELETE, ще видите, че операцията DELETE ще бъде последвана от операцията вмъкване, засягаща две различни таблици; родителската таблица, от която записът с предоставения идентификатор в клаузата WHERE на оператора DELETE ще бъде изтрит и операцията вмъкване в таблицата на историята поради задействане на тригера AFTER DELETE, както е показано в плана за изпълнение по-долу:
Ако проверите записите както на родителя, така и на записите в хронологията, като използвате операторите SELECT по-долу:
SELECT * FROM TriggerDemo_ParentGOSELECT * FROM TriggerDemo_History
Ще видите, че записът със стойност на ID, равна на 1, е изтрит от родителската таблица, която е предоставена в оператора DELETE, а журналът за изтриване, който съдържа идентификатора на изтрития запис и извършената операция, ще бъде вмъкнат в таблицата на историята , както е показано в резултата по-долу:
ВМЕСТО... DML тригер
Вторият тип DML тригери е ВМЕСТО DML тригера. Както бе споменато по-горе, тригерът INSTEAD OF ще отмени оператора на действието, което задейства тригера, с оператора, предоставен в тригера. Да приемем, че трябва да регистрираме DML действията, които потребителите се опитват да извършат на конкретна таблица, без да им позволяваме да извършат това действие. T-SQL изразите CREATE TABLE по-долу могат да се използват за създаване както на изходната, така и на алтернативните таблици:
CREATE TABLE TriggerDemo_NewParent( ID INT IDENTITY (1,1) PRIMARY KEY, Emp_First_name VARCHAR (50), Emp_Last_name VARCHAR (50), Emp_Salary INT )GOCREATE TABLE TriggerDemo_Instead INT IDParent( IDParent1, K Parent, IDParent1 , Извършено действие VARCHAR (50), )GO
След като създадем двете таблици, ще вмъкнем един запис в изходната таблица за нашата демонстрация, използвайки оператора INSERT INTO по-долу:
INSERT INTO TriggerDemo_NewParent VALUES ('AA','BB', 500)
За тази демонстрация ще създадем три тригера, които да отменят операциите INSERT, UPDATE и DELETE. Първият тригер ще се използва, за да се предотврати всякаква операция за вмъкване в родителската таблица и дневника, които се променят в алтернативната таблица. Тригерът се създава с помощта на оператора CREATE TRIGGER T-SQL по-долу:
СЪЗДАВАНЕ НА TRIGGER InsteadOfInsertTriggerON TriggerDemo_NewParent INSTEAD OF INSERTASINSERT INTO TriggerDemo_InsteadParent VALUES ((ИЗБЕРЕТЕ ТОП 1 вмъкнат.ID ОТ вмъкнат), 'Опит за вмъкване на нов ID')GO
Вторият тригер се използва за предотвратяване на всяка операция по актуализиране на родителската таблица и регистрационния файл, които се променят в алтернативната таблица. Този тригер се създава, както следва:
СЪЗДАВАНЕ НА TRIGGER InsteadOfUpdateTriggerON TriggerDemo_NewParent ВМЕСТО АКТУАЛИЗИРАНЕ ASINSERT INTO TriggerDemo_InsteadParent VALUES ((ИЗБЕРЕТЕ ТОП 1 вмъкнат. ИД от вмъкнат), 'Опит за актуализиране на съществуващ идентификационен номер')GO
Последният тригер ще се използва, за да се предотврати всякаква операция за изтриване на родителската таблица и регистрационния файл, които се променят в алтернативната таблица. Този тригер се създава по следния начин:
СЪЗДАВАНЕ НА TRIGGER InsteadOfDeleteTriggerON TriggerDemo_NewParent ВМЕСТО ИЗТРИВАНЕ НА ВЪВЕЖДАНЕ В СТОЙНОСТИТЕ TriggerDemo_InsteadParent ((ИЗБЕРЕТЕ ТОП 1 вмъкнат. ИД от вмъкнат), 'Опит за изтриване на съществуващ идентификационен номер')GO
Двете маси и трите тригера са готови сега. Ако се опитате да вмъкнете нова стойност в родителската таблица, като използвате оператора INSERT INTO T-SQL по-долу:
INSERT INTO TriggerDemo_NewParent VALUES ('CCC','DDD',500)
След това проверете записите както на родителя, така и на алтернативната таблица, като използвате операторите SELECT по-долу:
SELECT * FROM TriggerDemo_NewParentGOSELECT * FROM TriggerDemo_InsteadParent
Поради факта, че имаме тригера INSTEAD OF INSERT в родителската таблица, от резултата ще видите, че в родителската таблица не се вмъква нов запис и в алтернативната таблица се вмъква дневник за операцията вмъкване, както е показано в резултата по-долу:
Опит за актуализиране на съществуващ запис в родителската таблица с помощта на оператора UPDATE T-SQL по-долу:
АКТУАЛИЗИРАНЕ TriggerDemo_NewParent SET Emp_Salary=550 WHERE ID=1
След това се проверяват записите както на родителя, така и на алтернативната таблица с помощта на операторите SELECT по-долу:
SELECT * FROM TriggerDemo_NewParentGOSELECT * FROM TriggerDemo_InsteadParent
От резултата ще видите, че стойността на Emp_Salary на записа със стойност на ID, равна на 1 от родителската таблица, няма да бъде променена и журналът за операцията за актуализиране се вмъква в алтернативната таблица поради наличието на тригера INSTEAD OF UPDATE в родителската таблица, както е показано в резултата по-долу:
И накрая, ако се опитаме да изтрием съществуващ запис от родителската таблица с помощта на оператора DELETE T-SQL по-долу:
ИЗТРИВАНЕ ОТ TriggerDemo_NewParent WHERE ID=1
И проверете записите както на родителя, така и на алтернативната таблица, като използвате операторите SELECT по-долу:
SELECT * FROM TriggerDemo_NewParentGOSELECT * FROM TriggerDemo_InsteadParent
От резултата ще стане ясно, че записът със стойност на ID, равна на 1 от родителската таблица, няма да бъде изтрит, а журналът за операцията за изтриване се вмъква в алтернативната таблица поради наличието на тригера INSTEAD OF DELETE в родителската таблица таблица, както е показано в резултата по-долу:
СЛЕД... DML тригер със съобщения
Тригерът AFTER може да се използва и за издигане на предупредително съобщение за потребител. В този случай заявката ще бъде информационно съобщение, което няма да попречи на изпълнението на оператора, който задейства това задействане. Нека изхвърлим създадения по-рано тригер INSTEAD OF UPDATE и го заменим с друг тригер AFTER UPDATE, който ще изведе предупредителна грешка след извършване на каквато и да е операция за актуализиране с помощта на операторите DROP/CREATE TRIGGER T-SQL по-долу:
DROP TRIGGER InsteadOfUpdateTriggerCREATE TRIGGER ReminderTrigger ON TriggerDemo_NewParent СЛЕД АКТУАЛИЗИРАНЕ КАТО RAISERROR ('Извършва се актуализация на таблицата TriggerDemo_NewParent', 16, 10); ОТИВАЙТЕ
Ако се опитате да актуализирате стойността на Emp_Salary на служителя със стойността на ID, равна на 1, като използвате оператора UDPATE по-долу:
АКТУАЛИЗИРАНЕ TriggerDemo_NewParent SET Emp_Salary=550 WHERE ID=1
В Съобщения ще бъде изведено съобщение за грешка раздел, който съдържа съобщението, предоставено в създадения тригер, както е показано по-долу:
Проверка на данните от родителската таблица с помощта на оператора SELECT по-долу:
ИЗБЕРЕТЕ * ОТ TriggerDemo_NewParent
От резултата ще видите, че Emp_Salary е актуализиран успешно, както е показано по-долу:
Ако имате нужда от тригера AFTER UPDATE, за да спрете операцията по актуализиране след издигане на съобщението за грешка, ROLLBACK операторът може да бъде добавен към тригера, за да се отмени операцията за актуализиране, която е задействала този тригер, припомняйки, че тригерът и операторът, който задейства тригера, ще бъдат изпълнени в една и съща транзакция. Това може да се постигне с помощта на оператора ALTER TRIGGER T-SQL, вижте:
ALTER TRIGGER ReminderTrigger ON TriggerDemo_NewParent СЛЕД АКТУАЛИЗИРАНЕ КАТО RAISERROR ('Извършва се актуализация на таблицата TriggerDemo_NewParent', 16, 10); ROLLBACKGO
Ако се опитате да актуализирате стойността на Emp_Salary на служителя с идентификатор, равен на 1, като използвате оператора UPDATE по-долу:
АКТУАЛИЗИРАНЕ на TriggerDemo_NewParent SET Emp_Salary=700 WHERE ID=1
Отново в Съобщения ще се покаже съобщение за грешка раздел, но този път операцията за актуализиране ще бъде напълно върната, както е показано в съобщенията за грешка по-долу:
Проверка на стойността от изходната таблица с помощта на оператора SELECT по-долу:
ИЗБЕРЕТЕ * ОТ TriggerDemo_NewParent
Ще видите, че стойността на Emp_Salary не се е променила, тъй като тригерът AFTER UPDATE връща обратно цялата транзакция след повдигане на съобщението за грешка, както е показано в резултата от таблицата по-долу:
Недостатъци на тригера
С всички споменати предимства на тригерите на SQL Server, тригерите увеличават сложността на базата данни. Ако тригерът е лошо проектиран или прекомерно използван, това ще доведе до големи проблеми с производителността, като блокирани сесии, поради удължаване на живота на транзакцията за по-дълго време, допълнителни разходи за системата поради изпълнението му всеки път, когато INSERT, UPDATE или Действието DELETE е извършено или може да доведе до проблеми със загуба на данни. Освен това не е лесно да видите и проследите тригерите на базата данни, особено ако няма документация за това, тъй като е невидима за разработчиците и приложенията.
Алтернативи за задействане... Налагане на целостта
Ако се установи, че тригерите увреждат производителността на вашия екземпляр на SQL Server, трябва да ги замените с други решения. Например, вместо да се използват тригерите за налагане на целостта на обекта, тя трябва да бъде наложена на най-ниското ниво чрез използване на ограниченията PRIMARY KEY и UNIQUE. Същото се прилага за целостта на домейна, която трябва да бъде наложена чрез ограниченията CHECK, и референтната цялост, която трябва да бъде наложена чрез ограниченията FOREIGN KEY. Можете да използвате задействанията на DML само ако функциите, поддържани от конкретно ограничение, не могат да отговарят на изискванията на приложението ви.
Нека сравним между налагането на целостта на домейна чрез DML тригери и използването на ограниченията CHECK. Да приемем, че трябва да наложим вмъкване на положителни стойности само в колоната Emp_Salary. Ще започнем със създаване на проста таблица с помощта на оператора CREATE TABLE T-SQL по-долу:
CREATE TABLE EmployeeSalaryTrigger( ID INT IDENTITY (1,1) PRIMARY KEY, Emp_First_name VARCHAR (50), Emp_Last_name VARCHAR (50), Emp_Salary INT )GO
След това дефинирайте тригера AFTER INSERT DML, който гарантира, че ще вмъкнете положителна стойност в колоната Emp_Salary чрез връщане назад на транзакцията, ако потребителят вмъкне отрицателна стойност на заплатата, като използвате оператора CREATE TRIGGER T-SQL по-долу:
СЪЗДАДЕТЕ TRIGGER TRGR_EmployeeSalary ON EmployeeSalaryTrigger СЛЕД ВМЪКВАНЕ НА ASDECLARE @EmpSal КАТО INTSET @EmpSal =(ИЗБЕРЕТЕ ТОП 1 вмъкнат.Emp_Salary ОТ вмъкнато)IF @EmpSal<0BEGIN RAIStERROR, negative ('Claanry) ROLLBACKEND
За целите на сравнението ще създадем друга проста таблица със същата схема и ще дефинираме ограничение CHECK в израза CREATE TABLE, за да приема само положителни стойности в колоната Emp_Salary, както е показано по-долу:
CREATE TABLE EmployeeSalaryConstraint( ID INT IDENTITY (1,1) PRIMARY KEY, Emp_First_name VARCHAR (50), Emp_Last_name VARCHAR (50), Emp_Salary INT CONSTRAINT EmpSal CHECK (Emp_Salary) GO>=>Ако се опитате да вмъкнете записа по-долу, който съдържа отрицателна стойност Emp_Salary в първата таблица, която има предварително дефиниран тригер, като използвате оператора INSERT INTO по-долу:
INSERT INTO EmployeeSalaryTrigger VALUES('Ali', 'Fadi',-4)GOИнструкцията INSERT няма да изведе съобщение за грешка, показващо, че не можете да вмъкнете отрицателна стойност в колоната Emp_Salary и да върнете обратно цялата транзакция поради наличието на тригер AFTER INSERT, както е показано в съобщението за грешка по-долу:
Освен това, ако се опитате да вмъкнете същия запис, който съдържа отрицателна стойност Emp_Salary във втората таблица, която има предварително дефинирано ограничение CHECK, като използвате оператора INSERT INTO по-долу:
INSERT INTO EmployeeSalaryConstraint VALUES ('Ali', 'Fadi',-4)Инструкцията INSERT отново ще се провали, показвайки, че се опитвате да вмъкнете стойността, която е в конфликт с условието на ограничението CHECK, както е показано в съобщението за грешка по-долу:
От предишните резултати виждате, че както методът за задействане, така и методът за ограничаване CHECK постигат целта, като ви предотвратяват вмъкването на отрицателни стойности Emp_Salary. Но коя е по-добра? Нека сравним производителността на двата метода, като проверим теглото на плана за изпълнение за всеки от тях. От генерираните планове за изпълнение след изпълнение на двете заявки ще видите, че теглото на метода за задействане е три пъти теглото на метода на ограничението CHECK, както е показано в сравнението на плана за изпълнение по-долу:
Освен това, за да сравним времето за изпълнение, изразходвано от всеки един, нека изпълним всеки 1000 пъти, използвайки T-SQL изразите по-долу:
INSERT INTO EmployeeSalaryTrigger VALUES('Ali', 'Fadi',-4)GO 10000 INSERT INTO EmployeeSalaryConstraint VALUES ('Ali', 'Fadi',-4)GO 10000Ще видите, че първият метод, който използва тригера, ще отнеме около 31 мс да се изпълни напълно, като вторият метод, който използва ограничението CHECK, ще отнеме само 17 мс , което е около 0,5 времета изисква се в метода, използващ спусъка. Това се дължи на факта, че тригерът ще удължи живота на транзакцията и ще отмени заявката, която задейства тригера, след като го изпълни, когато бъде открито нарушение на целостта, което води до влошаване на производителността поради процеса на връщане назад. Случаят е различен при използване на ограничението CHECK, където ограничението ще свърши своята работа, преди да извърши каквато и да е промяна в данните, без да изисква връщане назад в случай на нарушение.
Алтернативи за задействане... Одит
Както споменахме по-рано, тригерите могат да се използват и за одит и проследяване на промените, извършени в конкретна таблица. Ако този метод на одит причини влошаване на производителността във вашия екземпляр на SQL Server, можете лесно да го замените с ИЗХОД клауза. Клаузата OUTPUT връща информация за всеки ред, засегнат от операцията INSERT, UPDATE или DELETE, под формата на съобщение за потвърждение или стойност, която може да бъде вмъкната в историческата таблица. Методът на клаузата OUTPUT ни предоставя също повече контрол върху изпълнявания код, тъй като той ще бъде добавен към самия оператор за вмъкване, модифициране или изтриване, когато пожелаете, обратно на тригера, който винаги ще се изпълнява.
Нека да сравним между регистрирането на вмъкването и модификацията на данни в таблицата на историята с помощта на DML тригери и използването на клаузата OUTPUT. Ще започнем със създаването на таблиците за производство и история по-долу с помощта на оператора CREATE TABLE T-SQL по-долу:
CREATE TABLE TriggerDemo_Prod( ID INT IDENTITY (1,1) PRIMARY KEY, Emp_First_name VARCHAR (50), Emp_Last_name VARCHAR (50), Emp_Salary INT )GOCREATE TABLE TriggerDemo_ProdHistory ( ID 1, K IDRY INT) , ProdSalary INT, TS DATETIME, )GOСлед като и двете таблици бъдат създадени успешно, ще създадем тригера AFTER INSERT, UPDATE DML, който ще запише запис в таблицата с историята, ако в производствената таблица се вмъкне нов ред или съществуващ запис бъде променен с помощта на оператора CREATE TRIGGER T-SQL по-долу:
СЪЗДАДЕТЕ TRIGGER ProdHistoryON TriggerDemo_ProdAFTER INSERT, UPDATEASINSERT INTO TriggerDemo_ProdHistory VALUES ((ИЗБЕРЕТЕ ТОП 1 вмъкнат.ID FROM е вмъкнат),(ИЗБЕРЕТЕ ТОП 1 вмъкнат.Emp_Salary FROM вмъкнат), GETDATE())GOЗа да сравним регистрирането на промените с помощта на метода за задействане и клаузата OUTPUT, трябва да създадем две нови прости таблици, производствената и хронологията, със същата схема като предишните две таблици, но този път без да дефинираме тригер, използвайки CREATE TABLE T-SQL изрази по-долу:
CREATE TABLE OutputDemo_Prod( ID INT IDENTITY (1,1) PRIMARY KEY, Emp_First_name VARCHAR (50), Emp_Last_name VARCHAR (50), Emp_Salary INT )GOCREATE TABLE OutputDemo_ProdHistory ( ID 1, K IDRY INT) , ProdSalary INT, TS DATETIME, ) GOСега четирите маси са готови за тестване. Ще вмъкнем един запис в първата производствена таблица, която има тригер, използвайки оператора INSERT INTO T-SQL по-долу:
INSERT INTO TriggerDemo_Prod values('AA','BB', 750)GOСлед това ще вмъкнем същия запис във втората производствена таблица, използвайки клаузата OUTPUT. Инструкцията INSERT INTO по-долу ще действа като два оператора за вмъкване; първият ще вмъкне същия запис в производствената таблица, а вторият оператор за вмъкване до клаузата OUTPUT ще вмъкне регистрационния файл за вмъкване в таблицата с историята:
INSERT INTO OutputDemo_Prod OUTPUT inserted.ID, inserted.Emp_Salary, GETDATE() INTO OutputDemo_ProdHistory values('AA','BB', 750)GOПроверявайки данните, вмъкнати в четирите производствени и исторически таблици, ще видите, че и двата метода, методите за задействане и OUTPUT, ще запишат същия регистрационен файл в таблицата на историята успешно и по същия начин, както е показано в резултата по-долу:
От генерираните планове за изпълнение след изпълнение на двете заявки ще видите, че теглото на метода за задействане е около (21%+36%) 57% от общото тегло, където теглото на метода OUTPUT е около 43% , с малка разлика в теглото, както е показано в сравнението на плановете за изпълнение по-долу:
Разликата в производителността е ясна, когато се сравнява времето за изпълнение, изразходвано от всеки метод, където регистрирането на промените чрез метода за задействане ще отнеме (114+125) 239 мс да се изпълни напълно, а методът, който използва метода на клаузата OUTPUT, консумира само 5 мс , което е 2% от времето, използвано в метода за задействане, както е ясно показано от статистиката за времето по-долу:
От предишния резултат вече е ясно, че използването на метода OUTPUT е по-добро от използването на тригери за одит на промените.
Полезни връзки:
- СЪЗДАВАНЕ НА TRIGGER (Transact-SQL) https://docs.microsoft.com/en-us/sql/t-sql/statements/create-trigger-transact-sql
- DML тригери https://docs.microsoft.com/en-us/sql/relational-databases/triggers/dml-triggers
- DDL задействания https://docs.microsoft.com/en-us/sql/relational-databases/triggers/ddl-triggers
- Клауза OUTPUT (Transact-SQL) https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql