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

Hướng dẫn học Microsoft SQL Server 2008 part 67 pptx

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.13 MB, 10 trang )

Nielsen c24.tex V4 - 07/23/2009 4:53pm Page 622
Part IV Developing with SQL Server
FIGURE 24-2
The path and scope of return methods differ among the five possible methods of returning data.
Client App
Proc A
Select
Raiserror
from A
#temp
table
(insert…exec)(try…catch)
Proc B
DML
Select
exec
exec
Result set from A
Result set from B
Raiserror
from B
Output
Parameters
Return
Return
Result set
from B
Raiserror
Output Parameters
Best Practice
W


ith every returned record set, SQL Server will, by default, also send a message stating the number of
rows affected or returned. Not only is this a nuisance, but I have found in my informal testing that it
can slow a query by up to 17 percent depending on the que ry’s complexity.
Therefore, get into the habit of beginning every stored procedure with the following code:
CREATE PROC MyProc
AS
SET NOCOUNT ON;
Summary
Using stored procedures is a way to save and optimize batches. Stored procedures are compiled and
stored in memory the first time they are executed. No method is faster at executing SQL commands, or
more popular for moving the processing close to the data. Like a batch, a stored procedure can return a
record set by simply executing a
SELECT command.
The next chapter covers user-defined functions, which combine the benefits of stored procedures with
the benefits of views at the cost of portability.
622
www.getcoolebook.com
Nielsen c25.tex V4 - 07/23/2009 4:55pm Page 623
Building User-Defined
Functions
IN THIS CHAPTER
Creating scalar functions
Replacing views with inline
table-valued functions
Using complex code within
multi-statement table-valued
functions to generate a result
set
S
QL Server 2000 introduced user-defined functions (UDFs), and the SQL

Server community was initially slow to adopt them. Nevertheless, UDFs
were my personal favorite new feature in SQL Server 2000, and I still use
them frequently.
The community discovered that UDFs can be used to embed complex T-SQL
logic within a query, and problems that were impossible or required cursors
could now be solved with UDFs. The result is that UDFs have become a favorite
tool in the toolbox of any serious SQL Server database developer.
The benefits of UDFs can be easily listed:
■ UDFs can be used to embed complex logic within a query. This is huge.
I’ve solved several nasty problems using user-defined functions.
■ UDFs can be used to create new functions for complex e xpressions.
■ UDFs offer the benefits of views because they can be used within
the
FROM clause of a SELECT statement or an expression, and they
can be schema-bound. In addition, user-defined functions can accept
parameters, whereas views cannot.
■ UDFs offer the benefits of stored procedures because they are compiled
and optimized in the same way.
The chief argument against developing with user-defined functions has to do with
potential performance issues if they’re misused. Any function, user-defined or
system that must be executed for every row in a
WHERE clause will cripple
performance (see the sidebar on algebra in Chapter 8, ‘‘Introducing Basic
Query Flow.’’)
User-defined functions come i n three distinct types (as shown in Figure 25-1.)
Management Studio groups inline table-valued functions with multi-statement
table-valued functions:
■ Scalar functions that return a single value
623
www.getcoolebook.com

Nielsen c25.tex V4 - 07/23/2009 4:55pm Page 624
Part IV Developing with SQL Server
■ Inline table-valued functions similar to views
■ Multi-statement table-valued functions that build a result set with code
New in 2008
U
ser-defined functions haven’t changed much since they were introduced in SQL Server 2000. If you’re
upgrading to SQL Server 2008 directly from SQL Server 2000, then it’s worth knowing that the APPLY
keyword, covered later in this chapter, was added in SQL Server 2005.
Microsoft system functions are changing. When they were introduced in SQL Server 2000, system functions
were all prefixed with a double colon, such as ::fn_SystemFunction. The double colon has been
deprecated and will be disabled in a future version of SQL Server. Instead, system functions are now in the
sys. schema — for example, sys.fn_SystemFunction.
FIGURE 25-1
Management Studio’s Object Explorer lists all the user-defined functions within a database, organized
by table-valued and scalar-valued.
624
www.getcoolebook.com
Nielsen c25.tex V4 - 07/23/2009 4:55pm Page 625
Building User-Defined Functions 25
Andrew Novick, who regularly presents about UDFs at user groups and SQL conferences,
has compiled a great resource of sample UDFs. His website is
www.novicksoftware.com.
Scalar Functions
A scalar function is one that returns a single specific value. The function can accept multiple parameters,
perform a calculation, and then return a single value. For example, a scalar function could accept three
parameters, perform a calculation, and return the answer.
Within the code of a scalar function, the value is passed back through the function by means of a
RETURN command. Every possible codepath in the user-defined function should conclude with
a

