Beginning Microsoft SQL Server 2008 ... - S3 Tech Training
Beginning Microsoft SQL Server 2008 ... - S3 Tech Training Beginning Microsoft SQL Server 2008 ... - S3 Tech Training
Chapter 13: User-Defined Functions 414 unlikely to return us any data — even if it happened on the same day (it would have had to have happened within in the same minute for a smalldatetime, within a millisecond for a full datetime field, and potentially down to as close as 100 milliseconds for datetime2). The typical solution is to convert the date to a string and back in order to truncate the time information, and then perform the comparison. It might look something like: SELECT * FROM Orders WHERE CONVERT(varchar(12), OrderDate, 101) = CONVERT(varchar(12), GETDATE(), 101) It is certainly worth noting that you could also do this by simply casting the value of @Date to the date data type. I’ve chosen to use CONVERT here just to show a more backward-compatible way of truncating dates (SQL Server 2005 and earlier did not support the date data type). This time, we will get back every row with today’s date in the OrderDate column, regardless of what time of day the order was taken. Unfortunately, this isn’t exactly the most readable code. Imagine you had a large series of dates you needed to perform such comparisons against — it can get very ugly indeed. So now let’s look at doing the same thing with a simple user-defined function. First, we’ll need to create the actual function. This is done with the new CREATE FUNCTION command, and it’s formatted much like a sproc. For example, we might code this function like this: CREATE FUNCTION dbo.DayOnly(@Date date) RETURNS date AS BEGIN RETURN @Date; END where the date returned from GETDATE() is passed in as the parameter and the task of converting the date is included in the function body and the truncated date is returned. Note that the preceding version is a SQL Server 2008 compatible version, relying on the coercion into the parameter’s date data type to truncate the time. If you wanted to do a truncation like this in SQL Server 2005 (as we did with the query-based example), you would need to use the CONVERT function as we did before. For example: CREATE FUNCTION dbo.DayOnly(@Date datetime) RETURNS varchar(12) AS BEGIN RETURN CONVERT(varchar(12), @Date, 101); END To see this function in action, let’s re-format our query slightly: SELECT * FROM Orders WHERE dbo.DayOnly(OrderDate) = dbo.DayOnly(GETDATE());
We get back the same set as with the stand-alone query. Even for a simple query like this one, the new code is quite a bit more readable. The call works pretty much as it would from most languages that support functions. There is, however, one hitch — the schema is required. SQL Server will, for some reason, not resolve scalar value functions the way it does with other objects. As you might expect, there is a lot more to UDFs than just readability. You can embed queries in them and use them as an encapsulation method for subqueries. Almost anything you can do procedurally that returns a discrete value could also be encapsulated in a UDF and used inline with your queries. Let’s take a look at a very simple subquery example. The subquery version looks like this: USE AdventureWorks2008; SELECT Name, ListPrice, (SELECT AVG(ListPrice) FROM Production.Product) AS Average, ListPrice - (SELECT AVG(ListPrice) FROM Production.Product) AS Difference FROM Production.Product WHERE ProductSubCategoryID = 1; -- The Mountain Bikes Sub-cat This gets us back a pretty simple set of data: Name ListPrice Average Difference ------------------------------ ------------ --------------- -------------- Mountain-100 Silver, 38 3399.99 438.6662 2961.3238 Mountain-100 Silver, 42 3399.99 438.6662 2961.3238 Mountain-100 Silver, 44 3399.99 438.6662 2961.3238 Mountain-100 Silver, 48 3399.99 438.6662 2961.3238 Mountain-100 Black, 38 3374.99 438.6662 2936.3238 Mountain-100 Black, 42 3374.99 438.6662 2936.3238 … … Mountain-500 Silver, 52 564.99 438.6662 126.3238 Mountain-500 Black, 40 539.99 438.6662 101.3238 Mountain-500 Black, 42 539.99 438.6662 101.3238 Mountain-500 Black, 44 539.99 438.6662 101.3238 Mountain-500 Black, 48 539.99 438.6662 101.3238 Mountain-500 Black, 52 539.99 438.6662 101.3238 (32 row(s) affected) Let’s try it again, only this time we’ll encapsulate both the average and the difference into two functions. The first encapsulates the task of calculating the average and the second does the subtraction. CREATE FUNCTION dbo.AveragePrice() RETURNS money WITH SCHEMABINDING AS BEGIN RETURN (SELECT AVG(ListPrice) FROM Production.Product); END GO Chapter 13: User-Defined Functions 415
- Page 401 and 402: IF @ErrorNo = 2714 -- Object exists
- Page 403: Chapter 11: Writing Scripts and Bat
- Page 406 and 407: Chapter 12: Stored Procedures Creat
- Page 408 and 409: Chapter 12: Stored Procedures Dropp
- Page 410 and 411: Chapter 12: Stored Procedures Suppl
- Page 412 and 413: Chapter 12: Stored Procedures 374 [
- Page 414 and 415: Chapter 12: Stored Procedures Confi
- Page 416 and 417: Chapter 12: Stored Procedures Now,
- Page 418 and 419: Chapter 12: Stored Procedures SQL S
- Page 420 and 421: Chapter 12: Stored Procedures 382 c
- Page 422 and 423: Chapter 12: Stored Procedures It wo
- Page 424 and 425: Chapter 12: Stored Procedures 386 r
- Page 426 and 427: Chapter 12: Stored Procedures 388 -
- Page 428 and 429: Chapter 12: Stored Procedures Note
- Page 430 and 431: Chapter 12: Stored Procedures 392 n
- Page 432 and 433: Chapter 12: Stored Procedures All t
- Page 434 and 435: Chapter 12: Stored Procedures Sproc
- Page 436 and 437: Chapter 12: Stored Procedures When
- Page 438 and 439: Chapter 12: Stored Procedures 400 @
- Page 440 and 441: Chapter 12: Stored Procedures I’d
- Page 442 and 443: Chapter 12: Stored Procedures match
- Page 444 and 445: Chapter 12: Stored Procedures There
- Page 446 and 447: Chapter 12: Stored Procedures 408 f
- Page 449 and 450: 13 User-Defined Functions Well, her
- Page 451: types!), except for BLOBs, cursors,
- Page 455 and 456: AS RETURN (SELECT BusinessEntityID,
- Page 457 and 458: in your relational database. These
- Page 459 and 460: AS BEGIN ( EmployeeID int NOT NULL,
- Page 461 and 462: So, as you can see, we can actually
- Page 463 and 464: Despite being schema-bound, this on
- Page 465 and 466: 14 Transactions and Locks This is o
- Page 467 and 468: we are unable or do not want to com
- Page 469 and 470: Figure 14-1 Data needed Data in cac
- Page 471 and 472: Transaction 4 This transaction wasn
- Page 473 and 474: Oops — problem!!! Transaction 2 h
- Page 475 and 476: The only cure for this is setting y
- Page 477 and 478: Exclusive Locks Exclusive locks are
- Page 479 and 480: Also: ❑ The Sch-S is compatible w
- Page 481 and 482: The syntax for switching between th
- Page 483 and 484: As with most things in life, howeve
- Page 485 and 486: purchased. Process 2 records sales;
- Page 487: Chapter 14: Transactions and Locks
- Page 490 and 491: Chapter 15: Triggers the world’s
- Page 492 and 493: Chapter 15: Triggers WITH ENCRYPTIO
- Page 494 and 495: Chapter 15: Triggers FOR|AFTER The
- Page 496 and 497: Chapter 15: Triggers 458 To illustr
- Page 498 and 499: Chapter 15: Triggers 460 IF EXISTS
- Page 500 and 501: Chapter 15: Triggers ❑ Feeding de
Chapter 13: User-Defined Functions<br />
414<br />
unlikely to return us any data — even if it happened on the same day (it would have had to have happened<br />
within in the same minute for a smalldatetime, within a millisecond for a full datetime field,<br />
and potentially down to as close as 100 milliseconds for datetime2).<br />
The typical solution is to convert the date to a string and back in order to truncate the time information,<br />
and then perform the comparison.<br />
It might look something like:<br />
SELECT *<br />
FROM Orders<br />
WHERE CONVERT(varchar(12), OrderDate, 101) = CONVERT(varchar(12), GETDATE(), 101)<br />
It is certainly worth noting that you could also do this by simply casting the value of @Date to the<br />
date data type. I’ve chosen to use CONVERT here just to show a more backward-compatible way of<br />
truncating dates (<strong>SQL</strong> <strong>Server</strong> 2005 and earlier did not support the date data type).<br />
This time, we will get back every row with today’s date in the OrderDate column, regardless of what<br />
time of day the order was taken. Unfortunately, this isn’t exactly the most readable code. Imagine you had<br />
a large series of dates you needed to perform such comparisons against — it can get very ugly indeed.<br />
So now let’s look at doing the same thing with a simple user-defined function. First, we’ll need to create<br />
the actual function. This is done with the new CREATE FUNCTION command, and it’s formatted much<br />
like a sproc. For example, we might code this function like this:<br />
CREATE FUNCTION dbo.DayOnly(@Date date)<br />
RETURNS date<br />
AS<br />
BEGIN<br />
RETURN @Date;<br />
END<br />
where the date returned from GETDATE() is passed in as the parameter and the task of converting the<br />
date is included in the function body and the truncated date is returned.<br />
Note that the preceding version is a <strong>SQL</strong> <strong>Server</strong> <strong>2008</strong> compatible version, relying on the coercion into the<br />
parameter’s date data type to truncate the time. If you wanted to do a truncation like this in <strong>SQL</strong> <strong>Server</strong><br />
2005 (as we did with the query-based example), you would need to use the CONVERT function as we did<br />
before. For example:<br />
CREATE FUNCTION dbo.DayOnly(@Date datetime)<br />
RETURNS varchar(12)<br />
AS<br />
BEGIN<br />
RETURN CONVERT(varchar(12), @Date, 101);<br />
END<br />
To see this function in action, let’s re-format our query slightly:<br />
SELECT *<br />
FROM Orders<br />
WHERE dbo.DayOnly(OrderDate) = dbo.DayOnly(GETDATE());