Multi-Statement Table-Valued Functions The multi-statement table-valued user-defined function combines the scalar function’s ability to contain complex code with the inline table-valued
Trang 1Part IV Developing with SQL Server
Anderson 01-016 2001-11-16 00:00:00.000 Outer Banks Lighthouses Andrews 01-015 2001-11-05 00:00:00.000 Amazon Trek
Andrews 01-012 2001-09-14 00:00:00.000 Gauley River Rafting Andrews 01-014 2001-10-03 00:00:00.000 Outer Banks Lighthouses Bettys 01-013 2001-09-15 00:00:00.000 Gauley River Rafting Bettys 01-015 2001-11-05 00:00:00.000 Amazon Trek
The second form, theOUTER APPLYcommand, operates much like a left outer join With this usage,
rows from the main query are included in the result set regardless of whether the virtual table returned
by the user-defined function is empty
Creating functions with schema binding
All three types of user-defined functions may be created with the significant added benefit of schema
binding Views may be schema bound; in this way, UDFs are like views — both can be schema bound
This is one reason why you might choose a UDF over a stored procedure, as stored procedures cannot
be schema bound Schema binding prevents the altering or dropping of any object on which the
func-tion depends If a schema-bound funcfunc-tion referencesTableA, then columns may be added toTableA,
but no existing columns can be altered or dropped, and neither can the table itself
To create a function with schema binding, add the option afterRETURNSand beforeASduring function
creation, as shown here:
CREATE FUNCTION FunctionName (Input Parameters) RETURNS DataType
WITH SCHEMA BINDING
AS BEGIN;
Code;
RETURNS Expression;
END;
Schema binding not only alerts the developer that the change will may affect an object, it prevents the
change To remove schema binding so that changes can be made,ALTERthe function so that schema
binding is no longer included
Multi-Statement Table-Valued Functions
The multi-statement table-valued user-defined function combines the scalar function’s ability to contain
complex code with the inline table-valued function’s ability to return a result set This type of function
creates a table variable and then populates it within code The table is then passed back from the
func-tion so that it may be used withinSELECTstatements
The primary benefit of the multi-statement table-valued user-defined function is that complex result sets
may be generated within code and then easily used with aSELECTstatement This enables you to build
complex logic into a query and solve problems that would otherwise be very difficult to solve without
a cursor
Trang 2TheAPPLYcommand may be used with multi-statement table-valued user-defined functions in the same
way that it’s used with inline user-defined functions
Creating a multi-statement table-valued function
The syntax to create the multi-statement table-valued function is very similar to that of the scalar
user-defined function:
CREATE FUNCTION FunctionName (InputParamenters)
RETURNS @TableName TABLE (Columns)
AS
BEGIN;
Code to populate table variable
RETURN;
END;
The following process builds a multi-statement table-valued user-defined function that returns a basic
result set:
1 The function first creates a table variable called@Pricewithin theCREATE FUNCTION
header
2 Within the body of the function, twoINSERTstatements populate the@Pricetable variable
3 When the function completes execution, the@Pricetable variable is passed back as the
output of the function
TheftPriceAvgfunction returns every price in thePricetable and the average price for each
product:
USE OBXKite;
go
CREATE FUNCTION ftPriceAvg()
RETURNS @Price TABLE
(Code CHAR(10),
EffectiveDate DATETIME,
Price MONEY)
AS
BEGIN;
INSERT @Price (Code, EffectiveDate, Price)
SELECT Code, EffectiveDate, Price
FROM Product
JOIN Price
ON Price.ProductID = Product.ProductID;
INSERT @Price (Code, EffectiveDate, Price)
SELECT Code, Null, Avg(Price)
FROM Product
Trang 3Part IV Developing with SQL Server
GROUP BY Code;
RETURN;
END;
Calling the function
To execute the function, refer to it within theFROMportion of aSELECTstatement The following code
retrieves the result from theftPriceAvgfunction:
SELECT *
FROM dbo.ftPriceAvg();
Result:
- -
-1001 2001-05-01 00:00:00.000 14.9500
1001 2002-06-01 00:00:00.000 15.9500
1001 2002-07-20 00:00:00.000 17.9500
Multi-statement table-valued user-defined functions use tempdb to pass the table variable
to the calling query For many applications this is not a concern, but for high-transaction applications I recommend focusing on the performance of the UDF and, if possible, incorporating the
code directly into the calling stored procedure.
Summary
User-defined functions expand the capabilities of SQL Server objects and open a world of flexibility
within expressions and theSELECTstatement
The big ideas from this chapter:
■ Scalar user-defined functions return a single value and must be deterministic
■ Inline table-valued user-defined functions are very similar to views, and return the results of a singleSELECTstatement
■ Multi-statement table-valued user-defined functions use code to populate a table variable, which is then returned
■ TheAPPLYfunction can be used to pass data to an inline table-valued UDF or a multi-statement table-valued UDF from the outer query, similar to how a correlated subquery can receive data from the outer query
T-SQL code can be packaged in stored procedures, user-defined functions, and triggers The next
chapter delves into triggers, specialized T-SQL procedures that fire in response to table-level events
Trang 4Creating DML Triggers
IN THIS CHAPTER
Creating instead of and after triggers
Using the transaction’s data within the trigger
Integrating multiple triggers Building a delete trigger to enforce a single-row delete
Triggers are special stored procedures attached to table events They can’t
be directly executed; they fire only in response to anINSERT,UPDATE,
orDELETEevent on a table In the same way that attaching code to a
form or control event in Visual Basic or Access causes that code to execute on
the form or control event, triggers fire on table events Users can’t bypass a
trig-ger; and unless the trigger sends a message to the client, the end-user is unaware
of the trigger
Developing triggers involves several SQL Server topics Understanding transaction
flow and locking, T-SQL, and stored procedures is a prerequisite for developing
smooth triggers Triggers contain a few unique elements and require careful
plan-ning, but they provide rock-solid execution of complex business rules and data
validation
Trigger Basics
SQL Server triggers fire once per data-modification operation, not once per
affected row This is different from Oracle, which can fire a trigger once
per operation, or once per row While this may seem at first glance to be a
limitation, being forced to develop set-based triggers actually helps ensure clean
logic and fast performance
Triggers may be created for the three table events that correspond to the three
data-modification commands:INSERT,UPDATE, andDELETE
Trang 5Part IV Developing with SQL Server
Best Practice
Triggers extend the duration of a transaction, which can lead to locking and blocking problems for
high-transaction systems For data integrity, sometimes a trigger is the best solution, but be aware of the
potential performance impact If the processing can be performed in the abstraction layer with 100 percent
certainty, then I’d rather see the code there than in a trigger If the abstraction layer isn’t enforced
100 percent of the time, then the code must exist in a trigger
SQL Server has two kinds of transaction triggers: instead of triggers and after triggers They differ in their
purpose, timing, and effect, as detailed in Table 26-1
Database triggers fire on data definition language (DDL) commands — CREATE, ALTER, DROP — and are useful for auditing server or database schema changes For more details, see Chapter 27, ‘‘Creating DDL Triggers.’’
TABLE 26-1
Trigger Type Comparison
Instead of Trigger After Trigger
DML statement Simulated but not
executed
Executed, but can be rolled back in the trigger
Timing Before PK and FK
constraints
After the transaction is complete, but before it is committed
Number possible per table
event
May be applied to views? Yes No
Nested? Depends on server
option
Depends on server option
Recursive? No Depends on database option
Transaction flow
Developing triggers requires understanding the overall flow of the transaction; otherwise, conflicts
between constraints and triggers can cause designing and debugging nightmares
Every transaction moves through the various checks and code in the following order:
1. IDENTITY INSERTcheck
2 Nullability constraint
Trang 63 Data-type check
4. INSTEAD OFtrigger execution If anINSTEAD OFtrigger exists, then execution of the DML
stops here.INSTEAD OFtriggers are not recursive Therefore, if theINSERTtrigger executes
another DML command, then theINSTEAD OFtrigger will be ignored the second time around
(recursive triggers are covered later in this chapter)
5 Primary-key constraint
6 Check constraints
7 Foreign-key constraint
8 DML execution and update to the transaction log
9. AFTERtrigger execution
10 Commit transaction (for more details on commits, see Chapter 66, ‘‘Managing Transactions,
Locking, and Blocking’’)
Based on SQL Server’s transaction flow, note a few key points about developing triggers:
■ AnAFTERtrigger occurs after all constraints Because of this, it can’t correct data, so the data
must pass any constraint checks, including foreign-key constraint checks
■ AnINSTEAD OFtrigger can circumvent foreign-key problems, but not nullability, data-type, or
identity-column problems
■ AnAFTERtrigger can assume that the data has passed all the other built-in data-integrity
checks
■ TheAFTERtrigger occurs before the DML transaction is committed, so it can roll back the
transaction if the data is unacceptable
Creating triggers
Triggers are created and modified with the standard DDL commands,CREATE,ALTER, andDROP, as
follows:
CREATE TRIGGER Schema.TriggerName ON Schema.TableName
AFTER | INSTEAD OF [Insert, Update, (and or) Delete]
AS
Trigger Code;
The trigger can be fired for any combination of insert, update, or delete events
Prior to SQL Server 2000, SQL Server hadAFTERtriggers only Because no distinction betweenAFTER
andINSTEAD OFwas necessary, the old syntax created the triggerFOR INSERT,UPDATE, orDELETE
To ensure that the oldFORtriggers will still work,AFTERtriggers can be created by using the keyword
FORin place ofAFTER
Although I strongly recommend that triggers be created and altered using scripts and version
control, you can view and modify triggers using Management Studio’s Object Explorer, as shown in
Trang 7Part IV Developing with SQL Server
FIGURE 26-1
Object Explorer will list all triggers for any table and may be used to modify the trigger using the
context menu
After triggers
A table may have severalAFTERtriggers for each of the three table events.AFTERtriggers may be
applied to tables only, not to views
The traditional trigger is anAFTERtrigger that fires after the modification implied by the statement is
complete, but before the statement ends and before the transaction is committed.AFTERtriggers are
useful for the following:
■ Complex data validation
■ Enforcing complex business rules
■ Writing data-audit trails
■ Maintaining modified date columns
■ Enforcing custom referential-integrity checks and cascading deletes
Trang 8Best Practice
When planning triggers, consider the most likely path If the trigger verifies data that will nearly always
be accepted, then an AFTER trigger is the best route That’s because the work is completed and the
trigger is merely a check
For inserts, updates, or deletes that are rarely accepted, use an INSTEAD OF trigger, which doesn’t actually
perform the DML statement’s work prior to the trigger’s execution
When you are learning a new programming language, the first program you write is traditionally a ‘‘hello
world’’ application that does nothing more than compile the program and prove that it runs by printing
‘‘hello world.’’ The followingAFTERtrigger simply prints‘In the After Trigger’when the trigger
is executed:
USE Family;
CREATE TRIGGER dbo.TriggerOne ON dbo.Person
AFTER INSERT
AS
PRINT ‘In the After Trigger’;
With theAFTERtrigger enforced, the following code inserts a sample row:
INSERT dbo.Person(PersonID, LastName, FirstName, Gender)
VALUES (50, ‘Ebob’, ‘Bill’,‘M’);
Result:
In the After Trigger
(1 row(s) affected)
TheINSERTworked and the trigger printed its own version of the ‘‘hello world’’ message
Instead of triggers
INSTEAD OFtriggers execute ‘‘instead of’’ (as a substitute for) the submitted transaction, so that the
sub-mitted transaction does not occur It’s as if the presence of anINSTEAD OFtrigger signals the submitted
transaction to be ignored by SQL Server
As a substitution procedure, each table is limited to only oneINSTEAD OFtrigger per table event In
addition,INSTEAD OFtriggers may be applied to views as well as tables
Don’t confuseINSTEAD OFtriggers withBEFOREtriggers or before update events They’re not the
Trang 9Part IV Developing with SQL Server
INSTEAD OFtriggers are useful when it’s known that the DML statement firing the trigger will always
be rolled back and some other logic will be executed instead of the DML statement For example:
■ When the DML statement attempts to update a non-updatable view, theINSTEAD OFtrigger updates the underlying tables instead
■ When the DML statement attempts to directly update an inventory table, anINSTEAD OF trigger updates the inventory transaction table instead
■ When the DML statement attempts to delete a row, anINSTEAD OFtrigger moves the row to
an archive table instead
The following code creates a testINSTEAD OFtrigger and then attempts toINSERTa row:
CREATE TRIGGER dbo.TriggerTwo ON dbo.Person
INSTEAD OF INSERT
AS
PRINT ‘In the Instead of Trigger’;
go INSERT dbo.Person(PersonID, LastName, FirstName, Gender) VALUES (51, ‘Ebob’, ‘’,‘M’);
Result:
In the Instead of Trigger
(1 row(s) affected) The result includes theINSTEAD OFtrigger’s ‘‘hello world’’ declaration and a report that one row was
affected However, selectingpersonID 51will prove that no rows were in fact inserted:
SELECT LastName FROM dbo.Person WHERE PersonID = 51;
Result:
LastName -(0 row(s) affected) TheINSERTstatement worked as if one row were affected, although the effect of theINSERTstatement
was blocked by theINSTEAD OFtrigger ThePRINTcommand was executed instead of the rows being
inserted In addition, theAFTERtrigger is still in effect, but itsPRINTmessage failed to print
Trigger limitations
Given their nature (code attached to tables), triggers have a few limitations The following SQL
commands are not permitted within a trigger:
Trang 10■ CREATE,ALTER, orDROPdatabase
■ RECONFIGURE
■ RESTOREdatabase or log
■ DISK RESIZE
■ DISK INIT
Disabling triggers
A user’s DML statement can never bypass a trigger, but a system administrator can temporarily
disable it, which is better than dropping it and then recreating it if the trigger gets in the way of a
data-modification task
Disabling a trigger can only be done for the entire database, not just for the current
connection or the current user; this makes disabling a trigger an extremely dangerous
instrument Think twice before making any attempt to bypass an instrument that is used to guard data
integrity!
To temporarily turn off a trigger, use theALTER TABLEDDL command with theENABLE TRIGGERor
DISABLE TRIGGERoption:
ALTER TABLE schema.TableName ENABLE or DISABLE TRIGGER
schema.TriggerName;
For example, the following code disables theINSTEAD OFtrigger (TriggerOneon thePerson
table):
ALTER TABLE dbo.Person
DISABLE TRIGGER TriggerOne;
To view the enabled status of a trigger, use theOBJECTPROPERTY()function, passing to it the object
ID of the trigger and theExecIsTriggerDisabledoption:
SELECT OBJECTPROPERTY(
OBJECT_ID(’TriggerOne’),’ExecIsTriggerDisabled’);
Listing triggers
Because triggers tend to hide in the table structure, the following query lists all the triggers in the
database based on thesys.triggerscatalog view:
SELECT Sc.name + ‘.’ + Ob.name as [table],
Tr.Name as [trigger],
CASE (Tr.is_instead_of_trigger )
WHEN 0 THEN ‘after’
WHEN 1 THEN ‘instead of’
END AS type,
CASE (Tr.is_disabled)