---------------------------------------------- -- Try/Catch USE AdventureWorks GO CREATE PROCEDURE MyProc AS BEGIN BEGIN TRY SELECT * FROM Person.Address WHERE 1 > 2/0 END TRY BEGIN CATCH PRINT 'Error Occurred' PRINT ERROR_NUMBER() PRINT ERROR_SEVERITY() PRINT ERROR_STATE() PRINT ERROR_MESSAGE() END CATCH END GO EXEC MyProc ---------------------------------------------- -- Common Table Expressions WITH Median AS ( SELECT ((MAX(LineTotal) - MIN(LineTotal)) / 2) AS MedianValue FROM Sales.SalesOrderDetail ) SELECT TOP 1000 SalesOrderID, SalesOrderDetailID, LineTotal, CASE WHEN LineTotal > Median.MedianValue THEN 1 ELSE 0 END AS OverHalf FROM Sales.SalesOrderDetail, Median ---------------------------------------------- -- Common Table Expressions (Recursive) SET NOCOUNT ON CREATE TABLE MLM ( ID int, Name varchar(10), Parent int) GO INSERT MLM VALUES ( 1,'Bob',null) INSERT MLM VALUES ( 2,'Amy',1) INSERT MLM VALUES ( 3,'Jake',1) INSERT MLM VALUES ( 4,'Kirra',2) INSERT MLM VALUES ( 5,'Mark',3) INSERT MLM VALUES ( 6,'Steve',3) INSERT MLM VALUES ( 7,'Randy',3) INSERT MLM VALUES ( 8,'Kristen',4) INSERT MLM VALUES ( 9,'Isaac',6) INSERT MLM VALUES (10,'Cole',9) GO SELECT * FROM MLM GO WITH Descendant (Parent,ID,Name) AS (SELECT Parent, ID, Name FROM MLM WHERE ID = 3 UNION ALL SELECT M.Parent, M.ID, M.Name FROM MLM AS M INNER JOIN Descendant AS D ON D.ID = M.Parent) SELECT ID FROM Descendant GO DROP TABLE MLM ---------------------------------------------- -- PIVOT -- Raw data - expensive (> $10k items) sold in 2002 by quarter SELECT DatePart(q,OrderDate) as Quarter, P.Name, LineTotal FROM Sales.SalesOrderHeader AS H INNER JOIN Sales.SalesOrderDetail AS D ON H.SalesOrderID = D.SalesOrderID INNER JOIN Production.Product AS P ON D.ProductID = P.ProductID WHERE DatePart(yy,OrderDate) = 2002 AND D.LineTotal > 10000 -- Put into a temporary table (optional) SET NOCOUNT ON SELECT DatePart(q,OrderDate) as Quarter, P.Name, LineTotal INTO #TempSales FROM Sales.SalesOrderHeader AS H INNER JOIN Sales.SalesOrderDetail AS D ON H.SalesOrderID = D.SalesOrderID INNER JOIN Production.Product AS P ON D.ProductID = P.ProductID WHERE DatePart(yy,OrderDate) = 2002 AND D.LineTotal > 10000 -- Pivot SELECT Name, [1] AS 'Q1', [2] AS 'Q2', [3] AS 'Q3', [4] AS 'Q4' FROM #TempSales PIVOT(SUM(LineTotal) FOR Quarter IN ([1], [2], [3], [4])) AS P -- Done DROP TABLE #TempSales ---------------------------------------------- -- ROW_NUMBER -- Return Orders for Customers < 100 with row numbers SELECT SalesOrderID, CustomerID, ROW_NUMBER() OVER(ORDER BY SalesOrderID) AS Number FROM Sales.SalesOrderHeader WHERE CustomerID < 100 -- Return Orders for Customers < 100 with row numbers and more! SELECT SalesOrderID, CustomerID, ROW_NUMBER() OVER(ORDER BY CustomerID) AS Number, RANK() OVER(ORDER BY CustomerID) AS [rank], DENSE_RANK() OVER(ORDER BY CustomerID) AS [denserank], NTILE(5) OVER(ORDER BY CustomerID) AS ntile5 FROM Sales.SalesOrderHeader WHERE CustomerID < 100 ---------------------------------------------- -- WINDOWING (OVER) SELECT *, RANK() OVER(PARTITION BY Title ORDER BY VacationHours) AS [rank] from ( SELECT E.Title, C.LastName, E.VacationHours FROM HumanResources.Employee AS E INNER JOIN Person.Contact AS C ON E.ContactID = C.ContactID ) AS A ---------------------------------------------- -- WINDOWING (OVER with MAX aggregate) SELECT *, RANK() OVER(PARTITION BY Title ORDER BY VacationHours) AS [rank], MAX(VacationHours) OVER(PARTITION BY Title) as [Most Hours by Title] from ( SELECT E.Title, C.LastName, E.VacationHours FROM HumanResources.Employee AS E INNER JOIN Person.Contact AS C ON E.ContactID = C.ContactID ) AS A ---------------------------------------------- -- CROSS APPLY and OUTER APPLY -- Create UDF CREATE FUNCTION Greater(@v float, @t float) RETURNS TABLE AS RETURN SELECT @v AS UDF WHERE @v > @t GO -- Test /* SELECT * FROM dbo.Greater(100,200) SELECT * FROM dbo.Greater(200,100) SELECT * FROM dbo.Greater(200,200) -- Test with CROSS APPLY SELECT * FROM Sales.SalesOrderDetail AS D CROSS APPLY Greater(D.LineTotal, 15000) WHERE OrderQty > 10 -- Test with OUTER APPLY SELECT * FROM Sales.SalesOrderDetail AS D OUTER APPLY Greater(D.LineTotal, 15000) WHERE OrderQty > 10 */ -- Done DROP FUNCTION Greater GO ---------------------------------------------- -- UPDATE with OUTPUT SET NOCOUNT OFF SELECT * FROM Production.Product WHERE Color = 'Black' -- UPDATE Production.Product SET Color = 'Schwarz' WHERE Color = 'Black' -- DECLARE @Changes TABLE (ProductID int, Name nvarchar(50), Color nvarchar(15), OldColor nvarchar(15)) UPDATE Production.Product SET Color = 'Black' OUTPUT Inserted.ProductID, Inserted.Name, Inserted.Color, Deleted.Color INTO @Changes WHERE Color = 'Schwarz' SELECT * FROM @Changes ---------------------------------------------- -- INSERT with OUTPUT USE AdventureWorks GO SET NOCOUNT ON GO -- Create table CREATE TABLE Test (ID int identity(1,1), Data varchar(20)) GO -- Quick Test INSERT Test (Data) Values ('Red') SELECT * FROM Test -- @@IDENTITY returns last identity value assigned INSERT Test (Data) Values ('Blue') SELECT @@IDENTITY -- @@IDENTITY doesn't handle inserting multiple rows INSERT Test (Data) SELECT Data FROM Test SELECT @@Identity -- Using INSERT ... with OUTPUT is the solution! DECLARE @NewRows TABLE (ID int, Data varchar(20)) INSERT Test (Data) OUTPUT Inserted.ID, Inserted.Data INTO @NewRows SELECT Data FROM Test SELECT * FROM @NewRows -- Done DROP TABLE Test ---------------------------------------------- -- Large Objects (LOBs) CREATE TABLE BigText (ID int, Data text) INSERT BigText VALUES (1,'Hello World') -- SELECT * FROM BigText SELECT UPPER(Data) FROM BigText -- INSERT BigText VALUES (1,REPLICATE('x',10000)) SELECT * FROM BigText -- DROP Table BigText CREATE TABLE BigText (ID int, Data varchar(max)) -- INSERT BigText VALUES (1,'Hello World') INSERT BigText VALUES (1,REPLICATE('x',10000)) SELECT * FROM BigText SELECT UPPER(Data) FROM BigText -- DROP Table BigText ---------------------------------------------- -- Synonyms CREATE SYNONYM Emp FOR AdventureWorks.HumanResources.Employee SELECT * FROM Emp DROP SYNONYM Emp ---------------------------------------------- -- DDL Triggers CREATE TRIGGER trgThankYou ON DATABASE FOR CREATE_TABLE AS PRINT 'Thank you for contributing to AdventureWorks!' -- CREATE TABLE NewTable (ID int) -- CREATE TRIGGER trgSafety ON DATABASE FOR DROP_TABLE AS PRINT 'Sorry, you may not drop tables!' ROLLBACK -- DROP TABLE NewTable -- How do you drop DDL triggers? (Have SMS show you ...) DROP TRIGGER [trgSafety] ON DATABASE DROP TRIGGER [trgThankYou] ON DATABASE DROP TABLE NewTable ---------------------------------------------- -- XML ---------------------------------------------- ---------------------------------------------- -- XML Data Type SET NOCOUNT ON CREATE TABLE Candidate (ID int, Name varchar(50), Resume xml) -- INSERT Candidate VALUES (1,'Smith','none') SELECT * FROM Candidate -- INSERT Candidate VALUES (1,'Smith','none') INSERT Candidate VALUES (1,'Smith','nonenone') SELECT * FROM Candidate -- INSERT Candidate VALUES (1,'Smith','none') INSERT Candidate VALUES (1,'Smith','no resume & no CV') ---------------------------------------------- -- XML Schema CREATE XML SCHEMA COLLECTION ResumeSchema AS N'' -- Schema -- SELECT * FROM sys.xml_schema_collections -- Create Table DROP TABLE Candidate CREATE TABLE Candidate (ID int, Name varchar(50), Resume xml (ResumeSchema)) -- INSERT Candidate VALUES (1,'Smith','') -- Smith.xml Pat Smith Speak about Yukon and Whidbey 1993 1999 Software Developer Built lots of really cool software! 2000 2005 Trainer and Author Wrote books and spoke at lots of conferences 1989 1993 Podunk University I like pudding! ---------------------------------------------- -- FOR XML Improvements SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE ContactID < 10 -- SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE ContactID < 10 FOR XML AUTO -- SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE ContactID < 10 FOR XML AUTO, ELEMENTS -- SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE ContactID < 10 FOR XML PATH -- SELECT Title AS '@Title', FirstName as 'Name/First', LastName as 'Name/Last', EmailAddress as 'Email' FROM Person.Contact WHERE ContactID < 10 FOR XML PATH ('Person') -- SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE FirstName = 'Cathan' -- SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE FirstName = 'Cathan' FOR XML AUTO, ELEMENTS -- SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE FirstName = 'Cathan' FOR XML AUTO, ELEMENTS XSINIL -- SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE FirstName = 'Cathan' FOR XML AUTO, XMLDATA -- SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE FirstName = 'Cathan' FOR XML AUTO, XMLSCHEMA -- SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE FirstName = 'Cathan' FOR XML AUTO, ELEMENTS, XMLSCHEMA -- DECLARE @XML XML SET @XML = (SELECT Title, FirstName, LastName, EmailAddress FROM Person.Contact WHERE ContactID < 10 FOR XML PATH) SELECT @XML ---------------------------------------------- -- XQUERY DECLARE @xmldoc xml SET @xmldoc = ' Martin Gudgin 33 short Simon Horrell 40 short Mark Szolkowski 30 medium ' SELECT @xmldoc -- Return names SELECT @xmldoc.query(' (: SQL Server 2005 :) (: doc function not used :) for $p in /people/person return $p/name') -- Return people older than 30 (XPATH way) SELECT @xmldoc.query(' (: this uses an XPath predicate :) /people/person[age > 30]') -- Return people older than 30 (FLWOR way) SELECT @xmldoc.query(' (: this uses a where :) for $p in /people/person where $p/age > 30 order by $p/age[1] return $p/name') -- Getting Creative SELECT @xmldoc.query(' (: this uses a where :) for $p in /people/person where $p/age > 30 order by $p/age[1] return {$p/name}') ---------------------------------------------- -- XML DML UPDATE HumanResources.JobCandidate SET Resume = ' entire new xml document ' -- DECLARE @myDoc xml SET @myDoc = '' SELECT @myDoc as Example -- SET @myDoc.modify('insert Built Microsoft BOB into (/Student/Experience)[1]') SELECT @myDoc -- SET @myDoc.modify(' replace value of (/Student/Experience/Development[1]/text())[1] with "Built Donkey .NET"') SELECT @myDoc -- SET @myDoc.modify('delete /Student/Experience/Development[1]') SELECT @myDoc --------------------------------------------------------------- -- Managed Code --------------------------------------------------------------- --------------------------------------------------------------- -- UDF using System; using System.Collections; using System.Data; using System.Data.Sql; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; public partial class CoolFunctions { [Microsoft.SqlServer.Server.SqlFunction] public static SqlString CoolEncrypt(SqlString Data) { char[] strArray = Data.ToString().ToCharArray(); Array.Reverse(strArray); return new string(strArray); } }; -- SELECT dbo.CoolEncrypt('SQL Server 2005 Rocks!') SELECT dbo.CoolEncrypt('A man a plan a canal Panama') --------------------------------------------------------------- -- Update the TEST.SQL and run with F5 --------------------------------------------------------------- -- Add a breakpoint and run with F5 --------------------------------------------------------------- -- Stored Procedure using System; using System.Data; using System.Data.Sql; using System.Data.SqlTypes; using System.IO; using System.Xml; using System.Xml.XPath; using System.Xml.Xsl; using Microsoft.SqlServer.Server; public partial class StoredProcedures { [Microsoft.SqlServer.Server.SqlProcedure] public static void spTransformXML(SqlXml rawXML, SqlXml rawXSLT) { SqlPipe pipe = SqlContext.Pipe; // Cast XML XmlDocument xmlDoc = new System.Xml.XmlDocument(); xmlDoc.LoadXml(rawXML.Value); // Cast XSLT XmlDocument xslDoc = new System.Xml.XmlDocument(); xslDoc.LoadXml(rawXSLT.Value); // Transform XslCompiledTransform xslt = new XslCompiledTransform(); xslt.Load(xslDoc); Stream stream = new MemoryStream(); XmlWriter wrt = new XmlTextWriter(stream, System.Text.Encoding.UTF8); xslt.Transform(xmlDoc, null, wrt); // Return HTML stream.Position = 0; StreamReader SR = new StreamReader(stream); pipe.Send(SR.ReadToEnd()); SR.Close(); } }; --------------------------------------------------------------- -- Test DECLARE @xml xml SET @xml = ' Richard Hundhausen Trainer Steven Borg Trainer ' DECLARE @xsl xml SET @xsl = '
First Last Title
' exec spTransformXML @xml, @xsl --------------------------------------------------------------- -- UDT using System; using System.Data; using System.Data.Sql; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; [Serializable] [Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native)] [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] public class DMS : INullable { private Int16 dd, mm, ss; private bool bIsNull = true; public Int16 DD { get { return dd; } set { dd = value; } } public Int16 MM { get { return mm; } set { mm = value; } } public Int16 SS { get { return ss; } set { ss = value; } } public override string ToString() { if (bIsNull) return "null"; else return String.Format("{0}°{1}'{2}", dd, mm, ss); } public bool IsNull { get { return bIsNull; } } public static DMS Null { get { DMS dms = new DMS(); return dms; } } public static DMS Parse(SqlString s) { if (s.IsNull || s.Value.ToLower().Equals("null")) return null; DMS dms = new DMS(); string[] st = s.Value.Split('.'); dms.DD = Int16.Parse(st[0]); dms.MM = Int16.Parse(st[1]); dms.SS = Int16.Parse(st[2]); dms.bIsNull = false; return dms; } } --------------------------------------------------------------- -- Test the UDT CREATE TABLE EmployeeLocation ( Employee int, Latitude DMS, Longitude DMS) GO INSERT EmployeeLocation VALUES (1,'15.55.00','37.15.12') INSERT EmployeeLocation VALUES (2,'25.27.15','114.38.05') SELECT Latitude.DD, Latitude.MM, Latitude.SS FROM EmployeeLocation SELECT Latitude.ToString() FROM EmployeeLocation -- Fails because the assembly isn't registered on the local machine SELECT * FROM EmployeeLocation INSERT EmployeeLocation VALUES (3,null,null) SELECT Latitude.ToString() FROM EmployeeLocation