---------------------------------------------- -- 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), Upline 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 (Upline,ID,Name) AS (SELECT Upline, ID, Name FROM MLM WHERE ID = 3 UNION ALL SELECT M.Upline, M.ID, M.Name FROM MLM AS M INNER JOIN Descendant AS D ON D.ID = M.Upline) SELECT ID, Name 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 -- UNPIVOT option available too! ---------------------------------------------- -- ROW_NUMBER -- Return Orders for Customers with id < 100 (and include row numbers) SELECT ROW_NUMBER() OVER(ORDER BY SalesOrderID) AS Number , SalesOrderID, CustomerID FROM Sales.SalesOrderHeader WHERE Number between 19 and 33 WITH Mark (Number,SalesOrderID,CustomerID) AS ( SELECT ROW_NUMBER() OVER(ORDER BY SalesOrderID) AS Number , SalesOrderID, CustomerID FROM Sales.SalesOrderHeader WHERE CustomerID < 100) SELECT * FROM Mark WHERE Number < 5 -- 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 desc) 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 DESC) 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), NewColor 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 drop table test 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 SELECT SCOPE_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 SELECT * FROM HUMANRESOURCES.SHIFT order by Checksum(NewID()) select checksum('i love sql serverr') select newid() SELECT * FROM Test ORDER BY Checksum(NewID()) ---------------------------------------------- -- 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 CREATE SYNONYM TFSThing FOR TfsVersionControl.dbo.tbl_Content SELECT * FROM TFSThing 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) -- drop trigger trgsafety on database CREATE TRIGGER trgSafety ON DATABASE FOR DROP_TABLE AS SELECT EVENTDATA() rollback PRINT 'Sorry, you may not drop tables!' CREATE TRIGGER trgSmackdown ON DATABASE FOR GRANT_DATABASE AS SELECT EVENTDATA() GRANT SELECT ON Person.Contact TO DBO -- 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','') 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' ' -- SELECT * FROM SYS.OBJECTS 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',' 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, elements -- 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'), ROOT ('People'), XMLSCHEMA -- 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('Pizza') -- 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 /* -- Sample code on returning parent and child records as XML -- SELECT TOP 10 * FROM Event DECLARE @Results XML SET @Results = (SELECT DISTINCT TOP 10 E.EventID AS '@ID', RTRIM(E.Category) AS 'Category', E.FY, CONVERT(varchar(20),E.StartDate,101) AS 'Starts', CONVERT(varchar(20),E.EndDate,101) AS 'Ends', (SELECT CountryID FROM EventLocation WHERE EventLocation.EventID = E.EventID FOR XML PATH ('')) AS 'Countries/Country', (SELECT '(' + [Type] + ') ' + Narrative FROM EventNarrative WHERE EventNarrative.EventID = E.EventID FOR XML PATH ('')) AS 'Narratives/Narrative' FROM Event E -- INNER JOIN EventLocation EL ON E.EventID = EL.EventID WHERE (SELECT COUNT(*) FROM EVENTLOCATION EL WHERE EL.EVENTID = E.EVENTID) > 1 and (SELECT COUNT(*) FROM EVENTNARRATIVE EN WHERE EN.EVENTID = E.EVENTID) > 0 FOR XML PATH ('Event'), root ('Events')) SELECT dbo.CleanXML(@Results) -- /* SELECT TOP 3 * FROM Event FOR XML AUTO, XMLSCHEMA Supporting functions CREATE FUNCTION CleanXML ( @XML xml ) RETURNS xml AS BEGIN DECLARE @ResultsText varchar(max) SET @ResultsText = CONVERT(varchar(max),@XML) SET @ResultsText = REPLACE(@ResultsText,'>','>') SET @ResultsText = REPLACE(@ResultsText,'<','<') RETURN CONVERT(xml,@ResultsText) END GO */ */ ---------------------------------------------- -- 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 (FWOR 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 "Married the woman who built Microsoft Bob"') SELECT @myDoc -- SET @myDoc.modify('delete /Student/Experience/Development[1]') SELECT @myDoc