RETURN command.
Scalar user-defined functions may be used within any expressions in SQL Server, even expres-
sions within check constraints (although I don’t recommend scalar function within check
constraints because that extends the duration of the transaction and it is difficult to locate and
maintain later).
Limitations
The scalar function must be deterministic, meaning it must repeatedly return the same value for
the same input parameters. For this reason, nondeterministic functions — such as
newid() and
rand() — are not allowed within scalar functions. Writing a UDF to return a random row is out of the
question.
User-defined scalar functions are not permitted to update the database or call
DBCC commands, with
the single exception that they may update table variables. They cannot return BLOB (binary large
object) data such as
text, ntext, timestamp,andimage data-type variables, nor can they return
table variables or
cursor data types. For error handling, UDFs may not include TRY CATCH or
RAISERROR.
A user-defined function may call other user-defined functions nesting up to 32 levels deep, or it can call
itself recursively up to 32 levels deep before it blows up.
Creating a scalar function
User-defined functions are created, altered, or dropped with the same DDL commands used for other
objects, although the syntax is slightly different to allow for the return value:
CREATE FUNCTION FunctionName (InputParameters)
RETURNS DataType
AS
BEGIN;
Code;
RETURN Expression;

END;
625
www.getcoolebook.com
Nielsen c25.tex V4 - 07/23/2009 4:55pm Page 626
Part IV Developing with SQL Server
The input parameters include a data-type definition and may optionally include a default value similar
to stored procedure parameters (
parameter = default). Function parameters differ from stored
procedure parameters in that even if the default is desired, the parameter is still required to call the
function. Parameters with defaults don’t become optional parameters. To request the default when
calling the function, pass the keyword
DEFAULT to the function.
The following user-defined scalar function performs a simple mathematical function. The second param-
eter includes a default value:
CREATE FUNCTION dbo.fsMultiply (@A INT, @B INT =3)
RETURNS INT
AS
BEGIN;
RETURN @A * @B;
END;
go
SELECT dbo.fsMultiply (3,4),
dbo.fsMultiply (7, DEFAULT);
Result:

12 21
While I’m not a stickler for naming conventions (as long as they’re consistent), I can under-
stand the practice of prefacing user-defined functions with an
f.
For a more complex scalar user-defined function, the fGetPrice function from the OBXKites sample

database returns a single result via an output parameter. It’s just a variation of the
pGetPrice stored
procedure. Both the stored procedure and function determine the correct price for any given date and
for any customer discount. Because the task returns a single value, calculating the price is a prime candi-
date for a scalar user-defined function. As a function, it can be plugged into any query, whereas a stored
procedure is more difficult to use as a building block in other code.
The function uses the same internal code as the stored procedure, except that the
@CurrPrice is
passed back through the final
RETURN instead of an output variable. The function uses a default value
of
NULL for the contact code. Here is the code for the fsGetPrice user-defined scalar function:
CREATE FUNCTION fsGetPrice (
@Code CHAR(10),
@PriceDate DATETIME,
@ContactCode CHAR(15) = NULL)
RETURNS MONEY
AS
BEGIN;
DECLARE @CurrPrice MONEY ;
DECLARE @DiscountPercent NUMERIC (4,2);
set the discount percent
626
www.getcoolebook.com
Nielsen c25.tex V4 - 07/23/2009 4:55pm Page 627
Building User-Defined Functions 25
if no customer lookup then it’s zilch discount
SELECT @DiscountPercent = CustomerType.DiscountPercent
FROM dbo.Contact
JOIN dbo.CustomerType

