1. Trang chủ
  2. » Công Nghệ Thông Tin

Microsoft SQL Server 2008 R2 Unleashed- P102 potx

10 210 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Creating and Managing Triggers
Trường học University of XYZ
Chuyên ngành Computer Science
Thể loại Bài luận
Năm xuất bản 2010
Thành phố New York
Định dạng
Số trang 10
Dung lượng 234,13 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The basic syntax for creating an AFTER trigger is as follows: CREATE TRIGGER trigger_name ON table_name AFTER { INSERT | UPDATE | DELETE } AS SQL statements The AFTER trigger is the de

Trang 1

trigger and inclusion of the word AFTER in the name of the old trigger have helped

accen-tuate the behavior of the AFTER trigger: the AFTER trigger executes after a data modification

has taken place

NOTE

Throughout the rest of this chapter, if the trigger type is not specified, you can assume

that it is an AFTER trigger

The fact that an AFTER trigger fires after a data modification might seem to be a simple

concept, but it is critical to understanding how it works The AFTER trigger fires after the

data modification statement completes but before the statement’s work is committed to

the databases The statement’s work is captured in the transaction log but not committed

to the database until the trigger has executed and performed its actions

The trigger has the capability to roll back its actions as well as the actions of the

modifica-tion statement that invoked it This is possible because an implicit transacmodifica-tion exists that

includes both the modification statement and trigger it fires If the trigger does not issue a

rollback, an implicit COMMIT of all the work is issued when the trigger completes

The basic syntax for creating an AFTER trigger is as follows:

CREATE TRIGGER trigger_name

ON table_name

AFTER { INSERT | UPDATE | DELETE }

AS

SQL statements

The AFTER trigger is the default type of DML trigger, so the AFTER keyword is optional

Listing 30.1 shows the code you use to create a trigger in the BigPubs2008 database (You

can find instructions for creating the BigPubs2008 database in the introduction chapter at

the beginning of this book.) This new trigger prints a message, stating the number of rows

updated by an UPDATE statement You then execute a couple of UPDATE statements to see

whether the trigger works

LISTING 30.1 An Example of a Simple AFTER Trigger

CREATE TRIGGER tr_au_upd ON authors

AFTER UPDATE

AS

PRINT ‘TRIGGER OUTPUT: ‘ +CONVERT(VARCHAR(5), @@ROWCOUNT)

+ ‘ rows were updated.’

GO

UPDATE authors

SET au_fname = au_fname

Trang 2

WHERE state = ‘UT’

GO

TRIGGER OUTPUT: 1 rows were updated

UPDATE authors

SET au_fname = au_fname

WHERE state = ‘CA’

GO

TRIGGER OUTPUT: 37 rows were updated

Even though you do not actually change the contents of the au_fname column (because

you set it to itself), the trigger fires anyway Listing 30.1 does not show the typical use of a

trigger, but it gives you some insight into how and when a trigger fires The fact that the

trigger fires, regardless of what is updated, causes many developers to test the @@rowcount

value at the beginning of the trigger code If @@rowcount is equal to zero, the trigger can

return without executing the remainder of the trigger code This is a good tactic for

opti-mizing the performance of triggers

NOTE

Triggers are meant to guarantee the integrity of data Although you can return result sets

and messages in triggers, doing so is not recommended The programmers who write

applications that perform modifications on a table are probably not prepared to get

unexpected result sets or messages when they submit data modification statements

The exception is returning an error with the RAISERROR command If a trigger performs

ROLLBACK TRAN, it should also execute RAISERROR to communicate the failure to the

application

Executing AFTER Triggers

You know that the AFTER trigger fires when a data modification (such as an insertion, an

update, or a deletion) takes place What about the trigger’s execution in relationship to

other events, including the execution of constraints? The following events take place

before an AFTER trigger executes:

Constraint processing—This includes CHECK constraints, UNIQUE constraints, and

PRIMARY KEY constraints

Declarative referential actions—These actions are defined by FOREIGN KEY

constraints that ensure the proper relationships between tables This includes

cascad-ing FOREIGN KEY constraints

Triggering action—This data modification caused the trigger to fire The action

occurs before the trigger fires, but the results are not committed to the database until

the trigger completes

You need to consider this execution carefully when you design triggers For example, if

you have a constraint and trigger defined on the same column, any violations to the

Trang 3

constraint abort the statement, and the trigger execution does not occur For example, if

you have a foreign key constraint on a table that ensures referencial integrity and a trigger

that that does some validation on that same foreign key column then the trigger

valida-tion will only execute if the foreign key validavalida-tion is successful

Specifying Trigger Firing Order

You can create more than one trigger on a table for each data modification action In

