Beginning Microsoft SQL Server 2008 ... - S3 Tech Training

Beginning Microsoft SQL Server 2008 ... - S3 Tech Training Beginning Microsoft SQL Server 2008 ... - S3 Tech Training

cdn.s3techtraining.com
from cdn.s3techtraining.com More from this publisher
17.06.2013 Views

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

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());

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!