Tải bản đầy đủ (.pdf) (10 trang)

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

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.04 MB, 10 trang )

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

×