other words, you can have multiple triggers responding to an INSERT, an UPDATE, or a

DELETE command This can be useful in certain situations, but it can generate confusion

because you might not know the order in which the triggers fire for the particular action

Some of the confusion has been alleviated by the fact that SQL Server 2008 allows you to

specify the first and last trigger that fire for a particular action If you have four triggers

responding to updates on a given table, you can set the order for two of the triggers (first

and last), but the order of the remaining two triggers remains unknown

The sp_settriggerorder procedure is the tool you use to set the trigger order This

proce-dure takes the trigger name, order value (FIRST, LAST, or NONE), and action (INSERT,

UPDATE, or DELETE) as parameters For example, you could use the following to set the

firing order on the trigger used in this chapter’s simple example:

sp_settriggerorder tr_au_upd, FIRST, ‘UPDATE’

The execution of this command sets the tr_au_upd trigger as the first trigger to fire when

an update happens to the table on which this trigger has been placed If an ALTER

state-ment is executed against the trigger after the trigger order has been defined, the firing

order is lost The sp_settriggerorder procedure must be run again to reestablish the

firing order

NOTE

It is recommended that you avoid defining multiple triggers for the same event on the

same table when possible Often, it is possible to include all the logic in one trigger

defined for an action This can simplify your database and avoid the uncertainty of the

firing order

Special Considerations with AFTER Triggers

Following are a few other considerations for AFTER triggers:

AFTER triggers can be used on tables that also have cascading referential integrity

constraints The cascading feature, which was new to SQL Server 2000, allows you to

define cascading actions when a user updates or deletes a primary key to which a

foreign key points This new feature is discussed in more detail in Chapter 24,

“Creating and Managing Tables.”

WRITETEXT and TRUNCATE TABLE do not fire triggers BCP, by default, does not fire

triggers either, but the FIRE_TRIGGERS bulk copy hint can be specified to cause both

AFTER and INSTEAD OF triggers to execute

Trang 4

Triggers are objects, so they must have unique names within the database If you try

to add a trigger with a name that already exists, you get an error message You can,

however, use ALTER on an existing trigger

The following restrictions apply to AFTER triggers:

AFTER triggers can be placed only on tables, not on views

A single AFTER trigger cannot be placed on more than one table

The text, ntext, and image columns cannot be referenced in the AFTER trigger logic

In most trigger situations, you need to know what changes were made as part of the data

modification You can find this information in the inserted and deleted tables For the

AFTER trigger, these temporary memory-resident tables contain the rows modified by the

statement With the INSTEAD OF trigger, the inserted and deleted tables are actually

temporary tables created on-the-fly

The inserted and deleted tables have identical column structures and names as the

tables that were modified Consider running the following statement against the

BigPubs2008 database:

UPDATE titles

SET price = $15.05

WHERE type LIKE ‘%cook%’

When this statement is executed, a copy of the rows to be modified is recorded, along

with a copy of the rows after the modification These copies are available to the trigger in

the deleted and inserted tables

If you want to be able to see the contents of the deleted and inserted tables for testing

purposes, you can create a copy of the table and then create a trigger on that copy (see

Listing 30.2) You can perform data modification statements and view the contents of

these tables without the modification actually taking place

LISTING 30.2 Viewing the Contents of the inserted and deleted Tables

Create a copy of the titles table in the BigPubs2008 database

SELECT *

INTO titles_copy

FROM titles

GO

add an AFTER trigger to this table for testing purposes

CREATE TRIGGER tc_tr ON titles_copy

FOR INSERT, UPDATE, DELETE

AS

PRINT ‘Inserted:’

SELECT title_id, type, price FROM inserted

Trang 5

PRINT ‘Deleted:’

SELECT title_id, type, price FROM deleted

ROLLBACK TRANSACTION

The inserted and deleted tables are available within the trigger after INSERT, UPDATE, and

DELETE Listing 30.3 shows the contents of inserted and deleted, as reported by the

trigger when executing the preceding UPDATE statement

LISTING 30.3 Viewing the Contents of the inserted and deleted Tables When Updating

the titles_copy Table

UPDATE titles_copy

SET price = $15.05

WHERE type LIKE ‘%cook%’

Inserted:

title_id type price

- -

-TC7777 trad_cook 15.05

TC4203 trad_cook 15.05

TC3218 trad_cook 15.05

MC3021 mod_cook 15.05

MC2222 mod_cook 15.05

Deleted:

title_id type price

- -

-TC7777 trad_cook 14.3279

TC4203 trad_cook 14.595

TC3218 trad_cook 0.0017

MC3021 mod_cook 15.894

MC2222 mod_cook 14.9532

