Hướng dẫn học Microsoft SQL Server 2008 part 70 potx

10 295 0
Hướng dẫn học Microsoft SQL Server 2008 part 70 potx

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

Thông tin tài liệu

Nielsen c26.tex V4 - 07/23/2009 4:56pm Page 652 Part IV Developing with SQL Server The OBXKites database includes a simplified inventory system. To demonstrate transaction-aggregation handling, the following triggers implement the required rules. The first script creates a sample valid inventory item for test purposes: USE OBXKites; DECLARE @ProdID UniqueIdentifier, @LocationID UniqueIdentifier; SELECT @ProdID = ProductID FROM dbo.Product WHERE Code = 1001; SELECT @LocationID= LocationID FROM dbo.Location WHERE LocationCode = ‘CH’; INSERT dbo.Inventory (ProductID, InventoryCode, LocationID) VALUES (@ProdID,’A1’, @LocationID); SELECT P.Code, I.InventoryCode, I.QuantityOnHand FROM dbo.Inventory AS I INNER JOIN dbo.Product AS P ON I.ProductID = P.ProductID; Result: Code InventoryCode QuantityOnHand 1001 A1 0 The inventory-transaction trigger The inventory-transaction trigger performs the aggregate function of maintaining the current quantity- on-hand value in the Inventory table. With each row inserted into the InventoryTransaction table, the trigger updates the Inventory table. The JOIN between the Inserted image table and the Inventory table enables the trigger to handle multiple-row inserts: CREATE TRIGGER InvTrans_Aggregate ON dbo.InventoryTransaction AFTER Insert AS UPDATE dbo.Inventory SET QuantityOnHand += i.Value FROM dbo.Inventory AS Inv INNER JOIN Inserted AS i ON Inv.InventoryID = i.InventoryID; Return; 652 www.getcoolebook.com Nielsen c26.tex V4 - 07/23/2009 4:56pm Page 653 Creating DML Triggers 26 The next batch tests the InvTrans_Aggregate trigger by inserting a transaction and observing the InventoryTransaction and Inventory tables: INSERT InventoryTransaction (InventoryID, Value) SELECT InventoryID, 5 FROM dbo.Inventory WHERE InventoryCode = ‘A1’; INSERT InventoryTransaction (InventoryID, Value) SELECT InventoryID, -3 FROM dbo.Inventory WHERE InventoryCode = ‘A1’; INSERT InventoryTransaction (InventoryID, Value) SELECT InventoryID, 7 FROM dbo.Inventory WHERE InventoryCode = ‘A1’; The following query views the data within the InventoryTransaction table: SELECT i.InventoryCode, it.Value FROM dbo.InventoryTransaction AS it INNER JOIN dbo.Inventory AS i ON i.InventoryID = it.InventoryID; Result: InventoryCode Value A1 5 A1 -3 A1 7 The InvTrans_Aggregate trigger should have maintained a correct quantity-on-hand value through the inserts to the InventoryTransaction table. Indeed, the next query proves the trigger functioned correctly: SELECT p.Code, i.InventoryCode, i.QuantityOnHand FROM dbo.Inventory AS i INNER JOIN dbo.Product AS p ON i.ProductID = p.ProductID; Result: Code InventoryCode QuantityOnHand 1001 A1 9 653 www.getcoolebook.com Nielsen c26.tex V4 - 07/23/2009 4:56pm Page 654 Part IV Developing with SQL Server The inventory trigger The quantity values in the Inventory table should never be directly manipulated. Every quantity adjustment must go through the InventoryTransaction table. However, some users will want to make manual adjustments to the Inventory table. The gentlest solution to the problem is to use server-side code to perform the correct operations regardless of the user’s method: 1. An inventory instead of trigger must redirect direct updates intended for the Inventory table, converting them into inserts in the InventoryTransaction table, while permitting the InvTrans_Aggregate trigger to update the Inventory table. 2. The inserts into the InventoryTransaction table then update the Inventory table, leaving the correct audit trail of inventory transactions. As a best practice, the trigger must accept multiple-row updates. The goal is to undo the original DML UPDATE command while keeping enough of the data to write the change as an INSERT to the InventoryTransaction table: CREATE TRIGGER Inventory_Aggregate ON Inventory INSTEAD OF UPDATE AS Redirect direct updates If Update(QuantityOnHand) BEGIN; UPDATE dbo.Inventory SET QuantityOnHand = d.QuantityOnHand FROM Deleted AS d INNER JOIN dbo.Inventory AS i ON i.InventoryID = d.InventoryID; INSERT dbo.InventoryTransaction (Value, InventoryID) SELECT i.QuantityOnHand - Inv.QuantityOnHand, Inv.InventoryID FROM dbo.Inventory AS Inv INNER JOIN Inserted AS i ON Inv.InventoryID = i.InventoryID; END; To demonstrate the trigger, the following UPDATE attempts to change the quantity on hand from 9 to 10. The new Inventory_Aggregate trigger traps the UPDATE and resets the quantity on hand back to 9, but it also writes a transaction of +1 to the InventoryTransaction table. (If the InventoryTransaction table had transaction type and comment columns, then the trans- action would be recorded as a manual adjustment by user X.) The InventoryTransaction table’s InvTrans_Aggregate trigger sees the INSERT and properly adjusts the Inventory .QuantityOnHand to 10: Trigger Test 654 www.getcoolebook.com Nielsen c26.tex V4 - 07/23/2009 4:56pm Page 655 Creating DML Triggers 26 UPDATE dbo.Inventory SET QuantityOnHand = 10 WHERE InventoryCode = ‘A1’; Having performed the manual adjustment, the following query examines the InventoryTransaction table: SELECT i.InventoryCode, it.Value FROM dbo.InventoryTransaction AS it INNER JOIN dbo.Inventory AS i ON i.InventoryID = it.InventoryID; Sure enough, the manual adjustment of 1 has been written to the InventoryTransaction table: InventoryCode Value A1 5 A1 -3 A1 7 A1 1 As the adjustment was being inserted into the InventoryTransaction table, the InvTrans_ Aggregate trigger posted the transaction to the Inventory table. The following query double-checks the QuantityOnHand for inventory item ‘A1’: SELECT p.Code, i.InventoryCode, i.QuantityOnHand FROM dbo.Inventory AS i INNER JOIN dbo.Product AS p ON i.ProductID = p.ProductID; Result: Code InventoryCode QuantityOnHand 1001 A1 10 Summary Triggers are a key feature of client/server databases. It is the trigger that enables the developer to create complex custom business rules that are strongly enforced at the Database Engine level. SQL Server has two types of triggers: INSTEAD OF triggers and AFTER triggers. Key takeaways about triggers: ■ INSTEAD OF triggers cancel the firing DML statement and do something else instead of the original DML statement. 655 www.getcoolebook.com Nielsen c26.tex V4 - 07/23/2009 4:56pm Page 656 Part IV Developing with SQL Server ■ Triggers extend the lock duration, so try to place the code in the abstraction layer before it goes into the trigger. ■ Triggers fire once per DML statement, not once per row, so be certain the trigger is set-based and can handle multiple rows well. ■ Use the inserted and deleted virtual tables to access the data being modified by the DML statement. ■ Logic in triggers can quickly become a rat’s nest of code — ad hoc SQL firing a trigger, which calls a stored procedure, which updates two tables, which fires two triggers, one of which updates another table, and so on. This type of system is very expensive to maintain or refactor. The previous five chapters presented T-SQL programming and described how to package the code within stored procedures, user-defined functions, and triggers. The next chapter, ‘‘Creating DDL Triggers,’’ continues the discussion about triggers. 656 www.getcoolebook.com Nielsen c27.tex V4 - 07/23/2009 8:27pm Page 657 DDL Triggers IN THIS CHAPTER Creating DDL database triggers Preventing server or database changes DDL trigger scope Reading event data with XML Security triggers S ometimes the difference between an endless search to identify the problem and readily solving the problem is as simple as knowing what changed. DDL triggers are perfect for auditing server-level and database changes. Why devote a whole chapter to DDL triggers when the material only takes a few pages? Because I’m convinced every database, development, test, or production, shouldhaveaschemaauditDDLtrigger. A trigger is code that executes as the result of some action. DML triggers fire as the result of DML code — an INSERT, UPDATE, DELETE,orMERGE statement. DDL triggers fire as the result of some server-level or database schema–level event — typically data definition language (DDL) code — a CREATE, ALTER,or DROP statement. DML triggers respond to data changes, and DDL triggers respond to schema changes. Just like DML triggers, DDL triggers can execute T-SQL code and can rollback the event. DML triggers can see into the data transaction using the inserted and deleted tables. Because DDL triggers can respond to so many types of events and commands, the command that fired the trigger and other appropriate informa- tion about the event is passed to the trigger in XML using the EventData() function. 657 www.getcoolebook.com Nielsen c27.tex V4 - 07/23/2009 8:27pm Page 658 Part IV Developing with SQL Server What’s New with DDL Triggers I ntroduced in SQL Server 2005, DDL triggers were extended to include logon triggers with SQL Server 2005 SP2. For SQL Server 2008, DDL triggers are a bit more fleshed out and slightly easier to code: ■ More trappable events — especially system stored procedures that work like DDL code ■ EventData() XML schema is more readily available Policy-Based Management, new in SQL Server 2008, leverages DDL triggers to enforce policies, so you can be assured that DDL triggers reflect a technology that’s here to stay. M any of the reasons for using a DDL trigger can now be accomplished with more consistency across multiple servers using Policy-Based Management (PBM). For more about PBM see Chapter 40, ‘‘Policy-Based Management.’’ Chapter 54, ‘‘Schema Audit Triggers,’’ is a sister chapter to this one that shows how to implement DDL triggers to record every schema change to a database. Managing DDL Triggers DDL triggers are easy to manage because they are managed using normal DDL. The most significant factor when developing a DDL trigger is the scope of the trigger — deciding which server- or database-level events will fire a trigger. Creating and altering DDL triggers DDL triggers are created or altered using syntax similar to working with DML triggers. The location of the trigger, specified by the ON clause, is either ALL SERVER or DATABASE — literally, the term is DATABASE, not the name of the database. The following code creates a database-level DDL trigger: CREATE TRIGGER SchemaAudit ON DATABASE FOR DDL_Database_Level AS code Server-level events are a superset of database-level events. They include all database level events. The next example shows a server-level DDL trigger: CREATE TRIGGER SchemaAudit ON ALL SERVER 658 www.getcoolebook.com Nielsen c27.tex V4 - 07/23/2009 8:27pm Page 659 DDL Triggers 27 FOR DDL_Server_Level WITH Options AS code Using Management Studio, database triggers are listed under the database’s Programmability node in Object Explorer. Server triggers are listed under Server Objects in Object Explorer. Database triggers can be scripted using Object Explorer, but not modified as easily as other programmability objects such as stored procedures. The context menu for DDL triggers does not offer the ➪ modify, or ➪ script to alter options that a stored procedure’s context menu includes. To list the database DDL triggers using code, query the sys.triggers and sys.events catalog views. Server triggers are found at sys.server_triggers and sys.server_trigger_events. Trigger scope There are dozens of events that can potentially fire a DDL trigger — one for every DDL type of action that can be executed on the server or database. These events are categorized into a hierarchy using event groups. Creating a DDL trigger for an event group will cause the DDL trigger to fire for every event in that group. DDL triggers can be fired by individual events, such as create_table, create_login,or alter_view. This can be the perfect scope for trapping the specific event. There are 136 server/ database-level events, and 33 server-only level events. The full list of DDL events is listed in Books Online — search for ‘‘DDL Events.’’ With so many events, it’s sometimes (often) useful to develop a DDL trigger that spans multiple events. Fortunately, Microsoft has combined multiple similar events into a well-designed logical hierarchy of event groups. The ‘‘DDL Event Groups’’ page in Books Online clearly shows the whole hierarchy. The top, or root, of the hierarchy is the ddl_events group, which includes every possible event. The next level has two groups: ddl_server_level_events and ddl_database_level_events. Each of these groups includes several subgroups and events. Chances are good that when you need to write a DDL trigger, there’s a group that matches exactly with the types of events you want to handle. The following code creates three DDL triggers to demonstrate DDL trigger scope. The first DDL trigger handles all server-level events: CREATE TRIGGER DDL_Server_Level_Sample ON ALL SERVER FOR DDL_SERVER_LEVEL_EVENTS AS Set NoCount ON Print ‘DDL_Server_Level_Sample DDL Trigger’ The second DDL trigger fires for all database-level events: USE tempdb CREATE TRIGGER DDL_Database_Sample 659 www.getcoolebook.com Nielsen c27.tex V4 - 07/23/2009 8:27pm Page 660 Part IV Developing with SQL Server ON DATABASE FOR DDL_DATABASE_LEVEL_EVENTS AS Set NoCount ON Print ‘DDL_Database_Sample DDL Trigger’ The third DDL trigger traps only create table commands: CREATE TRIGGER DDL_Create_Table_Sample ON DATABASE FOR Create_Table AS Set NoCount ON Print ‘DDL_Create_Table_Sample DDL Trigger’ With these three DDL triggers installed, the next few DDL commands demonstrate DDL trigger scope. Creating a new database is a server-level event: Create database Testdb Result: DDL_Server_Level_Sample DDL Trigger Creating a new table will fire the create table DDL trigger as well as the general database DDL events trigger: create table Test (col1 INT) Result: DDL_Database_Sample DDL Trigger DDL_Create_Table_Sample DDL Trigger Dropping the table fires the general database DDL event trigger, but not the specific create table event trigger: drop table Test Result: DDL_Database_Sample DDL Trigger DDL triggers and security The DDL trigger creation options, ENCRYPTION and EXECUTE AS, both ensure the security of system- level auditing triggers. The following DDL trigger will be encrypted when stored: 660 www.getcoolebook.com Nielsen c27.tex V4 - 07/23/2009 8:27pm Page 661 DDL Triggers 27 CREATE TRIGGER DDL_DDL_Level_Sample ON ALL SERVER WITH ENCRYPTION FOR DDL_EVENTS AS code As with stored procedures, triggers can be executed under a different security context. Instead of the user who issued the DDL command that caused the DDL trigger to fire, the trigger can execute as one the following security contexts: ■ Caller: Executes as the person executing the DDL command that fires the DDL trigger ■ Self: Executes as the person who created the DDL trigger ■ ‘login_name’: Executes as a specific login Enabling and disabling DDL triggers DDL triggers can be turned on and off. This is good because DBAs need an easy way to disable DDL triggers that roll back any schema changes. The following code disables and then enables the DDL_Create_Table_Sample trigger: DISABLE TRIGGER DDL_Create_Table_Sample ON DATABASE; ENABLE TRIGGER DDL_Create_Table_Sample ON DATABASE; Removing DDL triggers Typically, removing an object in SQL Server requires nothing more than a DROP command. Because DDL triggers can exist on either the database or server level, slightly more code is required. Also, because of their dual lives (residing in either the database or server), DDL triggers aren’t listed in sys.objects, nor can their presence be detected using object_id(). DDL triggers are listed in sys.server_triggers and sys.triggers DMVs : IF EXISTS (SELECT * FROM sys.server_triggers WHERE Name = ‘DDL_Server_Level_Sample’) DROP TRIGGER DDL_Server_Level_Sample ON ALL SERVER IF EXISTS (SELECT * FROM sys.triggers WHERE Name = ‘DDL_Database_Sample’) DROP TRIGGER DDL_Database_Sample ON DATABASE 661 www.getcoolebook.com . 658 Part IV Developing with SQL Server What’s New with DDL Triggers I ntroduced in SQL Server 2005, DDL triggers were extended to include logon triggers with SQL Server 2005 SP2. For SQL Server 2008, . The first DDL trigger handles all server- level events: CREATE TRIGGER DDL _Server_ Level_Sample ON ALL SERVER FOR DDL _SERVER_ LEVEL_EVENTS AS Set NoCount ON Print ‘DDL _Server_ Level_Sample DDL Trigger’ The. sys.triggers DMVs : IF EXISTS (SELECT * FROM sys .server_ triggers WHERE Name = ‘DDL _Server_ Level_Sample’) DROP TRIGGER DDL _Server_ Level_Sample ON ALL SERVER IF EXISTS (SELECT * FROM sys.triggers WHERE

Ngày đăng: 04/07/2014, 09:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan