SQLUSA
 

Microsoft SQL Server 2005 Articles

 

Top 10 SQL Server 2005 New Features for Developers

By Kalman Toth, M.Phil., M.Phil., MCDBA, MCITP

May 6, 2006

You may have heard lots of hype about SQL Server 2005 new features. Way and beyond the stable Transact-SQL which hardly changed anything in the last 15 years. You have even seen scripts and demonstrations of the new feature, and now you are convinced; SQL 2005 is much better than SQL 2000. But is it really? Based on my 15 years of experience with T-SQL in heavy-duty practical applications of Wall Street high finance and consumer websites, I came up with a rating. These features are mainly for programming, not administration or operations. Albeit sometimes the lines are blurred: a new programming feature may turn out to be an operational godsend as well. So without much further ado, here comes the Top 10 list for Microsoft's latest version of SQL Server.

1. TRY/CATCH Error Control

Without doubt this is the most beneficial new feature of SQL 2005. It introduces soft error control into T-SQL, the same kind available in standard programming languages. Well, a simplified version, but it does the job miraculously. Prior to SQL 2005 if a script encountered an error, that was the end. An application can deal with it, by catching the error, but an SQL agent job written in T-SQL cannot. Typical example: you are moving million rows from a staging table to final destination. The staging table date is in varchar format, the destination column is datetime format. The conversion fails on “2006 janu 44”. Which is row 804249. But you only know that after ½ hour of intense research. SQL 2000 T-SQL gives no indication where it fails. It just bombs. It may have taken 10 minutes to reach the point of failure; it may take another 10 minutes to roll back the 804248 good insert transactions; at the end you ended up with nothing. In SQL 2005 T-SQL, instead of bombing, control is passed to the CATCH routine that for example can log the erroneous data, and upon finishing with it, the control flow is returned to a designated T-SQL statement. If this processing is in a loop, the loop would continue. Here is an example. If you change 1/0 to 1/1, it will not go to the CATCH processing; the "try" succeeds.

PRINT 'Start of processing'
BEGIN TRY
-- Generate a divide-by-zero error.
SELECT Infinity = 1/0;
PRINT 'No error'
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
PRINT 'Error occured'
END CATCH;
PRINT 'Processing continues'
GO

In SQL 2000 and prior T-SQL, the divide by zero would terminate all processing, open transactions would be rolled back, and no opportunity would be given to recover from the error and continue processing. The error is raised though which can be processed by an application.

With TRY/CATCH, errors can be taken care of on the server side without involving the client side at all. That is a huge productivity gain; also it leads to a more reliable operational environment.

2. CTE – Common Table Expression

CTE is a neat way of writing complex queries without the use of temp tables or derived tables. CTE itself can be considered a temp table with scope limited to the query. Unlike derived tables which are intermingled with the main query, CTE-s are placed neatly to the top part of the query, greatly simplifying the main query itself. CTE-s will increase developer’s productivity by introducing “structured programming” into complex queries. Here is an example for a double CTE query where the second CTE is used to resolve both the employee name and his/her manager's name.

WITH cteEmployeeOrders(EmployeeID, NumOrders, MaxDate)
AS
(
SELECT EmployeeID, COUNT(*), MAX(OrderDate)
FROM Purchasing.PurchaseOrderHeader
GROUP BY EmployeeID
),
cteEmployeeName(Employee, Employeeid)
AS
(
SELECT LastName+', '+ FirstName, Employeeid
FROM HumanResources.Employee E
JOIN Person.Contact C
ON E.ContactID = C.ContactID
)
SELECT Employee = EN1.Employee, OrdersTaken=OE.NumOrders,
LastOrderDate=OE.MaxDate,
Manager = EN2.Employee, OrdersTaken=OM.NumOrders,
LastOrderDate = OM.MaxDate
FROM HumanResources.Employee AS E
JOIN cteEmployeeOrders AS OE
ON E.EmployeeID = OE.EmployeeID
LEFT OUTER JOIN cteEmployeeOrders AS OM
ON E.ManagerID = OM.EmployeeID
JOIN cteEmployeeName EN1
ON E.EmployeeID = EN1.EmployeeID
JOIN cteEmployeeName EN2
ON E.ManagerID = EN2.EmployeeID

3. Column Headers to Clipboard When Copying Results

How many times you had to produce an ad-hoc report and copy the results window into an Excel spreadsheet? Except the column headers did not come. If the report was destined to high circles, you had to manually type in 20 column headers?

With SQL 2005 you can turn on the option to include column headers when copying to clipboard.


4. ROW_NUMBER() Function

This solves a problem and more which in a curious way related to 3. In SQL 2000 results window you could the row sequence numbers on the frame, but again, those did not make it to the clipboard. In SQL 2005 you can generate row numbers for the entire results set or for partitions within the results set. Oracle had this feature for sometime. Here is an example:

