Archive
SQL Server 2012 (a.k.a. Denali) | “SET FMTONLY ON/OFF” Deprecated | new SPs and DMFs Introduced
While running SQL Server Profiler to know what’s running behind the Application you might have noticed “SET FMTONLY ON” with other SQL statements. Most of you would be knowing this, the main point of putting this post is to let people know that this feature is deprecated in Denali and not to use it in new development work.
“SET FMTONLY ON” Returns only metadata to the client. It can be used to test the format of the response without actually running the query.
Let’s check how it is being used and what it returns:
USE AdventureWorks2012; GO -- Simple SELECT without using the "SET FMTONLY ON", OFF by-default: SELECT TOP 10 BusinessEntityID, Title, FirstName, MiddleName, LastName, Suffix, EmailPromotion FROM [Person].[Person] -- Using "SET FMTONLY": SET FMTONLY ON; SELECT TOP 10 BusinessEntityID, Title, FirstName, MiddleName, LastName, Suffix, EmailPromotion FROM [Person].[Person] SET FMTONLY OFF; GO
Output:

This feature is deprecated in Denali and should not be used in production code.
Now in replacement to this Denali has introduced 4 new objects to get the same information in easy and more informative way.
These are as follows:
--> 1. SP: sp_describe_first_result_set
-- Returns the metadata for the first possible result set of the Transact-SQL batch.
EXEC sp_describe_first_result_set @tsql = N'SELECT * FROM HumanResources.Employee'
--> 2. SP: sp_describe_undeclared_parameters
-- Returns a result set that contains metadata about undeclared parameters in a Transact-SQL batch.
EXEC sp_describe_undeclared_parameters @tsql = N'SELECT * FROM HumanResources.Employee where BusinessEntityID = @id AND BirthDate > @dt'
--> 3. DMF: sys.dm_exec_describe_first_result_set()
-- This is a DMF (Dynamic Management Function) which takes a Transact-SQL statement as a parameter and describes the metadata of the first result set for the statement.
SELECT * FROM sys.dm_exec_describe_first_result_set (N'SELECT * FROM HumanResources.Employee', NULL, 0)
-- It is similar to the SP sp_describe_first_result_set mentioned above.
--> 4. DMF: sys.dm_exec_describe_first_result_set_for_object()
-- This is a DMF (Dynamic Management Function) which takes an @object_id as a parameter and describes the first result metadata for the module with that ID.
SELECT * FROM sys.dm_exec_describe_first_result_set_for_object(OBJECT_ID('uspGetEmployeeManagers'), 0)
-- It has same set definition as sys.dm_exec_describe_first_result_set() DMF mentioned above.
-- The last mentioned DMF can also be used with the sys.procedures to get the metadata of the Stored Procedures:
SELECT p.name, X.*
FROM sys.procedures AS p
CROSS APPLY sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS X
… Just execute each one of these and check what all information they provide.
I really like these SPs and DM Functions, will be very useful in Metadata based based Dev work.
More info on this check MS BOL: http://msdn.microsoft.com/en-us/library/ms173839.aspx
SQL Server 2012 (a.k.a Denali) – New feature | Enhanced OVER() Clause – Part1
OVER() clause in SQL Server was introduced in version 2005 and it was the same in 2008 and even 2008-R2.
Ranking functions like ROW_NUMBER(), RANK(), DENSE_RANK() & NTILE() can use the OVER() clause to Parition and Order a record-set before actually applying their logic. Thus a large record-set can be divided into several partitions and calculations can be applied to each set in a set-based approach rather than going with Loops or Cursors.
With the new version of SQL Server i.e. 2012 the OVER() clause has been extended completely to other aggregates, like SUM(), AVG(), etc. Prior to this the OVER() clause can be partially used for these Aggregate Functions.
Here we will see solving a classic problem of calculating Running Totals with this new feature.
I’ve already discussed about solving this problem with JOINS in version 2005,2008, 2008-R2 in my previous post at following [link].
Let’s see how can we use the SUM() Aggregate Function with OVER() clause to solve this:
USE [AdventureWorks2012] GO -- With 2012, calculating Running totals by using Aggregare function SUM() with extended Window Function OVER(): ;with RunTot as ( select row_number() over(order by s.OrderDate) as row, s.SalesPersonID, p.FirstName, p.LastName, s.TotalDue, s.OrderDate from Sales.SalesOrderHeader s join Person.Person p on s.SalesPersonID = p.BusinessEntityID ) SELECT SalesPersonID, FirstName, LastName, OrderDate, TotalDue, SUM(TotalDue) OVER(partition by SalesPersonID ORDER BY row) FROM RunTot order by SalesPersonID, row GO -- Prior to 2012 calculating Running totals by using SELF-JOIN: ;with RunTot as ( select row_number() over(partition by s.SalesPersonID order by s.OrderDate) as row, s.SalesPersonID, p.FirstName, p.LastName, s.TotalDue, s.OrderDate from Sales.SalesOrderHeader s join Person.Person p on s.SalesPersonID = p.BusinessEntityID) select a.row, a.SalesPersonID, a.FirstName, a.LastName, a.OrderDate, a.TotalDue, sum(b.TotalDue) as RunTotal from RunTot a join RunTot b on a.SalesPersonID = b.SalesPersonID and a.row >= b.row group by a.row, a.SalesPersonID, a.FirstName, a.LastName, a.TotalDue, a.OrderDate order by a.SalesPersonID, a.row
On comparing performance of both the queries within the batch:
– The first query with SUM() window function costs just 38%.
– While the second query without the SUM() window function by using JOINS costs 68%.
I still like to call SQL Server’s latest release by its code name “Denali”, not “SQL Server 2012”.
Stay tuned to other new features of Denali in my forthcomming posts.
For more information about the OVER() clause check MS BOL link here: http://msdn.microsoft.com/en-us/library/ms189461.aspx
SQL Server 2012 (a.k.a Denali) – New feature | WITH RESULT SETS
“WITH RESULT SETS”, a new feature added in SQL Server 2012 allows us to tweak the Column Names and their Datatypes returned by an SP upon executed as per our needs. In SQL Server 2012 while calling an SP you can add the “WITH RESULT SETS” option and can provide the new Column Names and/or with new Datatypes with the parenthesis as per your wish (shown below).
Prior to this if one has to do the same, he has to either:
– Edit the same SP with new column names, or
– Create a new duplicate SP with different column headers, or
– Push records to a temp table first then use the records set form that table (discussed in later part of this post).
Let’s check this new feature by a simple example here by using a Stored Procedure from [AdventureWorks2012] database for SQL Server 2012:
USE [AdventureWorks2012] GO -- Normal SP call without using "WITH RESULT SETS" option: EXEC [dbo].[uspGetEmployeeManagers] @BusinessEntityID = 100
Recordset with same headers as in SP: RecursionLevel BusinessEntityID FirstName LastName OrganizationNode ManagerFirstName ManagerLastName 0 100 Lolan Song /3/1/9/7/ Kok-Ho Loh 1 93 Kok-Ho Loh /3/1/9/ Peter Krebs 2 26 Peter Krebs /3/1/ James Hamilton 3 25 James Hamilton /3/ Ken Sánchez
-- This is how you can use the new option and get desired headers: EXEC [dbo].[uspGetEmployeeManagers] @BusinessEntityID = 100 WITH RESULT SETS ( ( Level INT, BusinessID INT, EmpFirstName VARCHAR(50), EmpLastName VARCHAR(50), OrgNode VARCHAR(20), ManagerFirstName VARCHAR(50), ManagerLastName VARCHAR(50) ) )
Recordset with new headers as in SP: Level BusinessID EmpFirstName EmpLastName OrgNode ManagerFirstName ManagerLastName 0 100 Lolan Song /3/1/9/7/ Kok-Ho Loh 1 93 Kok-Ho Loh /3/1/9/ Peter Krebs 2 26 Peter Krebs /3/1/ James Hamilton 3 25 James Hamilton /3/ Ken Sánchez
–> Traditonal appraoch prior to ver. 2012
-- Create a table with columns with required names: CREATE TABLE #tempData ( Level INT, BusinessID INT, EmployeeFirstName VARCHAR(50), EmployeeLastName VARCHAR(50), OrgNode VARCHAR(20), ManagerFirstName VARCHAR(50), ManagerLastName VARCHAR(50) ) -- Insert records from the SP to the table: INSERT INTO #tempData (Level, BusinessID, EmployeeFirstName, EmployeeLastName, OrgNode, ManagerFirstName, ManagerLastName) EXEC @return_value = [dbo].[uspGetEmployeeManagers] @BusinessEntityID = 100 -- Finaly select the table and use the records: SELECT * FROM #tempData -- Final Cleanup: DROP TABLE #tempData GO
Well this is OK, but not very exciting feature for me as a developer.
I was hoping this would allow me to add/remove the columns and/or Concatenate them, like FirstName & LastName to FullName, but No!!!
For more information you can check MS BOL, here: http://msdn.microsoft.com/en-us/library/ms188332(v=sql.110).aspx
SQL Server 2012 (a.k.a Denali) – New feature | FileTables
In my [previous posts] I discussed about new features of Denali, now SQL Server 2012.
Here, in this post I’ll talk about a new feature that I explored recently, when I was working with creating tables in SQL Server 2012. I observed a new item in object explorer that took my attention and created eagerness to explore it, and that new thing is FileTables.
As per MS BOL, The new FileTable feature brings support for the Windows file namespace and compatibility with Windows applications to the file data stored in SQL Server. FileTable lets an application integrate its storage and data management components, and provides integrated SQL Server services – including full-text search and semantic search – over unstructured data and metadata.
In other words, you can store files and documents in special tables in SQL Server called FileTables, but access them from Windows applications as if they were stored in the file system, without making any changes to your client applications. The FileTable feature builds on top of SQL Server FILESTREAM technology.
–> Let’s see how can we use this new feature by a simple example below:
USE [master] GO -- Create a new Database with Filestream enabled: CREATE DATABASE [newFileStreamDB] CONTAINMENT = NONE ON PRIMARY ( NAME = N'newFileStreamDB', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER12RC0\MSSQL\DATA\newFileStreamDB.mdf' , SIZE = 5120KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB ), FILEGROUP newFileStreamGroup CONTAINS FILESTREAM DEFAULT ( NAME = newFileStreamGroupFiles, FILENAME= 'D:\SQL_Server2012\FileTables\Files', MAXSIZE = UNLIMITED ) LOG ON ( NAME = N'newFileStreamDB_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER12RC0\MSSQL\DATA\newFileStreamDB_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10% ) WITH FILESTREAM ( NON_TRANSACTED_ACCESS = FULL, DIRECTORY_NAME = N'FileTables' ) GO -- Use the new Database: USE [newFileStreamDB] GO -- Creating a new FileTable CREATE TABLE [dbo].[firstFileTable] AS FILETABLE ON [PRIMARY] FILESTREAM_ON [newFileStreamGroup] WITH ( FILETABLE_DIRECTORY = N'myfirstFileTable', FILETABLE_COLLATE_FILENAME = Latin1_General_CI_AI ) GO
So, what all objects it creates under this new FileTable:
1. The table is created with following predefined Columns:

4. It also creates a FK reference key:

–> Now, when checking under Object Explorer under Tables you won’t see any table (even after refreshing it). But there is a new folder named FileTables, expand it and you can see this table there, shown below:
–> Right click on it and select ‘Explore FileTable Directory’, it will open the folder as shown below:
Here, I’ve manually created 3 files, 1 notepad, 1 powerpoint and 1 word doc. You can also copy files from other locations and paste/drop them here. As you paste/drop files here, SQL Server internally updates the [firstFileTable] file table.
–> We can check the table by simply issuing SELECT statement on the table:
SELECT * FROM dbo.firstFileTable
-- Final Cleanup DROP TABLE dbo.firstFileTable GO
I liked this new feature very much as this will ease the work and reduce the overhead of maintaining files with databases.
In my [next post] you can check how you to use Full Text Search with files stored in FileTables.
Using CROSS APPLY & OUTER APPLY operators with UDFs, Derived tables & XML data
In my previous posts CROSS APPLY vs OUTER APPLY I discussed on the same topic, but didn’t covered much on examples. Here we will see more examples on using APPLY operators, their workarounds and performance.
>> Using APPLY operator with User Define Functions:
USE [AdventureWorks] GO -- Create an "Inline Table-valued Function" that returns Sales Details of a Contact (Customer): CREATE FUNCTION [dbo].[ufnGetSalesInfo](@ContactID int) RETURNS TABLE AS RETURN ( SELECT SalesOrderID, OrderDate, PurchaseOrderNumber, AccountNumber, CustomerID, TotalDue FROM Sales.SalesOrderHeader WHERE ContactID = @ContactID ) GO -- We can use this function with SELECT FROM statement like we do for a table: SELECT * FROM [dbo].[ufnGetSalesInfo](100)
Here, we cannot provide multiple IDs or any variable to make it work for multiple or dynamic Contacts. To return Sales details for multiple Customers we need to JOIN it with Contacts table, but JOINS are now allowed with UFDs, they only work with Tables/Views.
So to JOIN a UDF with a TABLE we need an APPLY operator, check examples below:
-- CROSS APPLY acts as INNER JOIN & will return only matching rows, returns 513 rows: SELECT con.ContactID, con.FirstName, con.LastName, con.EmailAddress, con.Phone, x.* FROM Person.Contact con CROSS APPLY [dbo].[ufnGetSalesInfo](con.ContactID) x WHERE con.ContactID <= 100 -- OUTER APPLY acts as OUTER LEFT JOIN & will return all matching & non-matching rows, returns 529 rows: SELECT con.ContactID, con.FirstName, con.LastName, con.EmailAddress, con.Phone, x.* FROM Person.Contact con OUTER APPLY [dbo].[ufnGetSalesInfo](con.ContactID) x WHERE con.ContactID <= 100 -- Final Cleanup DROP FUNCTION [dbo].[ufnGetSalesInfo] GO
>> Using APPLY operator with Derived tables:
[Scenario]: Pull upto 5 Sales Orders for each Contact (Customer):
This is not possible with JOINS, because the condition to SELECT max 5 Orders is applied after the JOIN is made, we need to add the condition before JOIN. Using APPLY operator we can do this, let’s see how:
-- With Derived Table: SELECT con.ContactID, con.FirstName, con.LastName, con.EmailAddress, con.Phone, x.* FROM Person.Contact con CROSS APPLY (SELECT TOP 5 SalesOrderID, OrderDate, PurchaseOrderNumber, AccountNumber, CustomerID, TotalDue FROM Sales.SalesOrderHeader soh WHERE soh.ContactID = con.ContactID) x WHERE con.ContactID <= 100 -- We can also write the above query by using ROW_NUMBER() function & JOINs instead of the APPLY operator: ;WITH CTE AS ( SELECT SalesOrderID, OrderDate, PurchaseOrderNumber, AccountNumber, ContactID, TotalDue, ROW_NUMBER() OVER(PARTITION BY ContactID ORDER BY ContactID, SalesOrderID) AS ROWNUM FROM Sales.SalesOrderHeader) SELECT con.ContactID, con.FirstName, con.LastName, con.EmailAddress, con.Phone, CTE.* FROM Person.Contact con JOIN CTE cte ON cte.ContactID = con.ContactID WHERE con.ContactID <= 100 AND cte.ROWNUM <= 5 GO
Both of these queries do the same task. So to check which one is more optimized we can check the cost of each query in the batch. So the cost of query using APPLY operator is 84% and query using ROW_NUMBER & JOIN is 16%. Hence the second approach is much more performant & should be considered.
>> Using APPLY operator with XML data:
APPLY operators can also we used with XML data when there is a need to query parent/child nodes, check my previous blog post on this topic.