NOTE

In SQL Server 2008, an error message is displayed after a rollback is initiated in a

trig-ger The error message indicates that the transaction ended in the trigger and that the

batch has been aborted Prior to SQL Server 2005, an error message was not

dis-played when a rollback was encountered in the trigger

The nature of the inserted and deleted tables enables you to determine the action that

fired the trigger For example, when an INSERT occurs, the deleted table is empty because

there were no previous values prior to the insertion Table 30.1 shows the DML triggering

events and the corresponding contents in the deleted and inserted tables

Trang 6

NOTE

Triggers do not fire on a row-by-row basis One common mistake in coding triggers is to

assume that only one row is modified However, triggers are set-based If a single

statement affects multiple rows in the table, the trigger needs to handle the processing

of all the rows that were affected, not just one row at a time

One common approach to dealing with the multiple rows in a trigger is to place the

rows in a cursor and then process each row that was affected, one at a time This

approach works, but it can have an adverse effect on the performance of the trigger To

keep your trigger execution fast, you should try to use rowset-based logic instead of

cursors in triggers when possible

Rowset-based logic will typically join to the inserted or deleted table that are available

to a trigger You can join these tables to other tables that are being manipulated by the

trigger For example, a trigger on a Job table can update a related employee table with

rowset-based logic such as :

UPDATE employee

SET employee.job_lvl = i.min_lvl

FROM inserted i

WHERE employee.emp_id = i.emp_id

This kind of logic will allow the trigger update to work correctly if one job record is

changed or many job rows are changed at once This is much more efficient than

load-ing all of the rows from the inserted table into a cursor which updates the employee

records one at a time within the cursor loop

Checking for Column Updates

The UPDATE() function is available inside INSERT and UPDATE triggers UPDATE() allows a

trigger to determine whether a column was affected by the INSERT or UPDATE statement

that fired the trigger By testing whether a column was actually updated, you can avoid

performing unnecessary work

For example, suppose a rule mandates that you cannot change the city for an author (a silly

rule, but it demonstrates a few key concepts) Listing 30.4 creates a trigger for both INSERT

and UPDATE that enforces this rule on the authors table in the BigPubs2008 database

TABLE 30.1 Determining the Action That Fired a Trigger

Statement Contents of inserted Contents of deleted

INSERT Rows added Empty

UPDATE New rows Old rows

Trang 7

LISTING 30.4 Using the UPDATE() Function in a Trigger

CREATE TRIGGER tr_au_ins_upd ON authors

FOR INSERT, UPDATE

AS

IF UPDATE(city)

BEGIN

RAISERROR (‘You cannot change the city.’, 15, 1)

ROLLBACK TRAN

END

GO

UPDATE authors

SET city = city

WHERE au_id = ‘172-32-1176’

Server: Msg 50000, Level 15, State 1, Procedure

tr_au_ins_upd, Line 5

You cannot change the city

Listing 30.4 shows how you generally write triggers that verify the integrity of data If the

modification violates an integrity rule, an error message is returned to the client

applica-tion, and the modification is rolled back

The UPDATE() function evaluates to TRUE if you update the column in the UPDATE

state-ment As shown in the preceding example, you do not have to change the value in the

column for the UPDATE() function to evaluate to TRUE, but the column must be referenced

in the UPDATE statement For example, with the author update, the city column was set it

to itself (the value does not change), but the UPDATE() function still evaluates to TRUE

NOTE

If you created the tr_au_upd trigger on the authors table as part of the AFTER trigger

example earlier in this chapter, you might have also seen the TRIGGER OUTPUT: 1

rows were updated message This trigger was set to be the first trigger to fire, and it

executes in addition to the new ins_upd trigger added in the example from this section

Now you can add a couple of INSERT statements on the authors table:

INSERT authors (au_id, au_lname, au_fname, city, contract)

VALUES(‘111-11-1111’, ‘White’, ‘Johnson’,’Menlo Park’, 1)

Results from the previous insert

Server: Msg 50000, Level 15, State 1

You cannot change the city

Trang 8

The UPDATE() function evaluates to TRUE and displays the error message This outcome is

expected because the trigger was created for INSERT as well, and the IF UPDATE condition

is evaluated for both insertions and updates

Now you can see what happens if you change the INSERT statement so that it does not

include the city column in the INSERT:

INSERT authors (au_id, au_lname, au_fname, contract)

VALUES(‘111-11-2222’, ‘White’, ‘Johnson’, 1)

Server: Msg 50000, Level 15, State 1

You cannot change the city

The error message is still displayed, even though the insertion was performed without the

city column This process might seem counterintuitive, but the IF UPDATE condition