SELECT 'Row Number' = ROW_NUMBER()
OVER(ORDER BY SalesYTD DESC),
c.FirstName, c.LastName,
SalesYTD = '$'+convert(varchar,s.SalesYTD,1),
a.PostalCode
FROM Sales.SalesPerson s
JOIN Person.Contact c
ON s.SalesPersonID = c.ContactID
JOIN Person.Address a
ON a.AddressID = c.ContactID
WHERE TerritoryID IS NOT NULL
AND SalesYTD <> 0


5. Tree Processing Functions

Probably you did not need tree processing many times during your database development career, but when you needed it, you really needed it. In lieu of it, you resorted to cumbersome loops going from level to level. Of course, tree processing is built-in into C++ and other object-oriented languages. Now you can have it handy in T-SQL when someone asks you to produce an organizational chart. Tree processing is associated with the CTE feature. Here is an example. The organizational chart of AdventureWorks mountain bike manufacturing company:

WITH cteOrgTree(EmployeeID, EmployeeName, ManagerID, TreeLevel, SortOrder)
AS
(
SELECT EmployeeID, EmployeeName=FirstName+' '+LastName, ManagerID, 0,
CAST(EmployeeID AS VARBINARY(1024))
FROM HumanResources.Employee e
JOIN Person.Contact c
ON e.ContactID = c.ContactID
WHERE ManagerID is null
UNION ALL
SELECT E.EmployeeID, EmployeeName=FirstName+' '+LastName, E.ManagerID, M.TreeLevel+1,
CAST(SortOrder + CAST(E.EmployeeID AS BINARY(4)) AS VARBINARY(1024))
FROM HumanResources.Employee AS E
JOIN cteOrgTree AS M
ON E.ManagerID = M.EmployeeID
JOIN Person.Contact C
ON E.ContactID = C.ContactID
)
SELECT
REPLICATE(' | ', TreeLevel)
+ '(' + (CAST(EmployeeID AS VARCHAR(10))) + ') '
+ EmployeeName AS EmployeeName
FROM cteOrgTree
ORDER BY SortOrder


6. Native XML

The XML integration will surely give a boost in the proliferation of XML as a data format standard. Example for an XQUERY:

SELECT CatalogDescription.query('
declare namespace p1="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
for $pd in /p1:ProductDescription,
$f in $pd/p1:Features/*
return
<Feature>
<ProductModelName> { data($pd/@ProductModelName) } </ProductModelName>
{ $f }
</Feature>
') as Feature
FROM Production.ProductModel
WHERE ProductModelID=25


7. The OUTPUT Clause

Not a very fancy feature, but extremely useful for setting up auditing and logging. It essentially makes the inserted and deleted tables available in the query itself, without resorting to the use of a trigger. Here is an example:


SELECT *
INTO CopyOfEmployee
FROM HumanResources.Employee
GO
DECLARE @DeletedEmployees TABLE
(
EmployeeID INT
)
WHILE (2 > 1)

BEGIN
BEGIN TRAN
DELETE TOP(10) FROM CopyOfEmployee
OUTPUT deleted.EmployeeID
INTO @DeletedEmployees
WHERE HireDate < '2003-02-02'
SELECT * FROM @DeletedEmployees
COMMIT TRAN
DELETE FROM @DeletedEmployees
IF @@rowcount < 10
BREAK
END

8. PIVOT

How many times management came to you for an ad-hoc cross-tab report. You had no choice but setup a temporary table and fill it up or use ackward CASE construct with heavy hard wiring to produce it. PIVOT solves this issue partially, dynamic PIVOT solves it even better. Here is an example:


SELECT pvt.*
FROM
(SELECT PurchaseOrderID, EmployeeID, VendorID
FROM Purchasing.PurchaseOrderHeader) p
PIVOT
(
COUNT (PurchaseOrderID)
FOR EmployeeID in ([274],
[238],
[241],
[223],
[264],
[266],
[231],
[244],
[261],
[233],
[164],
[198])
) AS pvt
ORDER BY VendorID

9. Schemas

The idea is that a schema should own db objects since the schema never leaves the company unlike people, especially IT people. SQL Server always had a schema, the infamous "dbo". Another way of looking at schemas is as convenient prefixes for a group of tables. A major shortcoming is that the schemas are single level. In a large database with over 3,000 tables, one would need multi-level schemas. Perhaps that feature is coming with SQL Server 2010.

10. Ranking and Tiling Functions

You can produce tons of Business Intelligence reports using these functions. The more you produce, the more will be requested. Here is an example:

SELECT ProductName = Name, Color, ListPrice,
ROW_NUMBER() OVER(ORDER BY ListPrice DESC) AS SeqNo,
NTILE(6) OVER(ORDER BY ListPrice DESC) AS Sextile,
RANK() OVER(ORDER BY ListPrice DESC) AS Rank
FROM Production.Product
ORDER BY ListPrice DESC


 

The Best SQL Server 2005 Training in the World
 
SQLUSA.com Home Page