ON contact.CustomerTypeID =
CustomerType.CustomerTypeID
WHERE ContactCode = @ContactCode;
IF @DiscountPercent IS NULL
SET @DiscountPercent = 0;
SELECT @CurrPrice = Price * (1-@DiscountPercent)
FROM dbo.Price
JOIN dbo.Product
ON Price.ProductID = Product.ProductID
WHERE Code = @Code
AND EffectiveDate =
(SELECT MAX(EffectiveDate)
FROM dbo.Price
JOIN dbo.Product
ON Price.ProductID = Product.ProductID
WHERE Code = @Code
AND EffectiveDate <= @PriceDate);
RETURN @CurrPrice;
END;
Calling a scalar function
Scalar functions may be used anywhere within any expression that accepts a single value. User-
defined scalar functions must always be called by means of at least a two-part name (owner.name). The
following script demonstrates calling the
fGetPrice() function within OBXKites:
USE OBXKites;
SELECT dbo.fsGetPrice(’1006’,CURRENT_TIMESTAMP,DEFAULT),
dbo.fsGetPrice(’1001’,’5/1/2001’,NULL);
Result:

125.9500 14.9500

The user-defined scalar function dbo.fTitleCase is created in Chapter 9, ‘‘Data Types,
Expressions, and Scalar Functions,’’ and is available on
www.sqlserverbible.com.
Inline Table-Valued Functions
The second type of user-defined function, the inline table-valued function, is very similar to a view. Both
are wrapped for a stored
SELECT statement. An inline table-valued user-defined function retains the
benefits of a view, and adds parameters. As with a view, if the
SELECT statement is updateable, then
the function will be updateable.
627
www.getcoolebook.com
Nielsen c25.tex V4 - 07/23/2009 4:55pm Page 628
Part IV Developing with SQL Server
Creating an inline table-valued function
The inline table-valued user-defined function has no BEGIN/END body. Instead, the SELECT statement
is returned as a virtual table:
CREATE FUNCTION FunctionName (InputParameters)
RETURNS Table
AS
RETURN (Select Statement);
The following inline table-valued user-defined function is functionally equivalent to the vEventList
view created in Chapter 14, ‘‘Projecting Data Through Views.’’
USE CHA2;
go
CREATE FUNCTION ftEventList ()
RETURNS Table
AS
RETURN(
SELECT dbo.CustomerType.Name AS Customer,

dbo.Customer.LastName, dbo.Customer.FirstName,
dbo.Customer.Nickname,
dbo.Event_mm_Customer.ConfirmDate, dbo.Event.Code,
dbo.Event.DateBegin, dbo.Tour.Name AS Tour,
dbo.BaseCamp.Name, dbo.Event.Comment
FROM dbo.Tour
INNER JOIN dbo.Event
ON dbo.Tour.TourID = dbo.Event.TourID
INNER JOIN dbo.Event_mm_Customer
ON dbo.Event.EventID = dbo.Event_mm_Customer.EventID
INNER JOIN dbo.Customer
ON dbo.Event_mm_Customer.CustomerID
= dbo.Customer.CustomerID
LEFT OUTER JOIN dbo.CustomerType
ON dbo.Customer.CustomerTypeID
= dbo.CustomerType.CustomerTypeID
INNER JOIN dbo.BaseCamp
ON dbo.Tour.BaseCampID = dbo.BaseCamp.BaseCampID);
Calling an inline table-valued function
To retrieve data through ftEventList, call the function within the FROM portion of a SELECT
statement:
SELECT LastName, Code, DateBegin
FROM dbo.ftEventList();
Result (abridged):
628
www.getcoolebook.com
Nielsen c25.tex V4 - 07/23/2009 4:55pm Page 629
Building User-Defined Functions 25
LastName Code DateBegin


Anderson 01-003 2001-03-16 00:00:00.000
Brown 01-003 2001-03-16 00:00:00.000
Frank 01-003 2001-03-16 00:00:00.000

Using parameters
An advantage of inline table-valued f unctions over views is the function’s ability to include parameters
within the pre-compiled
SELECT statement. Views, conversely, do not include parameters, and restrict-
ing the result at runtime is typically achieved by adding a
WHERE clause to the SELECT statement that
calls the view.
The following examples compare adding a restriction to the view to using a function parameter. The
following view returns the current price list for all products:
USE OBXKites;
go
CREATE VIEW vPricelist
AS
SELECT P.Code, Price.Price
FROM dbo.Price
JOIN dbo.Product P
ON Price.ProductID = P.ProductID
WHERE EffectiveDate =
(SELECT MAX(EffectiveDate)
FROM dbo.Price
WHERE ProductID = P.ProductID
AND EffectiveDate <= CURRENT_TIMESTAMP);
To retrieve the current price for a single product, the calling SELECT statement adds a WHERE-clause
restriction when calling the view:
SELECT *
FROM vPriceList

