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

Microsoft SQL Server 2008 R2 Unleashed- P103 pps

10 107 0

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 241,66 KB

Nội dung

ptg 964 CHAPTER 30 Creating and Managing Triggers ROLLBACK TRAN RETURN END The following DELETE statement deletes the row for Customer 1, so all three rows for that customer in the orders table should be deleted by the trigger: DELETE customers WHERE customer_id = 1 Server: Msg 547, Level 16, State 1 The DELETE statement conflicted with COLUMN REFERENCE constraint ‘FK_orders_customers’. The conflict occurred in database ‘BigPubs2008’, table ‘orders’, column ‘customer_id’. The statement has been terminated. This result might not be what you expected. The FOREIGN KEY constraint here restricts the DELETE statement, so the trigger never fires. The trigger in this example is an AFTER trigger. Therefore, the trigger does not fire, and the cascading action never takes place. You have several options to get around this situation: . Remove the FOREIGN KEY constraint from orders to customers. . Disable the FOREIGN KEY constraint from orders to customers. . Keep the FOREIGN KEY constraint and perform all cascading actions in stored procedures. . Keep the FOREIGN KEY constraint and perform all cascading actions in the application. . Use an INSTEAD OF trigger in place of the AFTER trigger. . Use the new cascading referential integrity constraints. Listing 30.7 shows how you can disable the FOREIGN KEY constraint so that a cascading delete can occur. LISTING 30.7 Disabling the FOREIGN KEY Constraint to the customers Table So That a Cascading Delete Can Occur ALTER TABLE orders NOCHECK CONSTRAINT FK_orders_customers GO GO DELETE customers WHERE customer_id = 1 SELECT * FROM customers SELECT * FROM orders customer_id customer_name customer_comments Download from www.wowebook.com ptg 965 Using DML Triggers 2 Software Suppliers AB Elisabeth is contact. 3 Firmware Suppliers AB Mike is contact. order_id customer_id order_date In Listing 30.7, the cascading deletes occur via the trigger because the FOREIGN KEY constraint is disabled. Compared to a trigger for cascading deletes, a trigger for cascading updates is more complex and not as common. This issue is discussed in more detail in the next section. If you disable the FOREIGN KEY constraint, you have a potential integrity problem. If rows are inserted or updated in the orders table, there are no constraints to ensure that the customer number exists in the customer table. You can take care of this situation by using an INSERT and UPDATE trigger on the orders table (see Listing 30.8). The trigger in Listing 30.8 tests for the existence of a customer before the order is inserted or updated. LISTING 30.8 Handling a Restriction by Using a Trigger on the orders Table if exists (select * from sysobjects where id = object_id(‘dbo.ord_ins_upd_cust’) and sysstat & 0xf = 8) drop trigger dbo.ord_ins_upd_cust GO CREATE TRIGGER ord_ins_upd_cust ON orders FOR INSERT, UPDATE AS IF EXISTS (SELECT * FROM inserted WHERE customer_id NOT IN (SELECT customer_id FROM customers)) BEGIN RAISERROR(‘No customer with such customer number’, 16, 1) ROLLBACK TRAN RETURN END Cascading Updates A cascading update with a trigger is a bit tricky to achieve. Modifying a primary key, per definition, really involves deleting a row and inserting a new row. The problem is that you lose the connection between the old row and new row in the customers table. How do you know which changes to cascade to which rows? 30 Download from www.wowebook.com ptg 966 CHAPTER 30 Creating and Managing Triggers This situation is simpler if you can restrict the changes to one row (see Listing 30.9) because you have only one row in the deleted and inserted tables. You know the customer number before and after the modification. LISTING 30.9 A Cascading Update in a Trigger if exists (select * from sysobjects where id = object_id(‘dbo.cust_upd_orders’) and sysstat & 0xf = 8) drop trigger dbo.cust_upd_orders GO CREATE TRIGGER cust_upd_orders ON customers FOR UPDATE AS DECLARE @rows_affected int, @c_id_before int, @c_id_after int SELECT @rows_affected = @@ROWCOUNT IF @rows_affected = 0 RETURN No rows changed, exit trigger IF UPDATE(customer_id) BEGIN IF @rows_affected = 1 BEGIN SELECT @c_id_before = customer_id FROM deleted SELECT @c_id_after = customer_id FROM inserted UPDATE orders SET customer_id = @c_id_after WHERE customer_id = @c_id_before END ELSE BEGIN RAISERROR (‘Cannot update more than 1 row.’, 16, 1) ROLLBACK TRAN RETURN END END If several rows are updated, it’s not easy to know which order belongs to which customer. You can easily modify the trigger shown in Listing 30.9 to handle a situation in which several rows change to the same value; however, this is not allowed because of the primary key on the customers table. Instances in which several rows are modified and the primary key value is changed are rare, and you are not likely to encounter such situations. Download from www.wowebook.com ptg 967 Using DML Triggers NOTE The cascading FOREIGN KEY constraints are an excellent alternative to triggers, and they are efficient. If you choose not to use the cascading feature, you might still want to enjoy the simplicity of constraints. Then you need to handle cascading actions only in stored procedures or in client applications. Stored procedures are often a good choice because they essentially give application developers a function-based interface for modifications. If the implementation details (for example, the table structure or rules) change, client applications can be isolated from the changes, as long as the interfaces to the stored procedures stay the same. The question of how to handle a cascade is a matter of personal preference, however. Handling cascading updates in a client application or stored procedure is a chicken- and-egg situation: you cannot change the primary key table first because other tables reference it. You also cannot change the referencing table because no row exists in the primary key table with a corresponding value. The solution is to insert in the referenced table a new row that contains the new primary key value, change the referencing rows, and then delete the old row from the referenced table. INSTEAD OF Triggers SQL Server 2000 introduced a type of trigger called an INSTEAD OF trigger. This type of trigger extends SQL Server’s trigger capabilities and provides an alternative to the AFTER trigger that was heavily utilized in prior versions of SQL Server. The name of the trigger gives you some insight into how this trigger operates: this trigger performs its actions instead of the action that fired it. This is much different from the AFTER trigger, which performs its actions after the statement that caused it to fire has completed. This means you can have an INSTEAD OF update trigger on a table that success- fully completes but does not include the actual update to the table. The basic syntax for creating an INSTEAD OF trigger is as follows: CREATE TRIGGER trigger_name ON table_name INSTEAD OF { INSERT | UPDATE | DELETE } AS SQL statements Listing 30.10 shows how to create a trigger that prints a message stating the number of rows updated by an UPDATE statement. It then executes an UPDATE against the table that has the trigger on it. Finally, it selects the rows from the table for review. 30 Download from www.wowebook.com ptg 968 CHAPTER 30 Creating and Managing Triggers LISTING 30.10 A Simple INSTEAD OF Trigger if exists (select * from sysobjects where id = object_id(‘dbo.cust_upd_orders’) and sysstat & 0xf = 8) drop trigger dbo.cust_upd_orders GO CREATE TRIGGER trI_au_upd ON authors INSTEAD OF UPDATE AS PRINT ‘TRIGGER OUTPUT: ‘ +CONVERT(VARCHAR(5), @@ROWCOUNT) + ‘ rows were updated.’ GO UPDATE authors SET au_fname = ‘Rachael’ WHERE state = ‘UT’ GO TRIGGER OUTPUT: 1 rows were updated. SELECT au_fname, au_lname FROM authors WHERE state = ‘UT’ GO au_fname au_lname Johann Wolfgang von Goethe As you can see from the results of the SELECT statement, the first name (au_fname) column is not updated to ’Rachael’. The UPDATE statement is correct, but the INSTEAD OF trigger logic does not apply the update from the statement as part of its INSTEAD OF action. The only action the trigger carries out is to print its message. The important point to realize is that after you define an INSTEAD OF trigger on a table, you need to include all the logic in the trigger to perform the actual modification as well as any other actions that the trigger might need to carry out. Executing INSTEAD OF Triggers To gain a complete understanding of the INSTEAD OF trigger, you must understand its execution in relationship to the other events that are occurring. The following key events are important when the INSTEAD OF trigger fires: . Triggering action—The INSTEAD OF trigger fires instead of the triggering action. As shown earlier, the actions of the INSTEAD OF trigger replace the actions of the origi- nal data modification that fired the trigger. . Constraint processing—Constraint processing—including CHECK constraints, UNIQUE constraints, and PRIMARY KEY constraints—happens after the INSTEAD OF trigger fires. Download from www.wowebook.com ptg 969 Using DML Triggers Listing 30.11 demonstrates the trigger execution order. LISTING 30.11 INSTEAD OF Trigger Execution CREATE TRIGGER employee_insInstead ON employee INSTEAD OF insert AS DECLARE @job_id smallint Insert the jobs record for the employee if it does not already exist IF NOT EXISTS (SELECT 1 FROM jobs j, inserted i WHERE i.job_id = j.job_id) BEGIN INSERT jobs (job_desc, min_lvl, max_lvl) SELECT ‘Automatic Job Add’, i.job_lvl, i.job_lvl FROM inserted i Capture the identify value for the job just inserted This will be used for the employee insert later SELECT @job_id = @@identity PRINT ‘NEW job_id ADDED FOR NEW EMPLOYEE:’ + convert(char(3),@job_id) END Execute the original insert action with the newly added job_id INSERT employee (emp_id, fname, minit, lname, job_id, job_lvl, pub_id, hire_date) SELECT emp_id, fname, minit, lname, @job_id, job_lvl, pub_id, hire_date FROM Inserted GO The trigger in Listing 30.11 can be created in BigPubs2008. The key feature of this INSTEAD OF trigger is that it can satisfy a referential integrity constraint that was not satisfied before the INSERT was executed. Note the FOREIGN KEY constraint on the employee table that references job_id on the jobs table. The trigger first checks whether the jobs record asso- ciated with the job_id of the employee being inserted exists. If the jobs record does not 30 Download from www.wowebook.com ptg 970 CHAPTER 30 Creating and Managing Triggers exist for the inserted employee’s job_id, the trigger inserts a new jobs record and uses it for the insertion of the employee record. If you execute the following INSERT statement, which has a job_id that does not exist, it succeeds: INSERT EMPLOYEE (emp_id, fname, minit, lname, job_id, job_lvl, pub_id, hire_date) VALUES (‘KNN33333F’, ‘Kayla’, ‘N’, ‘Nicole’, 20, 100, 9952, getdate()) Go This statement succeeds because the constraint processing happens after the INSTEAD OF trigger completes its actions. Conversely, if you were to create the same trigger as an AFTER trigger, the FOREIGN KEY constraint would execute before the AFTER trigger, and the follow- ing error message would be displayed: INSERT statement conflicted with COLUMN FOREIGN KEY constraint ‘FK__employee__job_id__1BFD2C07’. The conflict occurred in database ‘BigPubs2008’, table ‘jobs’, column ‘job_id’. >The statement has been terminated. Notice with the previous INSTEAD OF trigger example that the last action the trigger performs is the actual insertion of the employee record. The trigger was created to fire when an employee was inserted, so the trigger must perform the actual insertion. This insertion occurs in addition to any other actions that justify the trigger’s creation. Using AFTER Versus INSTEAD OF Triggers Now that you have seen some of the key differences between AFTER and INSTEAD OF trig- gers, you need to decide which trigger to use. In the previous example (Listing 30.11), the INSTEAD OF trigger is the only trigger option for this kind of functionality. However, you can often use either trigger type to attain the same result. Something you should consider when choosing one of these triggers is the efficiency of the overall modification. For example, if you have a modification that will cause a trigger to fire and often reject the modification, you might want to consider using the INSTEAD OF trigger. The rationale is that the INSTEAD OF trigger does not perform the actual modifi- cation until after the trigger completes, so you do not need to undo the modification. If you were to use an AFTER trigger in the same scenario, any modifications that were rejected would need to be rolled back because they have already been written to the trans- action log by the time the AFTER trigger fires. Conversely, if you have a situation in which the vast majority of the updates are not rejected, the AFTER trigger might be your best choice. The particular situation dictates the preferred trigger, but you should keep in mind that INSTEAD OF triggers tend to be more involved than AFTER triggers because an INSTEAD OF trigger must perform the actual data modification that fired it. Download from www.wowebook.com ptg 971 Using DML Triggers Using AFTER and INSTEAD OF Triggers Together An important consideration when coding an INSTEAD OF trigger is that it can exist on the same table as an AFTER trigger. INSTEAD OF triggers can also execute based on the same data modifications as AFTER triggers. Consider, for example, the INSTEAD OF trigger from Listing 30.11 that you placed on the employee table in the BigPubs2008 database. An AFTER trigger already existed on the employee table. Listing 30.12 shows the code for the existing AFTER trigger on the employee table. LISTING 30.12 An AFTER Trigger Placed on the Same Table as an INSTEAD OF Trigger if exists (select * from sysobjects where id = object_id(‘dbo.employee_insupd’) and sysstat & 0xf = 8) drop trigger dbo.employee_insupd GO CREATE TRIGGER employee_insupd ON employee FOR INSERT, UPDATE AS Get the range of level for this job type from the jobs table. declare @min_lvl tinyint, @max_lvl tinyint, @emp_lvl tinyint, @job_id smallint select @min_lvl = min_lvl, @max_lvl = max_lvl, @emp_lvl = i.job_lvl, @job_id = i.job_id from employee e, jobs j, inserted i where e.emp_id = i.emp_id AND i.job_id = j.job_id IF (@job_id = 1) and (@emp_lvl <> 10) begin raiserror (‘Job id 1 expects the default level of 10.’,16,1) ROLLBACK TRANSACTION end ELSE IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl) begin raiserror (‘The level for job_id:%d should be between %d and %d.’, 16, 1, @job_id, @min_lvl, @max_lvl) ROLLBACK TRANSACTION End go 30 Download from www.wowebook.com ptg 972 CHAPTER 30 Creating and Managing Triggers This AFTER trigger checks whether the job level assigned to the employee falls within a valid range for the job_id assigned to the employee. It is fired for both insertions and updates, and it can exist on the same table as the employee_insInstead INSTEAD OF trigger described earlier. The combined effect on an employee insertion with both the trig- gers on the employee table is to have the following actions happen: 1. The INSERT data modification is executed. 2. The INSTEAD OF trigger fires, completes its validation, and ultimately does the employee insertion that is written to the transaction log. 3. Constraint processing completes. 4. The AFTER trigger fires, performing its actions on the employee record inserted by the INSTEAD OF trigger. 5. The AFTER trigger completes and commits the transaction to the database. One of the key points in this example is that the AFTER trigger performs its actions on the row inserted by the INSTEAD OF trigger. It does not use the record from the original INSERT that started the trigger execution. Therefore, in this chapter’s example, where the INSTEAD OF trigger generates a new job_id, the new job_id value—not the job_id that was origi- nally inserted—is used in the AFTER trigger. You need to consider rollback and recovery in this scenario as well, but they are beyond the scope of this discussion. This example simply shows that INSTEAD OF and AFTER trig- gers can be combined and that you need to consider the order of execution when design- ing a trigger solution. Using Views with INSTEAD OF Triggers One of the most powerful applications of an INSTEAD OF trigger is to a view. The INSTEAD OF trigger, unlike the AFTER trigger, can be applied to a view and triggered based on modi- fications to the view. For more information on views, see Chapter 27, “Creating and Managing Views.” The creation of INSTEAD OF triggers on views is important because data modifications against views have many restrictions. The list is extensive, but following are a few examples: . You cannot use data modification statements that apply to more than one table in the view in a single statement. . All columns defined as NOT NULL in the underlying tables that are being inserted must have the column values specified in the INSERT statement. . If the view was defined with the WITH CHECK OPTION clause, rows cannot be modi- fied in a way that will cause them to disappear from the view. You can use the INSTEAD OF trigger to overcome some of these restrictions. In particular, the first restriction (related to making a single table modification) can be addressed with the INSTEAD OF trigger. The INSTEAD OF trigger fires before the actual modification takes place, so it can resolve the modifications to the underlying tables associated with the view. It can then execute the modification directly against those base tables. The following example demonstrates this capability: Download from www.wowebook.com ptg 973 Using DML Triggers Use BigPubs2008 go CREATE VIEW employeeJobs AS select j.min_lvl, j.max_lvl, j.job_id, j.job_desc, e.job_lvl, e.emp_id from employee e, jobs j where e.job_id = j.job_id GO This example creates a view in the BigPubs2008 database that joins data from the employee and jobs tables. It retrieves the job types and the associated levels, the employ- ees assigned to the job types, and each employee’s current job level. Following is a sample set of rows from the view: min_lvl max_lvl job_id job_desc job_lvl emp_id 25 100 14 Designer 35 ENL44273F 25 100 14 Designer 89 PSA89086M 25 100 14 Designer 100 KFJ64308F 25 100 12 Editor 32 Y-L77953M 25 100 12 Editor 35 H-B39728F 25 100 12 Editor 100 HAS54740M Let’s say you want to change the minimum job level (min_lvl) for the Designer job to 40 and at the same time set the job level (job_lvl) for any employees who have this job to 40. If you execute the following update—without an INSTEAD OF trigger—against the view, you get the message shown: UPDATE employeeJobs SET min_lvl = 40, job_lvl = 40 WHERE job_id = 12 GO View or function ‘employeeJobs’ is not updateable because the modification affects multiple base tables. To get around this problem, you can use an INSTEAD OF trigger. The trigger can decipher the update to the view and apply the updates to the base table without causing the error. This functionality is demonstrated in the INSTEAD OF trigger shown in Listing 30.13. LISTING 30.13 A Basic View with an INSTEAD OF Trigg er CREATE TRIGGER employeeJobs_updInstead ON employeeJobs INSTEAD OF UPDATE AS IF @@ROWCOUNT = 0 RETURN 30 Download from www.wowebook.com . the referenced table. INSTEAD OF Triggers SQL Server 2000 introduced a type of trigger called an INSTEAD OF trigger. This type of trigger extends SQL Server s trigger capabilities and provides. customer_id = 1 Server: Msg 547, Level 16, State 1 The DELETE statement conflicted with COLUMN REFERENCE constraint ‘FK_orders_customers’. The conflict occurred in database ‘BigPubs2008’, table. provides an alternative to the AFTER trigger that was heavily utilized in prior versions of SQL Server. The name of the trigger gives you some insight into how this trigger operates: this trigger

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

TỪ KHÓA LIÊN QUAN