always returns a TRUE value for INSERT actions The reason is that the columns have either

explicit default values or implicit (NULL) values inserted, even if they are not specified The

IF UPDATE conditions see this as a change and evaluate to TRUE

If you change the tr_au_ins_upd trigger to be for UPDATE only (not INSERT and UPDATE),

the insertions can take place without error

Enforcing Referential Integrity by Using DML Triggers

Several options, including foreign key constraints and stored procedures, are available to

enforce referential integrity, but using a trigger is still a viable alternative A trigger

provides a great deal of flexibility and allows you to customize your referential integrity

solution to fit your needs Some of the other alternatives, such as foreign keys, do not

provide the same degree of customization

TIP

In a database environment in which multiple databases are used with related data, a

trigger can be invaluable for enforcing referential integrity The trigger can span

data-bases, and it can ensure that data rows inserted into a table in one database are valid

based on rows in another database

Listing 30.5 shows how to re-create and populate the customers and orders tables in the

sample BigPubs2008 database

LISTING 30.5 Creating and Populating the customers and orders Tables

if exists (select * from sysobjects

where id = object_id(‘orders’) and sysstat & 0xf = 3)

drop table orders

GO

if exists (select * from sysobjects

Trang 9

where id = object_id(‘customers’) and sysstat & 0xf = 3)

drop table customers

GO

CREATE TABLE customers

(customer_id INT PRIMARY KEY NOT NULL,

customer_name NVARCHAR(25) NOT NULL,

customer_comments NVARCHAR(22) NULL)

CREATE TABLE orders

(order_id INT PRIMARY KEY NOT NULL,

customer_id INT,

order_date DATETIME,

CONSTRAINT FK_orders_customers

FOREIGN KEY (customer_id) REFERENCES customers (customer_id))

INSERT customers (customer_id, customer_name, customer_comments)

VALUES(1, ‘Hardware Suppliers AB’,’Stephanie is contact.’)

INSERT customers (customer_id, customer_name, customer_comments)

VALUES(2, ‘Software Suppliers AB’,’Elisabeth is contact.’)

INSERT customers (customer_id, customer_name, customer_comments)

VALUES(3, ‘Firmware Suppliers AB’,’Mike is contact.’)

INSERT orders (order_id, customer_id, order_date)

VALUES(100, 1, GETDATE())

INSERT orders (order_id, customer_id, order_date)

VALUES(101, 1, GETDATE())

INSERT orders (order_id, customer_id, order_date)

VALUES(102, 1, GETDATE())

SELECT * FROM customers

SELECT * FROM orders

customer_id customer_name customer_comments

- -

-1 Hardware Suppliers AB Stephanie is contact

2 Software Suppliers AB Elisabeth is contact

3 Firmware Suppliers AB Mike is contact

order_id customer_id order_date

- -

-100 1 2009-06-17 05:16:49.233

101 1 2009-06-17 05:16:49.233

102 1 2009-06-17 05:16:49.233

When foreign key constraints are used to enforce referencial integrity, they prohibit

several different types of changes to the data in the related tables These restrictions

Trang 10

ensure the that the integrity of the relationships is maintained For example, the FOREIGN

KEY constraint FK_orders_customers on the orders table prohibits the following:

Inserting rows into the orders table for customer numbers that don’t exist in the

customers table

Updating the orders table by changing the customer number to values that don’t

exist in the customers table

Deleting rows in the customers table for which orders exist

Updating the customers table by changing the customer number for which orders

exist

You might want a cascading action instead of prohibiting the deletion or update of rows

on the customers table This would include automatically cascading the DELETE or UPDATE

statement executed on the customers table to the related orders table You can do this by

using triggers

Cascading Deletes

A cascading delete is relatively simple to create Listing 30.6 shows a cascading delete

trigger for the customers table

TIP

SQL Server 2000 added a feature that allows you to define cascading actions on a

FOREIGN KEY constraint When defining the constraints on a table, you can use the ON

UPDATE CASCADE or ON DELETE CASCADE clause, which causes changes to the primary

key of a table to cascade to the related foreign key tables Refer to Chapter 26,

“Implementing Data Integrity,” for further information on this option

LISTING 30.6 A Cascading Delete for the customers Table

CREATE TRIGGER cust_del_orders ON customers

FOR DELETE

AS

IF @@ROWCOUNT = 0

RETURN

DELETE orders

FROM orders o , deleted d

WHERE o.customer_id = d.customer_id

IF @@ERROR <> 0

BEGIN

RAISERROR (‘ERROR encountered in cascading trigger.’, 16, 1)

Ngày đăng: 05/07/2014, 02:20

TỪ KHÓA LIÊN QUAN