WHERE = ‘1001’;
Result:
Code Price

1001 14.9500
SQL Server internally creates a new SQL statement from vPricelist and the calling SELECT state-
ment’s
WHERE-clause restriction and then generates a query execution plan.
629
www.getcoolebook.com
Nielsen c25.tex V4 - 07/23/2009 4:55pm Page 630
Part IV Developing with SQL Server
In contrast, a function allows the restriction to be passed as a parameter to the SQL SELECT statement:
CREATE FUNCTION dbo.ftPriceList (
@Code CHAR(10) = Null, @PriceDate DateTime)
RETURNS Table
AS
RETURN(
SELECT Code, Price.Price
FROM dbo.Price
JOIN dbo.Product P
ON Price.ProductID = P.ProductID
WHERE EffectiveDate =
(SELECT MAX(EffectiveDate)
FROM dbo.Price
WHERE ProductID = P.ProductID
AND EffectiveDate <= @PriceDate)
AND (Code = @Code
OR @Code IS NULL)
);

If the function is called with default code, then the price for the entered date is returned for all
products:
SELECT * FROM dbo.ftPriceList(DEFAULT, ‘20020220’);
Result:
Code Price

1047 6.9500
1049 12.9500

If a product code is passed in the first input parameter, then the pre-compiled SELECT statement within
the function returns the single product row:
SELECT * FROM dbo.ftPriceList(’1001’, ‘2/20/2002’);
Result:
Code Price

1001 14.9500
Correlated user-defined functions
The APPLY command may be used with a table-valued user-defined function so that the UDF accepts a
different parameter value for each corresponding row being processed by the main query.
630
www.getcoolebook.com
Nielsen c25.tex V4 - 07/23/2009 4:55pm Page 631
Building User-Defined Functions 25
Back in the SQL Server 2000 days, not having this capability was a serious limitation that caused me
a considerable amount of time to work around, so I’m pleased to see that Microsoft added the
APPLY
function in SQL Server 2005.
The
APPLY command has two forms. The most common form, the CROSS APPLY,hasaconfusing
name because it operates more like an inner join than a cross join. The

CROSS APPLY command will
join data from the main query with any table-value d data sets from the user-defined function. If no
data is returned from the UDF, then the row from the main query is also not returned, as shown in the
following example:
USE CHA2;
go
CREATE FUNCTION ftEventList2 (@CustomerID INT)
RETURNS Table
AS
RETURN(
SELECT dbo.CustomerType.Name AS Customer,
dbo.Customer.LastName, dbo.Customer.FirstName,
dbo.Customer.Nickname,
dbo.Event_mm_Customer.ConfirmDate, dbo.Event.Code,
dbo.Event.DateBegin, dbo.Tour.Name AS Tour,
dbo.BaseCamp.Name, dbo.Event.Comment
FROM dbo.Tour
INNER JOIN dbo.Event
ON dbo.Tour.TourID = dbo.Event.TourID
INNER JOIN dbo.Event_mm_Customer
ON dbo.Event.EventID = dbo.Event_mm_Customer.EventID
INNER JOIN dbo.Customer
ON dbo.Event_mm_Customer.CustomerID
= dbo.Customer.CustomerID
LEFT OUTER JOIN dbo.CustomerType
ON dbo.Customer.CustomerTypeID
= dbo.CustomerType.CustomerTypeID
INNER JOIN dbo.BaseCamp
ON dbo.Tour.BaseCampID = dbo.BaseCamp.BaseCampID
WHERE Customer.CustomerID = @CustomerID

);
SELECT C.LastName, Code, DateBegin, Tour
FROM Customer C
CROSS APPLY ftEventList2(C.CustomerID)
ORDER BY C.LastName;
Result:
LastName Code DateBegin Tour

Anderson 01-003 2001-03-16 00:00:00.000 Amazon Trek
Anderson 01-006 2001-07-03 00:00:00.000 Bahamas Dive
631
www.getcoolebook.com

×