<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">

<channel>
	<title>Benjamin Nevarez</title>
	
	<link>http://www.benjaminnevarez.com</link>
	<description>Query Optimization and other SQL Server Topics</description>
	<lastBuildDate>Thu, 03 May 2012 05:39:34 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/BenjaminNevarez" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="benjaminnevarez" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">BenjaminNevarez</feedburner:emailServiceId><feedburner:feedburnerHostname xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://feedburner.google.com</feedburner:feedburnerHostname><item>
		<title>More Undocumented Query Optimizer Trace Flags</title>
		<link>http://www.benjaminnevarez.com/2012/04/more-undocumented-query-optimizer-trace-flags/</link>
		<comments>http://www.benjaminnevarez.com/2012/04/more-undocumented-query-optimizer-trace-flags/#comments</comments>
		<pubDate>Sun, 29 Apr 2012 02:38:17 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[Trace Flags]]></category>
		<category><![CDATA[Undocumented]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2012/04/more-undocumented-query-optimizer-trace-flags/</guid>
		<description><![CDATA[&#160; This is my second post discussing some query optimizer undocumented trace flags (you can read the first one, “Inside the Query Optimizer Memo Structure”, here). Although is not the purpose of this post to discuss the entire optimization process or go into detail on any of the optimization phases, you can read more about [...]]]></description>
			<content:encoded><![CDATA[<p>&nbsp;</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2012/04/clip_image001.gif"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="clip_image001" border="0" alt="clip_image001" src="http://www.benjaminnevarez.com/wp-content/uploads/2012/04/clip_image001_thumb.gif" width="290" height="143"></a></p>
<p>This is my second post discussing some query optimizer undocumented trace flags (you can read the first one, “Inside the Query Optimizer Memo Structure”, <a href="http://www.benjaminnevarez.com/2012/04/inside-the-query-optimizer-memo-structure/">here</a>). Although is not the purpose of this post to discuss the entire optimization process or go into detail on any of the optimization phases, you can read more about it in my book <a href="http://www.simple-talk.com/books/sql-books/inside-the-sql-server-query-optimizer/">Inside the SQL Server Query Optimizer</a>, which you can download for free from the simple-talk website. And same as before, please bear in mind that all these trace flags are undocumented and unsupported, and should not be used on a production environment. You can use them as a way to explore and understand how the query optimizer works.</p>
<p>As mentioned in my previous post, you will first have to enable the trace flag 3604 to redirect the trace output to the client executing the command, in this case to the Messages tab in SQL Server Management Studio.</p>
<pre class="code"><span style="color: blue">DBCC </span>TRACEON<span style="color: gray">(</span>3604<span style="color: gray">)</span></pre>
<p>I’ll start with three trace flags which display logical and physical trees used during the optimization process. First, trace flag 8605 will display the query initial tree representation created by SQL Server. Test if by running</p>
<pre class="code"><span style="color: blue">SELECT </span>e<span style="color: gray">.</span>EmployeeID <span style="color: blue">FROM </span>HumanResources<span style="color: gray">.</span>Employee <span style="color: blue">AS </span>e
<span style="color: gray">INNER JOIN </span>Sales<span style="color: gray">.</span>SalesPerson <span style="color: blue">AS </span>s <span style="color: blue">ON </span>e<span style="color: gray">.</span>EmployeeID <span style="color: gray">= </span>s<span style="color: gray">.</span>SalesPersonID
<span style="color: blue">OPTION </span><span style="color: gray">(</span><span style="color: blue">RECOMPILE</span><span style="color: gray">, </span>QUERYTRACEON 8605<span style="color: gray">)</span></pre>
<p>It will show the following output</p>
<pre class="code">*** Converted Tree: ***
    LogOp_Project QCOL: [e].EmployeeID
        LogOp_Join
            LogOp_Get TBL: HumanResources.Employee(alias TBL: e) HumanResources.Employee TableID=901578250 TableReferenceID=0 IsRow: COL: IsBaseRow1001
            LogOp_Get TBL: Sales.SalesPerson(alias TBL: s) Sales.SalesPerson TableID=1042102753 TableReferenceID=0 IsRow: COL: IsBaseRow1003
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [e].EmployeeID
                ScaOp_Identifier QCOL: [s].SalesPersonID
        AncOp_PrjList </pre>
<p>Trace flag 8606 will display additional logical trees used during the optimization process. Run</p>
<pre class="code"><span style="color: blue">SELECT </span>e<span style="color: gray">.</span>EmployeeID <span style="color: blue">FROM </span>HumanResources<span style="color: gray">.</span>Employee <span style="color: blue">AS </span>e
<span style="color: gray">INNER JOIN </span>Sales<span style="color: gray">.</span>SalesPerson <span style="color: blue">AS </span>s <span style="color: blue">ON </span>e<span style="color: gray">.</span>EmployeeID <span style="color: gray">= </span>s<span style="color: gray">.</span>SalesPersonID
<span style="color: blue">OPTION </span><span style="color: gray">(</span><span style="color: blue">RECOMPILE</span><span style="color: gray">, </span>QUERYTRACEON 8606<span style="color: gray">)</span></pre>
<p>The output shows several different logical trees: input tree, simplified tree, join-collapsed tree, tree before project normalization, and tree after project normalization. These trees will include logical operators only. Part of the output is shown next.</p>
<pre class="code">

*** Input Tree: ***
        LogOp_Project QCOL: [e].EmployeeID
            LogOp_Select
                LogOp_Join
                    LogOp_Get TBL: HumanResources.Employee(alias TBL: e) HumanResources.Employee TableID=901578250 TableReferenceID=0 IsRow: COL: IsBaseRow1001
                    LogOp_Get TBL: Sales.SalesPerson(alias TBL: s) Sales.SalesPerson TableID=1042102753 TableReferenceID=0 IsRow: COL: IsBaseRow1003
                    ScaOp_Const TI(bit,ML=1) XVAR(bit,Not Owned,Value=1)
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL: [e].EmployeeID
                    ScaOp_Identifier QCOL: [s].SalesPersonID
            AncOp_PrjList
*******************

*** Simplified Tree: ***
        LogOp_Join
            LogOp_Get TBL: HumanResources.Employee(alias TBL: e) HumanResources.Employee TableID=901578250 TableReferenceID=0 IsRow: COL: IsBaseRow1001
            LogOp_Get TBL: Sales.SalesPerson(alias TBL: s) Sales.SalesPerson TableID=1042102753 TableReferenceID=0 IsRow: COL: IsBaseRow1003
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [s].SalesPersonID
                ScaOp_Identifier QCOL: [e].EmployeeID
*******************
*** Join-collapsed Tree: ***
        LogOp_Join
            LogOp_Get TBL: HumanResources.Employee(alias TBL: e) HumanResources.Employee TableID=901578250 TableReferenceID=0 IsRow: COL: IsBaseRow1001
            LogOp_Get TBL: Sales.SalesPerson(alias TBL: s) Sales.SalesPerson TableID=1042102753 TableReferenceID=0 IsRow: COL: IsBaseRow1003
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [s].SalesPersonID
                ScaOp_Identifier QCOL: [e].EmployeeID
*******************
*** Tree Before Project Normalization ***
        LogOp_Join
            LogOp_Get TBL: HumanResources.Employee(alias TBL: e) HumanResources.Employee TableID=901578250 TableReferenceID=0 IsRow: COL: IsBaseRow1001
            LogOp_Get TBL: Sales.SalesPerson(alias TBL: s) Sales.SalesPerson TableID=1042102753 TableReferenceID=0 IsRow: COL: IsBaseRow1003
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [s].SalesPersonID
                ScaOp_Identifier QCOL: [e].EmployeeID
*****************************************
*** Tree After Project Normalization ***
        LogOp_Join
            LogOp_Get TBL: HumanResources.Employee(alias TBL: e) HumanResources.Employee TableID=901578250 TableReferenceID=0 IsRow: COL: IsBaseRow1001
            LogOp_Get TBL: Sales.SalesPerson(alias TBL: s) Sales.SalesPerson TableID=1042102753 TableReferenceID=0 IsRow: COL: IsBaseRow1003
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [s].SalesPersonID
                ScaOp_Identifier QCOL: [e].EmployeeID
****************************************
*** Stop search, level 1 ***
</pre>
<p>One interesting example is seeing how a tree is simplified when the query optimizer can detect a contradiction during the simplification phase. The purpose of the simplification stage is to reduce the query tree into a simpler form in order to make the optimization process easier. Contradiction detection is one of several possible simplifications. Following on an example on my book and <a href="http://www.benjaminnevarez.com/2011/06/the-query-optimizer-and-contradiction-detection/">this</a> blog post, run the following query</p>
<pre class="code"><span style="color: blue">SELECT </span><span style="color: gray">* </span><span style="color: blue">FROM </span>HumanResources<span style="color: gray">.</span>Employee
<span style="color: blue">WHERE </span>VacationHours <span style="color: gray">&gt; </span>300
<span style="color: blue">OPTION </span><span style="color: gray">(</span><span style="color: blue">RECOMPILE</span><span style="color: gray">, </span>QUERYTRACEON 8606<span style="color: gray">)</span></pre>
<p>Part of the output is next</p>
<pre class="code">*** Input Tree: ***
        LogOp_Project QCOL: [AdventureWorks].[HumanResources].[Employee].EmployeeID QCOL: [AdventureWorks].[HumanResources].[Employee].NationalIDNumber QCOL: [AdventureWorks].[HumanResources].[Employee].ContactID QCOL: [AdventureWorks].[HumanResources].[Employee].LoginID QCOL: [AdventureWorks].[HumanResources].[Employee].ManagerID QCOL: [AdventureWorks].[HumanResources].[Employee].Title QCOL: [AdventureWorks].[HumanResources].[Employee].BirthDate QCOL: [AdventureWorks].[HumanResources].[Employee].MaritalStatus QCOL: [AdventureWorks].[HumanResources].[Employee].Gender QCOL: [AdventureWorks].[HumanResources].[Employee].HireDate QCOL: [AdventureWorks].[HumanResources].[Employee].SalariedFlag QCOL: [AdventureWorks].[HumanResources].[Employee].VacationHours QCOL: [AdventureWorks].[HumanResources].[Employee].SickLeaveHours QCOL: [AdventureWorks].[HumanResources].[Employee].CurrentFlag QCOL: [AdventureWorks].[HumanResources].[Employee].rowguid QCOL: [AdventureWorks].[HumanResources].[Employee].ModifiedDate
            LogOp_Select
                LogOp_Get TBL: HumanResources.Employee HumanResources.Employee TableID=901578250 TableReferenceID=0 IsRow: COL: IsBaseRow1001
                ScaOp_Comp x_cmpGt
                    ScaOp_Identifier QCOL: [AdventureWorks].[HumanResources].[Employee].VacationHours
                    ScaOp_Const TI(smallint,ML=2) XVAR(smallint,Not Owned,Value=300)
            AncOp_PrjList
*******************
*** Simplified Tree: ***
        LogOp_ConstTableGet (0) COL: Chk1000  COL: IsBaseRow1001  QCOL: [AdventureWorks].[HumanResources].[Employee].EmployeeID QCOL: [AdventureWorks].[HumanResources].[Employee].NationalIDNumber QCOL: [AdventureWorks].[HumanResources].[Employee].ContactID QCOL: [AdventureWorks].[HumanResources].[Employee].LoginID QCOL: [AdventureWorks].[HumanResources].[Employee].ManagerID QCOL: [AdventureWorks].[HumanResources].[Employee].Title QCOL: [AdventureWorks].[HumanResources].[Employee].BirthDate QCOL: [AdventureWorks].[HumanResources].[Employee].MaritalStatus QCOL: [AdventureWorks].[HumanResources].[Employee].Gender QCOL: [AdventureWorks].[HumanResources].[Employee].HireDate QCOL: [AdventureWorks].[HumanResources].[Employee].SalariedFlag QCOL: [AdventureWorks].[HumanResources].[Employee].VacationHours QCOL: [AdventureWorks].[HumanResources].[Employee].SickLeaveHours QCOL: [AdventureWorks].[HumanResources].[Employee].CurrentFlag QCOL: [AdventureWorks].[HumanResources].[Employee].rowguid QCOL: [AdventureWorks].[HumanResources].[Employee].ModifiedDate
*******************
*** Join-collapsed Tree: ***
        LogOp_ConstTableGet (0) COL: Chk1000  COL: IsBaseRow1001  QCOL: [AdventureWorks].[HumanResources].[Employee].EmployeeID QCOL: [AdventureWorks].[HumanResources].[Employee].NationalIDNumber QCOL: [AdventureWorks].[HumanResources].[Employee].ContactID QCOL: [AdventureWorks].[HumanResources].[Employee].LoginID QCOL: [AdventureWorks].[HumanResources].[Employee].ManagerID QCOL: [AdventureWorks].[HumanResources].[Employee].Title QCOL: [AdventureWorks].[HumanResources].[Employee].BirthDate QCOL: [AdventureWorks].[HumanResources].[Employee].MaritalStatus QCOL: [AdventureWorks].[HumanResources].[Employee].Gender QCOL: [AdventureWorks].[HumanResources].[Employee].HireDate QCOL: [AdventureWorks].[HumanResources].[Employee].SalariedFlag QCOL: [AdventureWorks].[HumanResources].[Employee].VacationHours QCOL: [AdventureWorks].[HumanResources].[Employee].SickLeaveHours QCOL: [AdventureWorks].[HumanResources].[Employee].CurrentFlag QCOL: [AdventureWorks].[HumanResources].[Employee].rowguid QCOL: [AdventureWorks].[HumanResources].[Employee].ModifiedDate
*******************
*** Tree Before Project Normalization ***
        LogOp_ConstTableGet (0) COL: Chk1000  COL: IsBaseRow1001  QCOL: [AdventureWorks].[HumanResources].[Employee].EmployeeID QCOL: [AdventureWorks].[HumanResources].[Employee].NationalIDNumber QCOL: [AdventureWorks].[HumanResources].[Employee].ContactID QCOL: [AdventureWorks].[HumanResources].[Employee].LoginID QCOL: [AdventureWorks].[HumanResources].[Employee].ManagerID QCOL: [AdventureWorks].[HumanResources].[Employee].Title QCOL: [AdventureWorks].[HumanResources].[Employee].BirthDate QCOL: [AdventureWorks].[HumanResources].[Employee].MaritalStatus QCOL: [AdventureWorks].[HumanResources].[Employee].Gender QCOL: [AdventureWorks].[HumanResources].[Employee].HireDate QCOL: [AdventureWorks].[HumanResources].[Employee].SalariedFlag QCOL: [AdventureWorks].[HumanResources].[Employee].VacationHours QCOL: [AdventureWorks].[HumanResources].[Employee].SickLeaveHours QCOL: [AdventureWorks].[HumanResources].[Employee].CurrentFlag QCOL: [AdventureWorks].[HumanResources].[Employee].rowguid QCOL: [AdventureWorks].[HumanResources].[Employee].ModifiedDate
*****************************************
*** Tree After Project Normalization ***
        LogOp_ConstTableGet (0) COL: Chk1000  COL: IsBaseRow1001  QCOL: [AdventureWorks].[HumanResources].[Employee].EmployeeID QCOL: [AdventureWorks].[HumanResources].[Employee].NationalIDNumber QCOL: [AdventureWorks].[HumanResources].[Employee].ContactID QCOL: [AdventureWorks].[HumanResources].[Employee].LoginID QCOL: [AdventureWorks].[HumanResources].[Employee].ManagerID QCOL: [AdventureWorks].[HumanResources].[Employee].Title QCOL: [AdventureWorks].[HumanResources].[Employee].BirthDate QCOL: [AdventureWorks].[HumanResources].[Employee].MaritalStatus QCOL: [AdventureWorks].[HumanResources].[Employee].Gender QCOL: [AdventureWorks].[HumanResources].[Employee].HireDate QCOL: [AdventureWorks].[HumanResources].[Employee].SalariedFlag QCOL: [AdventureWorks].[HumanResources].[Employee].VacationHours QCOL: [AdventureWorks].[HumanResources].[Employee].SickLeaveHours QCOL: [AdventureWorks].[HumanResources].[Employee].CurrentFlag QCOL: [AdventureWorks].[HumanResources].[Employee].rowguid QCOL: [AdventureWorks].[HumanResources].[Employee].ModifiedDate
</pre>
<p>In this case the query optimizer makes use of an existing check constraint to conclude that no records qualify for the predicate VacationHours &gt; 300, replacing the entire tree with a LogOp_ConstTableGet logical operator. If you try displaying an output tree, as discussed next, you will get a PhyOp_ConstTableScan physical operator, and you will get a constant scan operator on the final execution plan. You can see a different behavior if you try the same query with a predicate like VacationHours &gt; 20.</p>
<p>Trace flag 8607 shows the optimization output tree. Try the following sentence</p>
<pre class="code"><span style="color: blue">SELECT </span>e<span style="color: gray">.</span>EmployeeID <span style="color: blue">FROM </span>HumanResources<span style="color: gray">.</span>Employee <span style="color: blue">AS </span>e
<span style="color: gray">INNER JOIN </span>Sales<span style="color: gray">.</span>SalesPerson <span style="color: blue">AS </span>s <span style="color: blue">ON </span>e<span style="color: gray">.</span>EmployeeID <span style="color: gray">= </span>s<span style="color: gray">.</span>SalesPersonID
<span style="color: blue">OPTION </span><span style="color: gray">(</span><span style="color: blue">RECOMPILE</span><span style="color: gray">, </span>QUERYTRACEON 8607<span style="color: gray">)</span></pre>
<p>Notice that this time we have physical operators and the output tree is closer to the final execution plan.</p>
<pre class="code">****************************************
*** Output Tree: ***
        PhyOp_Apply lookup TBL: HumanResources.Employee (0) (x_jtInner)
            PhyOp_Range TBL: Sales.SalesPerson(alias TBL: s)(2) ASC  Bmk ( QCOL: [s].SalesPersonID) IsRow: COL: IsBaseRow1003
            PhyOp_Range TBL: HumanResources.Employee(alias TBL: e)(1) ASC  Bmk ( QCOL: [e].EmployeeID) IsRow: COL: IsBaseRow1001
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL: [s].SalesPersonID
                    ScaOp_Identifier QCOL: [e].EmployeeID
********************
** Query marked as Cachable
********************
</pre>
<p>Trace flag 8675 shows the query optimization phases for a specific optimization along with some other information like cost estimation, tasks, etc. You may want to test it with complex queries to see different optimization phases like in the following example</p>
<pre class="code"><span style="color: blue">SELECT I</span><span style="color: gray">.CustomerID, C.FirstName, C.LastName, A.AddressLine1, A.City,
SP.Name </span><span style="color: blue">AS State</span><span style="color: gray">, CR.Name </span><span style="color: blue">AS CountryRegion
FROM Person</span><span style="color: gray">.Contact </span><span style="color: blue">AS C
</span><span style="color: gray">JOIN Sales.Individual </span><span style="color: blue">AS I ON C</span><span style="color: gray">.ContactID = I.ContactID
JOIN Sales.CustomerAddress </span><span style="color: blue">AS CA ON CA</span><span style="color: gray">.CustomerID = I.CustomerID
JOIN Person.</span><span style="color: blue">Address AS A ON A</span><span style="color: gray">.AddressID = CA.AddressID
JOIN Person.StateProvince SP </span><span style="color: blue">ON
SP</span><span style="color: gray">.StateProvinceID = A.StateProvinceID
JOIN Person.CountryRegion CR </span><span style="color: blue">ON
CR</span><span style="color: gray">.CountryRegionCode = SP.CountryRegionCode
</span><span style="color: blue">ORDER BY I</span><span style="color: gray">.CustomerID
</span><span style="color: blue">OPTION </span><span style="color: gray">(</span><span style="color: blue">RECOMPILE</span><span style="color: gray">, QUERYTRACEON 8675)</pre>
<p></span></p>
<p>It shows the following output</p>
<pre class="code">End of simplification, time: 0.003 net: 0.003 total: 0.003 net: 0.003
end exploration, tasks: 275 no total cost time: 0.005 net: 0.005 total: 0.009 net: 0.009
end exploration, tasks: 642 no total cost time: 0.003 net: 0.003 total: 0.012 net: 0.012
end search(0),  cost: 9.04 tasks: 681 time: 0 net: 0 total: 0.013 net: 0.013
end exploration, tasks: 1465 Cost = 9.04 time: 0.004 net: 0.004 total: 0.017 net: 0.017
end exploration, tasks: 2518 Cost = 9.04 time: 0.007 net: 0.007 total: 0.024 net: 0.024
end search(1),  cost: 5.86879 tasks: 2609 time: 0 net: 0 total: 0.025 net: 0.025
end exploration, tasks: 2610 Cost = 5.86879 time: 0 net: 0 total: 0.025 net: 0.025
end exploration, tasks: 5170 Cost = 5.86879 time: 0.021 net: 0.021 total: 0.047 net: 0.047
end search(1),  cost: 5.86248 tasks: 5469 time: 0.001 net: 0.001 total: 0.048 net: 0.048
end exploration, tasks: 5756 Cost = 5.86248 time: 0.001 net: 0.001 total: 0.05 net: 0.05
end exploration, tasks: 6434 Cost = 5.86248 time: 0.005 net: 0.005 total: 0.055 net: 0.055
end search(2),  cost: 5.84575 tasks: 7092 time: 0.004 net: 0.004 total: 0.059 net: 0.059
End of post optimization rewrite, time: 0 net: 0 total: 0.059 net: 0.059
End of query plan compilation, time: 0 net: 0 total: 0.06 net: 0.06
</pre>
<p>Note the optimization phases search(0), search(1) and search(2). Also, as mentioned in my book, you need at least three tables to qualify for search 0, so the following query will go directly to search 1 (you can also test this with trace flag 2372 as described later):</p>
<pre class="code"><span style="color: blue">SELECT </span><span style="color: gray">* </span><span style="color: blue">FROM </span>HumanResources<span style="color: gray">.</span>Employee
<span style="color: blue">WHERE </span>ManagerID <span style="color: gray">= </span>12
<span style="color: blue">OPTION </span><span style="color: gray">(</span><span style="color: blue">RECOMPILE</span><span style="color: gray">, </span>QUERYTRACEON 8675<span style="color: gray">)</span></pre>
<p>Trace flags 2372 and 2373 show memory utilization during the optimization process but we can also use them to gain information about the activities the query optimizer is performing for a particular query. Trace flag 2372 shows memory utilization during the different optimization stages. Same as before, more complicated queries will show more optimization stages.</p>
<pre class="code"><span style="color: blue">SELECT </span>I<span style="color: gray">.</span>CustomerID<span style="color: gray">, </span>C<span style="color: gray">.</span>FirstName<span style="color: gray">, </span>C<span style="color: gray">.</span>LastName<span style="color: gray">, </span>A<span style="color: gray">.</span>AddressLine1<span style="color: gray">, </span>A<span style="color: gray">.</span>City<span style="color: gray">,
</span>SP<span style="color: gray">.</span>Name <span style="color: blue">AS State</span><span style="color: gray">, </span>CR<span style="color: gray">.</span>Name <span style="color: blue">AS </span>CountryRegion
<span style="color: blue">FROM </span>Person<span style="color: gray">.</span>Contact <span style="color: blue">AS </span>C
<span style="color: gray">JOIN </span>Sales<span style="color: gray">.</span>Individual <span style="color: blue">AS </span>I <span style="color: blue">ON </span>C<span style="color: gray">.</span>ContactID <span style="color: gray">= </span>I<span style="color: gray">.</span>ContactID
<span style="color: gray">JOIN </span>Sales<span style="color: gray">.</span>CustomerAddress <span style="color: blue">AS </span>CA <span style="color: blue">ON </span>CA<span style="color: gray">.</span>CustomerID <span style="color: gray">= </span>I<span style="color: gray">.</span>CustomerID
<span style="color: gray">JOIN </span>Person<span style="color: gray">.</span><span style="color: blue">Address AS </span>A <span style="color: blue">ON </span>A<span style="color: gray">.</span>AddressID <span style="color: gray">= </span>CA<span style="color: gray">.</span>AddressID
<span style="color: gray">JOIN </span>Person<span style="color: gray">.</span>StateProvince SP <span style="color: blue">ON
</span>SP<span style="color: gray">.</span>StateProvinceID <span style="color: gray">= </span>A<span style="color: gray">.</span>StateProvinceID
<span style="color: gray">JOIN </span>Person<span style="color: gray">.</span>CountryRegion CR <span style="color: blue">ON
</span>CR<span style="color: gray">.</span>CountryRegionCode <span style="color: gray">= </span>SP<span style="color: gray">.</span>CountryRegionCode
<span style="color: blue">ORDER BY </span>I<span style="color: gray">.</span>CustomerID
<span style="color: blue">OPTION </span><span style="color: gray">(</span><span style="color: blue">RECOMPILE</span><span style="color: gray">, </span>QUERYTRACEON 2372<span style="color: gray">)</span></pre>
<p>The output is next</p>
<pre class="code">Memory before NNFConvert: 13
Memory after NNFConvert: 14
Memory before project removal: 15
Memory after project removal: 15
Memory before simplification: 15
Memory after simplification: 35
Memory before heuristic join reordering: 35
Memory after heuristic join reordering: 46
Memory before project normalization: 46
Memory after project normalization: 46
Memory before stage TP: 46
Memory after stage TP: 81
Memory before stage QuickPlan: 81
Memory after stage QuickPlan: 144
Memory before stage Full: 144
Memory after stage Full: 156
Memory before copy out: 156
Memory after copy out: 157</pre>
<p>Notice that “stage TP” or transaction processing phase is the same as search(0) shown before with trace flag 8675. In the same way, “stage QuickPlan” is the search(1) and “stage Full” is search(2).</p>
<p>Trace flag 2373 shows memory utilization while applying optimization rules and deriving properties.</p>
<pre class="code"><span style="color: blue">SELECT </span>e<span style="color: gray">.</span>EmployeeID <span style="color: blue">FROM </span>HumanResources<span style="color: gray">.</span>Employee <span style="color: blue">AS </span>e
<span style="color: gray">INNER JOIN </span>Sales<span style="color: gray">.</span>SalesPerson <span style="color: blue">AS </span>s <span style="color: blue">ON </span>e<span style="color: gray">.</span>EmployeeID <span style="color: gray">= </span>s<span style="color: gray">.</span>SalesPersonID
<span style="color: blue">OPTION </span><span style="color: gray">(</span><span style="color: blue">RECOMPILE</span><span style="color: gray">, </span>QUERYTRACEON 2373<span style="color: gray">)</span></pre>
<p>Part of the output is next</p>
<pre class="code">Memory before rule IJtoIJSEL: 14
Memory after rule IJtoIJSEL: 14
Memory before rule MatchGet: 14
Memory after rule MatchGet: 14
Memory before rule MatchGet: 14
Memory after rule MatchGet: 14
Memory before rule JoinToIndexOnTheFly: 14
Memory after rule JoinToIndexOnTheFly: 14
Memory before rule JoinCommute: 14
Memory after rule JoinCommute: 14
Memory before rule JoinToIndexOnTheFly: 14
Memory after rule JoinToIndexOnTheFly: 14
Memory before rule JNtoIdxLookup: 14</pre>
<p>Finally, trace 8757 can be used to skip the trivial plan optimization and basically force a full optimization. As a reminder, the trivial plan optimization is used for very simple queries that don&#8217;t require any cost estimation decision. For example, the following query will produce a trivial plan, which you can verify by looking at the optimization level or StatementOptmLevel property in your execution plan</p>
<pre class="code"><span style="color: blue">SELECT </span><span style="color: gray">* </span><span style="color: blue">FROM </span>dbo<span style="color: gray">.</span>DatabaseLog</pre>
<p>By applying trace flag 8757, the trivial plan optimization will be skipped as you can verify by running the following query and once again verifying the optimization level property. </p>
<pre class="code"><span style="color: blue">SELECT </span><span style="color: gray">* </span><span style="color: blue">FROM </span>dbo<span style="color: gray">.</span>DatabaseLog
<span style="color: blue">OPTION </span><span style="color: gray">(</span>QUERYTRACEON 8757<span style="color: gray">)</span></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2012/04/more-undocumented-query-optimizer-trace-flags/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Inside the Query Optimizer Memo Structure</title>
		<link>http://www.benjaminnevarez.com/2012/04/inside-the-query-optimizer-memo-structure/</link>
		<comments>http://www.benjaminnevarez.com/2012/04/inside-the-query-optimizer-memo-structure/#comments</comments>
		<pubDate>Thu, 26 Apr 2012 06:39:43 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Memo]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[Trace Flags]]></category>
		<category><![CDATA[Undocumented]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2012/04/inside-the-query-optimizer-memo-structure/</guid>
		<description><![CDATA[I just learned a few query-optimizer-related undocumented trace flags from my friend Dmitry Pilugin, who blogged about them in his blog in Russian, SomewhereSomehow&#8217;s Blog, and asked me if I could be interested in posting something in English. Some of these interesting trace flags allow us to see the contents of the memo structure, something [...]]]></description>
			<content:encoded><![CDATA[<p>I just learned a few query-optimizer-related undocumented trace flags from my friend Dmitry Pilugin, who blogged about them in his blog in Russian, <a href="http://www.sql.ru/blogs/somewheresomehow">SomewhereSomehow&#8217;s Blog</a>, and asked me if I could be interested in posting something in English. Some of these interesting trace flags allow us to see the contents of the memo structure, something I was trying to find while writing my book <a href="http://www.simple-talk.com/books/sql-books/inside-the-sql-server-query-optimizer/">Inside the SQL Server Query Optimizer</a>.
<p>But first, a quick reminder of what the memo structure is. The memo is a search data structure that is used to store the alternatives generated and analyzed by the SQL Server query optimizer. These alternatives can be logical or physical operators and are organized into groups such that each alternative in the same group produces the same results. The query optimizer first copies the original query tree&#8217;s logical expressions into the memo structure, placing each operator from the query tree in its own group, and then triggers the entire optimization process. During this process, transformation rules are applied to generate all the alternatives, starting with these initial logical expressions. As the transformation rules produce new alternatives, these are added to their equivalent groups. Transformation rules may also produce a new expression which is not equivalent to any existing group, and which causes a new group to be created. A new memo structure is created for each optimization.
<p>But instead of trying to explain the basics of how the memo structure works you can refer to my book, Inside the SQL Server Query Optimizer, which you can download for free from the <a href="http://www.simple-talk.com/books/sql-books/inside-the-sql-server-query-optimizer/">simple-talk</a> website. In this post I will show you how to see the contents of the memo structure. First enable the trace flag 3604 to redirect the trace output to the client executing the command, in this case SQL Server Management Studio.</p>
<pre class="code"><span style="color: blue">DBCC </span>TRACEON<span style="color: gray">(</span>3604<span style="color: gray">)</span></pre>
<p>Next I will be using the undocumented trace flags 8608 and 8615 but, although I can still use DBCC TRACEON, this time I will use the also undocumented QUERYTRACEON query hint. Please bear in mind that all these are undocumented SQL Server statements and should not be used on a production environment. </p>
<p>The first trace flag, 8608, will show the initial memo structure, which you can see in the Messages tab of the Query window in Management Studio. </p>
<pre class="code"><span style="color: blue">SELECT </span>e<span style="color: gray">.</span>EmployeeID <span style="color: blue">FROM </span>HumanResources<span style="color: gray">.</span>Employee <span style="color: blue">AS </span>e
<span style="color: gray">INNER JOIN </span>Sales<span style="color: gray">.</span>SalesPerson <span style="color: blue">AS </span>s <span style="color: blue">ON </span>e<span style="color: gray">.</span>EmployeeID <span style="color: gray">= </span>s<span style="color: gray">.</span>SalesPersonID
<span style="color: blue">OPTION </span><span style="color: gray">(</span>QUERYTRACEON 8608<span style="color: gray">)</span></pre>
<p>Running the previous query will show the following output</p>
<pre class="code">--- Initial Memo Structure ---
Root Group 5: Card=8.33333 (Max=10000, Min=0)
   0 LogOp_Join 0 1 4
Group 4:
   0 ScaOp_Comp  2 3
Group 3:
   0 ScaOp_Identifier
Group 2:
   0 ScaOp_Identifier
Group 1: Card=17 (Max=10000, Min=0)
   0 LogOp_Get
Group 0: Card=290 (Max=10000, Min=0)
   0 LogOp_Get</pre>
<p>The next query uses trace flag 8615 to display the final memo structure </p>
<pre class="code"><span style="color: blue">SELECT e</span><span style="color: gray">.EmployeeID </span><span style="color: blue">FROM HumanResources</span><span style="color: gray">.Employee </span><span style="color: blue">AS e
</span><span style="color: gray">INNER JOIN Sales.SalesPerson </span><span style="color: blue">AS s ON e</span><span style="color: gray">.EmployeeID = s.SalesPersonID
</span><span style="color: blue">OPTION </span><span style="color: gray">(QUERYTRACEON 8615)</pre>
<p></span></p>
<p>which shows the following output</p>
<pre class="code">--- Final Memo Structure ---
Group 9: Card=1 (Max=1, Min=0)
   0 LogOp_SelectIdx 8 4
Group 8: Card=17 (Max=10000, Min=0)
   0 LogOp_GetIdx
Group 7: Card=1 (Max=1, Min=0)
   1 PhyOp_Range 1 ASC 4.0 Cost(RowGoal 0,ReW 0,ReB 16,Dist 17,Total 17)= 0.0058127
   0 LogOp_SelectIdx 6 4
Group 6: Card=290 (Max=10000, Min=0)
   0 LogOp_GetIdx
Root Group 5: Card=8.33333 (Max=10000, Min=0)
   2 PhyOp_Applyx_jtInner 1.2 7.1 Cost(RowGoal 0,ReW 0,ReB 0,Dist 0, …)= 0.00918446
   1 LogOp_Join 1 0 4
   0 LogOp_Join 0 1 4
Group 4:
   0 ScaOp_Comp  2.0 3.0  Cost(RowGoal 0,ReW 0,ReB 0,Dist 0,Total 0)= 3
Group 3:
   0 ScaOp_Identifier   Cost(RowGoal 0,ReW 0,ReB 0,Dist 0,Total 0)= 1
Group 2:
   0 ScaOp_Identifier   Cost(RowGoal 0,ReW 0,ReB 0,Dist 0,Total 0)= 1
Group 1: Card=17 (Max=10000, Min=0)
   3 PhyOp_Range 1 ASC   Cost(RowGoal 0,ReW 0,ReB 0,Dist 0,Total 0)= 0.0033007
   2 PhyOp_Range 2 ASC   Cost(RowGoal 0,ReW 0,ReB 0,Dist 0,Total 0)= 0.0033007
   0 LogOp_Get
Group 0: Card=290 (Max=10000, Min=0)
   0 LogOp_Get</pre>
<p>You can notice that among other things the output shows information about operators, groups, cardinality information (Card) and cost estimation. I will be discussing a few more undocumented trace flags on a second post very soon.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2012/04/inside-the-query-optimizer-memo-structure/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Speaking at 24 Hours of PASS and SQL Server Connections</title>
		<link>http://www.benjaminnevarez.com/2012/03/speaking-at-24-hours-of-pass-and-sql-server-connections/</link>
		<comments>http://www.benjaminnevarez.com/2012/03/speaking-at-24-hours-of-pass-and-sql-server-connections/#comments</comments>
		<pubDate>Tue, 20 Mar 2012 07:18:19 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Speaking]]></category>
		<category><![CDATA[24 Hours of PASS]]></category>
		<category><![CDATA[SQL Server Connections]]></category>
		<category><![CDATA[SQLSaturday]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2012/03/speaking-at-24-hours-of-pass-and-sql-server-connections/</guid>
		<description><![CDATA[I can&#8217;t believe I haven&#8217;t posted anything in my blog in months. Just after getting back from the PASS Summit 2011 I got involved in several SQL Server projects and I’ve been really busy since then. By the way, I just recently saw my first article published in SQL Server Pro (formerly SQL Server Magazine). [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2012/03/clip_image001.jpg"><img style="display: block; float: none; margin-left: auto; margin-right: auto; border-width: 0px;" title="clip_image001" src="http://www.benjaminnevarez.com/wp-content/uploads/2012/03/clip_image001_thumb.jpg" alt="clip_image001" width="402" height="92" border="0" /></a></p>
<p>I can&#8217;t believe I haven&#8217;t posted anything in my blog in months. Just after getting back from the <a href="http://www.benjaminnevarez.com/2011/11/pass-summit-2011-recap/">PASS Summit 2011</a> I got involved in several SQL Server projects and I’ve been really busy since then. By the way, I just recently saw my first article published in SQL Server Pro (formerly SQL Server Magazine). The article, “Improve the Performance of Data Warehouse Queries with Columnstore Indexes”, can also be seen online <a href="http://www.sqlmag.com/article/sqlserverdenali/data-warehouse-queries-columnstore-indexes-141712">here</a> at the SQL Server Pro website. Also, just a few days ago I was selected as a 2012 Idera ACE so I am really excited to be part of this team. In addition, I will be speaking at some SQL Server events this month.</p>
<p>First, I will be presenting a session at <a href="http://www.sqlpass.org/24hours/spring2012/Home.aspx">24 Hours of PASS</a>, which is a free event with 24 consecutive SQL Server webcasts and it is scheduled for March 21st. My session, <a href="http://www.sqlpass.org/24hours/spring2012/SessionsbySchedule/SessionDetails.aspx?sid=2586">Improving the Performance of your Data Warehouse Queries with Columnstore Indexes</a> will be the last session of the event and it is scheduled for 4:00pm Pacific Time. This edition of 24 Hours of PASS will feature close captioning in 15 different languages and you can still register <a href="http://www.sqlpass.org/24hours/spring2012/Home.aspx">here</a>.</p>
<p>Three days after 24 Hours of PASS I will be presenting four sessions at our local <a href="http://sqlsaturday.com/120/eventhome.aspx">SQLSaturday #120</a> in Orange County, CA. SQLSaturday #120 will be held at the Goldenwest College in Huntington Beach on March 24th and will include many great speakers like Grant Fritchey, Kendra Little, Jeremiah Peschka, Denny Cherry, Lynn Langit, Audrey Hammonds, and Carlos Bossy, just to name a few.</p>
<p>Just after SQLSaturday #120 I will be headed to Las Vegas, NV to participate on my first <a href="http://www.devconnections.com/shows/sp2012/default.aspx?s=185">SQL Server Connections</a> conference. The Spring 2012 edition of SQL Server Connections will run from March 26th to 29th and will host one of the SQL Server 2012 launch events. I will be presenting my session <a href="http://www.devconnections.com/shows/sp2012/sessions.aspx?s=185">Top 10 Query Optimizer Topics for Better Performance</a> on Thursday afternoon.</p>
<p>As always, I look forward to meeting lots of SQL Server professionals on these events.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2012/03/speaking-at-24-hours-of-pass-and-sql-server-connections/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PASS Summit 2011 Recap</title>
		<link>http://www.benjaminnevarez.com/2011/11/pass-summit-2011-recap/</link>
		<comments>http://www.benjaminnevarez.com/2011/11/pass-summit-2011-recap/#comments</comments>
		<pubDate>Thu, 03 Nov 2011 07:28:55 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Speaking]]></category>
		<category><![CDATA[PASS Summit]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2011/11/pass-summit-2011-recap/</guid>
		<description><![CDATA[This is my late review of the PASS Summit 2011, which was hosted once again at the Washington State Convention &#38; Trade Center in Seattle a couple of weeks ago. The PASS Summit is the largest SQL Server event in the world and this year was scheduled to run 189 sessions with 204 speakers from [...]]]></description>
			<content:encoded><![CDATA[</p>
<p>This is my late review of the PASS Summit 2011, which was hosted once again at the Washington State Convention &amp; Trade Center in Seattle a couple of weeks ago. The PASS Summit is the largest SQL Server event in the world and this year was scheduled to run 189 sessions with 204 speakers from all over the world. In addition to being my ninth year attending this conference I was also excited that this was my fourth year speaking there as well.</p>
<p>So the week started for me on Monday morning flying from Los Angeles to Seattle. Later in the afternoon I went to the registration area where I started meeting a lot of people of the SQL Server community just to end the day with some SQL Karaoke at the Bush Garden. I was originally scheduled to attend Dr. David DeWitt’s pre-con “A Peek Inside an RDBMS” on Tuesday but unfortunately it was cancelled and I didn’t attend any other pre-con that day. The Welcome Reception was held on Tuesday night and included the traditional Quiz Bowl, which I show in the next picture. From the Welcome Reception some of us went to the Speakers and Volunteers Party at hosted The Garage.</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/11/clip_image0023.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image002[3]" border="0" alt="clip_image002[3]" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/11/clip_image0023_thumb.jpg" width="620" height="463" /></a></p>
<p>On Wednesday, the first keynote of the conference started with Rushabh Mehta, PASS President, and was followed by Ted Kummert, Microsoft Senior Vice President, Business Platform Division. Not a big surprise but Ted made it official that SQL Server code-named Denali will now be named SQL Server 2012 and will be released on the first half of the next calendar year. Among other things, Ted talked about Big Data and announced that Microsoft will be supporting Hadoop and it is planning to deliver Apache Hadoop-based distributions for both Windows Server and Windows Azure. He also mentioned that SQL Server and SQL Server Parallel Data Warehouse connectors for Apache Hadoop had being <a href="http://www.microsoft.com/download/en/details.aspx?id=27584">released</a> just the previous week. By the way, all three keynotes of the conference were broadcasted live and you can still watch them on demand at the <a href="http://www.sqlpass.org/summit/2011/Live/LiveStreaming.aspx">PASS website</a>.</p>
<p>I had to leave the keynote early to find the room and prepare everything for my first session at the conference. My session, “Inside the SQL Server Query Optimizer”, was scheduled for a room with capacity for 520 people and next is a picture taken just a few minutes before I started presenting. I was even asked to sign a copy of my <a href="http://www.amazon.com/Inside-SQL-Server-Query-Optimizer/dp/1906434603">book</a> just before my session <img src='http://www.benjaminnevarez.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/11/clip_image0043.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image004[3]" border="0" alt="clip_image004[3]" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/11/clip_image0043_thumb.jpg" width="620" height="468" /></a></p>
<p>The rest of Wednesday after lunch I went to a couple of sessions. With 15 sessions running concurrently choosing which ones to attend was not an easy task. I attended Bob Ward’s half-day session “Inside Tempdb” and later went to Aaron Bertrand’s “What’s new in SQL Server code-named Denali – Engine and Tools”. Half-day sessions were new at the PASS Summit this year. On Wednesday night I went to the SolidQ Party, which I’ve also attended in previous years.</p>
<p>I was not presenting any session on Thursday so I just went to the keynote and spent the rest of the day attending sessions. The keynote included Bill Graziano, PASS Executive Vice President, Finance, and Quentin Clark, Microsoft Corporate Vice President. Quentin started talking about SQL Server 2012 and warned that would be impossible to talk about all the hundreds of new features and improvements of this new version so instead he went into explaining his 12 favorite areas of value, which he called the “Fantastic 12 of SQL Server 2012”. He spent the rest of the keynote talking about the most important new SQL Server 2012 features grouped into these 12 areas of value. After the keynote, my choice for session in the morning was Itzik Ben-Gan “Bug or Feature?”. </p>
<p>During lunch I was on the same table as Eric Hanson, Principal Program Manager Lead, Query Processing and Storage at Microsoft, so I used the opportunity to talk to him about the new columnstore indexes feature. After lunch I went to the session “Physical Join Operators” by Ami Levin, which was very entertaining. I continued with Rob Farley “Joins, Sargability and the Evils of Residualiciousness” to finally close the day with “SQLCAT: SQL Server HA and DR Design Architectures and Best Practices” with Sanjay Mishra, Justin Erickson and Mike Weiner. </p>
<p>I spent Thursday night mostly at the Community Appreciation Party, hosted again this year at GameWorks. I met many people there and ended in the same table with Lubor Kollar who mentioned that he is back to work with the SQL Server Core Engine development team, responsible for query optimization, query execution, and data warehousing.</p>
<p>The keynote on Friday started with Rick Heighes, PASS Vice President, Marketing, followed by David DeWitt, Microsoft Technical Fellow, Data and Storage Platform Division. Dr. DeWitt’s keynote, entitled &quot;Big Data: What&#8217;s the Big Deal?&quot;, was one of the most anticipated sessions of the conference and focused on Hadoop and its ecosystem of software tools. He concluded his keynote saying that relational databases and Hadoop are designed to meet different needs and they can complement each other so database professionals need to make sure that both technologies work together the best they can. After the keynote I attended Adam Machanic “Query Tuning Mastery: Zend and the Art of Workspace Memory”.</p>
<p>Something amazing for me during the conference was seeing my book <a href="http://www.amazon.com/Inside-SQL-Server-Query-Optimizer/dp/1906434603">Inside the SQL Server Query Optimizer</a> available at the PASS Bookstore. Next I am including a picture taken on Friday when there were only a few copies left <img src='http://www.benjaminnevarez.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />  -It is the one with the beacon in the cover. I also saw my book at the Red Gate booth and, of course, I gave away a couple of copies at each of my sessions.</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/11/clip_image0063.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image006[3]" border="0" alt="clip_image006[3]" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/11/clip_image0063_thumb.jpg" width="620" height="468" /></a></p>
<p>Just after lunch on Friday I went to see Susan Price and Murshed Zaman presenting “Project Apollo: How to use Columnstore Indexes to Revolutionize Query Performance on your Data Warehouse”. Susan presented a similar session last year but I was not able to attend it as I was also presenting a session at the same time. I stayed in the same room to learn more about the columnstore indexes with Wayne Snyder’s session “Using Columnstore/Vertipaq indexes in SQL Server code-named Denali” but again I had to leave early as I was speaking next.</p>
<p>Then it was time for presenting my last session, “Parameter Sniffing: the Query Optimizer vs. the Plan Cache”. Since this was the last round of sessions at the conference I was wondering, same as other speakers I met at the Speaker Ready room, if nobody or very few people would show up. Fortunately the attendance for my session was good enough and the presentation went really well. Finally I spend Friday night with dinner at the Tap House with several people of the SQL Server community.</p>
<p>Something interesting I saw on Saturday afternoon while going for lunch to the Hard Rock Cafe was the people of the Occupy Seattle movement on the streets of Seattle. I took several pictures there, one is shown next.</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/11/clip_image0083.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image008[3]" border="0" alt="clip_image008[3]" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/11/clip_image0083_thumb.jpg" width="620" height="468" /></a></p>
<p>I flew back to Los Angeles on Saturday night and interestingly enough I was scheduled to speak again at the <a href="http://www.socalcodecamp.com/">SoCal Code Camp</a> at the University of Southern California the next day, where I presented the same two sessions I did at the PASS Summit. </p>
<p>In summary, this was another excellent PASS Summit and I can’t wait for the next one, which is already scheduled for Seattle in November 6-9, 2012. See you then.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2011/11/pass-summit-2011-recap/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Parameter Sniffing and Plan-reuse-affecting SET Options</title>
		<link>http://www.benjaminnevarez.com/2011/09/parameter-sniffing-and-plan-reuse-affecting-set-options/</link>
		<comments>http://www.benjaminnevarez.com/2011/09/parameter-sniffing-and-plan-reuse-affecting-set-options/#comments</comments>
		<pubDate>Wed, 28 Sep 2011 07:07:21 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Parameter Sniffing]]></category>
		<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2011/09/parameter-sniffing-and-plan-reuse-affecting-set-options/</guid>
		<description><![CDATA[One interesting problem I am asked to troubleshoot sometimes is when a developer tells me that a stored procedure is timing out or taking too long to execute on a web application but returning immediately when executed directly in Management Studio. Even for the same parameters. Although there could be a few reasons for a [...]]]></description>
			<content:encoded><![CDATA[<p>One interesting problem I am asked to troubleshoot sometimes is when a developer tells me that a stored procedure is timing out or taking too long to execute on a web application but returning immediately when executed directly in Management Studio. Even for the same parameters. Although there could be a few reasons for a problem like this to happen, including blocking, the most frequent one is related to a behavior in which the plan used by the web application was optimized using a combination of parameters which produced a “bad” plan for some other executions of the same stored procedure with different parameters. Although you may be tempted to just run sp_recompile to force a new optimization and allow the application to continue working, this does not really fix the problem and it may eventually come back. You could have also seen some similar scenarios where you have updated statistics, rebuild an index or changed something else to find out that suddenly the problem seems to be fixed. It is not. Those changes probably just forced a new optimization with the “good” parameter you were just testing. Obviously the best thing to do for this kind of problem is capturing the “bad” plan for further analysis in order to provide a permanent solution. In this post I will show you how to do that.</p>
<p>But first, a little bit of background. Remember that in general query optimization is an expensive operation and, in order to avoid this optimization cost, the plan cache will try to keep the generated execution plans in memory so they can be reused. So, if the stored procedure is executed thousands of times, only one optimization is needed. However, if a new connection running the same stored procedure has different SET options it may generate a new plan instead of reusing the one already on the plan cache. This new plan can be reused by later executions of the same stored procedure with the same connection settings. A new plan is needed as these SET options can impact the choice of an execution plan because they affect the results of evaluating constant expressions during the optimization process (a process known as constant folding and explained <a href="http://msdn.microsoft.com/en-us/library/ms175933.aspx">here</a>). Another connection setting, FORCEPLAN, acts in a similar way to a hint, requesting the Query Optimizer both to preserve the join order as specified on the query syntax and to use nested loop joins only. As indicated in the Microsoft white paper <a href="http://msdn.microsoft.com/en-us/library/ee343986(v=sql.100).aspx">Plan Caching in SQL Server 2008</a>, the following SET options will affect the reuse of execution plans. </p>
<pre class="code"><span style="color: blue">ANSI_NULL_DFLT_OFF
ANSI_NULL_DFLT_ON
ANSI_NULLS
ANSI_PADDING
ANSI_WARNINGS
ARITHABORT
CONCAT_NULL_YIELDS_NULL
DATEFIRST
DATEFORMAT
FORCEPLAN
LANGUAGE
NO_BROWSETABLE
NUMERIC_ROUNDABORT
QUOTED_IDENTIFIER</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Unfortunately different management or development tools, like Management Studio, ADO.NET, or even sqlcmd, may have different SET options in their default configuration. You will find that mostly the problem is that one of the options, ARITHABORT, is OFF in ADO.NET and ON in Management Studio. So it may be possible that, in our example, Management Studio and the web application are using distinct cached plans and that the web application initially got a good plan for the parameters used during the optimization, but this plan is not good for some other executions of the same stored procedure with different parameters. </p>
<p>But now let us see how to prove that parameter sniffing is in fact the problem for your specific instance of the issue, and how to extract the plans to inspect both the parameters and the SET options used during optimization. Since AdventureWorks does not have the default SET options of a new database, let us create our own and copy some data from AdventureWorks</p>
<pre class="code"><span style="color: blue">CREATE DATABASE </span>Test
<span style="color: blue">GO</span></pre>
<p>Create a new table and a stored procedure to test</p>
<pre class="code"><span style="color: blue">USE </span>Test
<span style="color: blue">GO
SELECT </span><span style="color: gray">* </span><span style="color: blue">INTO </span>dbo<span style="color: gray">.</span>SalesOrderDetail
<span style="color: blue">FROM </span>AdventureWorks<span style="color: gray">.</span>Sales<span style="color: gray">.</span>SalesOrderDetail
<span style="color: blue">GO
CREATE NONCLUSTERED INDEX </span>IX_SalesOrderDetail_ProductID
<span style="color: blue">ON </span>dbo<span style="color: gray">.</span>SalesOrderDetail<span style="color: gray">(</span>ProductID<span style="color: gray">)
</span><span style="color: blue">GO
CREATE PROCEDURE </span>test <span style="color: gray">(</span>@pid <span style="color: blue">int</span><span style="color: gray">)
</span><span style="color: blue">AS
SELECT </span><span style="color: gray">* </span><span style="color: blue">FROM </span>dbo<span style="color: gray">.</span>SalesOrderDetail
<span style="color: blue">WHERE </span>ProductID <span style="color: gray">= </span>@pid</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Let us test two different applications, executing the stored procedure from a .NET application (C# code included at the end) and from Management Studio. For the purpose of this test we want to assume that a plan with a table scan is a bad plan and a plan using an index seek/RID lookup is the optimal one. </p>
<p>Start with a clean plan cache by running</p>
<pre class="code"><span style="color: blue">DBCC </span>FREEPROCCACHE</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Run the .NET application from a command prompt window and provide the value 870 as a parameter (note that this application is only running the test stored procedure)</p>
<pre class="code">C:\TestApp\test
Enter ProductID: 870</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>At this moment we can start inspecting the plan cache to see the plans available in memory. Run the following script from the Test database (we will be running this script again later during this exercise)</p>
<pre class="code"><span style="color: blue">SELECT </span>plan_handle<span style="color: gray">, </span>usecounts<span style="color: gray">, </span>pvt<span style="color: gray">.</span>set_options
<span style="color: blue">FROM </span><span style="color: gray">(
    </span><span style="color: blue">SELECT </span>plan_handle<span style="color: gray">, </span>usecounts<span style="color: gray">, </span>epa<span style="color: gray">.</span>attribute<span style="color: gray">, </span>epa<span style="color: gray">.</span>value
    <span style="color: blue">FROM </span><span style="color: green">sys</span><span style="color: gray">.</span><span style="color: green">dm_exec_cached_plans
        </span><span style="color: gray">OUTER APPLY </span><span style="color: green">sys</span><span style="color: gray">.</span><span style="color: green">dm_exec_plan_attributes</span><span style="color: gray">(</span>plan_handle<span style="color: gray">) </span><span style="color: blue">AS </span>epa
    <span style="color: blue">WHERE </span>cacheobjtype <span style="color: gray">= </span><span style="color: red">'Compiled Plan'</span><span style="color: gray">) </span><span style="color: blue">AS </span>ecpa
<span style="color: gray">PIVOT (</span><span style="color: magenta">MAX</span><span style="color: gray">(</span>ecpa<span style="color: gray">.</span>value<span style="color: gray">) </span><span style="color: blue">FOR </span>ecpa<span style="color: gray">.</span>attribute <span style="color: gray">IN (</span>&quot;set_options&quot;<span style="color: gray">, </span>&quot;objectid&quot;<span style="color: gray">)) </span><span style="color: blue">AS </span>pvt
<span style="color: blue">where </span>pvt<span style="color: gray">.</span>objectid <span style="color: gray">= </span><span style="color: magenta">object_id</span><span style="color: gray">(</span><span style="color: red">'dbo.test'</span><span style="color: gray">)</span></pre>
<p>You should get an output similar to this</p>
<pre class="code">plan_handle                                           usecounts    set_options
0x05000700210F0207B8C09007000000000000000000000000    1            251</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>The output shows that we have one execution plan in the plan cache, it has been used once (as indicated by the usecounts value), and the set_options value, taken from the sys.dm_exec_plan_attributes DMF, is 251. Since this was the first execution of the stored procedure, it was optimized using the parameter 870 which in this case created a plan using a table scan (consider here a “bad” plan). Now run the application again using a parameter that returns only a few records and will benefit from an index seek/RID lookup plan:</p>
<pre class="code">C:\TestApp\test
Enter ProductID: 898</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>If you inspect the plan cache again you will notice that the plan has been used twice and unfortunately this time it was not good for the second parameter used</p>
<pre class="code">plan_handle                                           usecounts    set_options
0x05000700210F0207B8C09007000000000000000000000000    2            251</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>At this moment the developer may try to troubleshoot this problem by running the stored procedure in Management Studio using something like this</p>
<pre class="code"><span style="color: blue">EXEC </span>test @pid <span style="color: gray">= </span>898</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Now the developer is surprised to find that SQL Server is returning a good execution plan and the query is returning immediately. Inspecting the plan cache again will show something similar to this</p>
<pre class="code">plan_handle                                           usecounts    set_options
0x05000700210F0207B8C09007000000000000000000000000    2            251
0x05000700210F0207B860650B000000000000000000000000    1            4347</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>You can see that a new plan was added for the Management Studio execution, with a different value for set_options.</p>
<p>What to do next? It is time to inspect the plans and look at the SET options and parameters used during the optimization. Select the plan_handle of the first plan created (the one with set_options 251 in your own example) and use it to run the following query </p>
<pre class="code"><span style="color: blue">select </span><span style="color: gray">* </span><span style="color: blue">from </span><span style="color: green">sys</span><span style="color: gray">.</span><span style="color: green">dm_exec_query_plan
</span><span style="color: gray">(</span>0x05000700210F0207B8C09007000000000000000000000000<span style="color: gray">)</span></pre>
<p>You can find the SET options at the beginning of the plan</p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">StatementSetOptions </span><span style="color: red">QUOTED_IDENTIFIER</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot; <span style="color: red">ARITHABORT</span><span style="color: blue">=</span>&quot;<span style="color: blue">false</span>&quot;
<span style="color: red">CONCAT_NULL_YIELDS_NULL</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot; <span style="color: red">ANSI_NULLS</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot;
<span style="color: red">ANSI_PADDING</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot; <span style="color: red">ANSI_WARNINGS</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot; <span style="color: red">NUMERIC_ROUNDABORT</span><span style="color: blue">=</span>&quot;<span style="color: blue">false</span>&quot; <span style="color: blue">/&gt;</span></pre>
<p>And the used parameters at the end</p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">ParameterList</span><span style="color: blue">&gt;
    &lt;</span><span style="color: #a31515">ColumnReference </span><span style="color: red">Column</span><span style="color: blue">=</span>&quot;<span style="color: blue">@pid</span>&quot; <span style="color: red">ParameterCompiledValue</span><span style="color: blue">=</span>&quot;<span style="color: blue">(870)</span>&quot; <span style="color: blue">/&gt;
&lt;/</span><span style="color: #a31515">ParameterList</span><span style="color: blue">&gt;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Do the same for the second plan and you will get the following information for the SET options</p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">StatementSetOptions </span><span style="color: red">QUOTED_IDENTIFIER</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot; <span style="color: red">ARITHABORT</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot;
<span style="color: red">CONCAT_NULL_YIELDS_NULL</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot; <span style="color: red">ANSI_NULLS</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot;
<span style="color: red">ANSI_PADDING</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot; <span style="color: red">ANSI_WARNINGS</span><span style="color: blue">=</span>&quot;<span style="color: blue">true</span>&quot; <span style="color: red">NUMERIC_ROUNDABORT</span><span style="color: blue">=</span>&quot;<span style="color: blue">false</span>&quot; <span style="color: blue">/&gt;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>and the following parameter information</p>
<pre class="code"><span style="color: blue">&lt;</span><span style="color: #a31515">ParameterList</span><span style="color: blue">&gt;
    &lt;</span><span style="color: #a31515">ColumnReference </span><span style="color: red">Column</span><span style="color: blue">=</span>&quot;<span style="color: blue">@pid</span>&quot; <span style="color: red">ParameterCompiledValue</span><span style="color: blue">=</span>&quot;<span style="color: blue">(898)</span>&quot; <span style="color: blue">/&gt;
&lt;/</span><span style="color: #a31515">ParameterList</span><span style="color: blue">&gt;</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>This information shows that the ARITHABORT SET option has different value on these plans and that the parameter used to optimize the query on the web application was 870. (The same information is available from the Properties window of a graphical plan). You can also verify the operators used in the plan, the first one using a table scan and the second one an index seek/RID lookup combination.</p>
<p>Now that you have captured the plans you can force a new optimization so the application can use a better plan immediately (keeping in mind that this is not a permanent solution). Try this</p>
<pre class="code"><span style="color: maroon">sp_recompile </span>test</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>So now you know that you have a problem related to parameter sniffing. What to do next? I have a few recommendations on previous posts <a href="http://www.benjaminnevarez.com/2010/06/the-parameter-sniffing-problem/">here</a> and <a href="http://www.benjaminnevarez.com/2010/06/how-optimize-for-unknown-works/">here</a>. I have another one <a href="http://www.benjaminnevarez.com/2010/08/disabling-parameter-sniffing/">here</a> but usually you should not be doing this.</p>
<p>Finally, you can use the following script to display SET options for a specific set_options value </p>
<pre class="code"><span style="color: blue">declare </span>@set_options <span style="color: blue">int </span><span style="color: gray">= </span>251
<span style="color: blue">if </span><span style="color: gray">((</span>1 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>1<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'ANSI_PADDING'
</span><span style="color: blue">if </span><span style="color: gray">((</span>4 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>4<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'FORCEPLAN'
</span><span style="color: blue">if </span><span style="color: gray">((</span>8 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>8<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'CONCAT_NULL_YIELDS_NULL'
</span><span style="color: blue">if </span><span style="color: gray">((</span>16 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>16<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'ANSI_WARNINGS'
</span><span style="color: blue">if </span><span style="color: gray">((</span>32 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>32<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'ANSI_NULLS'
</span><span style="color: blue">if </span><span style="color: gray">((</span>64 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>64<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'QUOTED_IDENTIFIER'
</span><span style="color: blue">if </span><span style="color: gray">((</span>128 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>128<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'ANSI_NULL_DFLT_ON'
</span><span style="color: blue">if </span><span style="color: gray">((</span>256 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>256<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'ANSI_NULL_DFLT_OFF'
</span><span style="color: blue">if </span><span style="color: gray">((</span>512 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>512<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'NoBrowseTable'
</span><span style="color: blue">if </span><span style="color: gray">((</span>4096 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>4096<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'ARITH_ABORT'
</span><span style="color: blue">if </span><span style="color: gray">((</span>8192 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>8192<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'NUMERIC_ROUNDABORT'
</span><span style="color: blue">if </span><span style="color: gray">((</span>16384 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>16384<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'DATEFIRST'
</span><span style="color: blue">if </span><span style="color: gray">((</span>32768 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>32768<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'DATEFORMAT'
</span><span style="color: blue">if </span><span style="color: gray">((</span>65536 <span style="color: gray">&amp; </span>@set_options<span style="color: gray">) = </span>65536<span style="color: gray">) </span><span style="color: blue">print </span><span style="color: red">'LanguageID'</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>C# Code</p>
<p><a href="http://11011.net/software/vspaste"></a></p>
<pre class="code"><span style="color: blue">using </span>System;
<span style="color: blue">using </span>System.Data;
<span style="color: blue">using </span>System.Data.SqlClient;

<span style="color: blue">class </span><span style="color: #2b91af">Test
</span>{
    <span style="color: blue">static void </span>Main()
    {
        SqlConnection cnn = <span style="color: blue">null</span>;
        SqlDataReader reader = <span style="color: blue">null</span>;

        <span style="color: blue">try
        </span>{
            <span style="color: #2b91af">Console</span>.Write(<span style="color: #a31515">&quot;Enter ProductID: &quot;</span>);
            <span style="color: blue">string </span>pid = <span style="color: #2b91af">Console</span>.ReadLine();

            cnn = <span style="color: blue">new </span>SqlConnection(<span style="color: #a31515">&quot;Data Source=(local);Initial Catalog=Test;
                </span>Integrated Security=SSPI<span style="color: #a31515">&quot;);
            </span>SqlCommand cmd = <span style="color: blue">new </span>SqlCommand();
            cmd.Connection = cnn;
            cmd.CommandText = <span style="color: #a31515">&quot;dbo.test&quot;</span>;
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Add(<span style="color: #a31515">&quot;@pid&quot;</span>, SqlDbType.Int).Value = pid;
            cnn.Open();
            reader = cmd.ExecuteReader();
            <span style="color: blue">while </span>(reader.Read())
            {
                <span style="color: #2b91af">Console</span>.WriteLine(reader[0]);
            }
            <span style="color: blue">return</span>;
        }
        <span style="color: blue">catch </span>(<span style="color: #2b91af">Exception </span>e)
        {
            <span style="color: blue">throw </span>e;
        }
        <span style="color: blue">finally
        </span>{
            <span style="color: blue">if </span>(cnn != <span style="color: blue">null</span>)
            {
                <span style="color: blue">if </span>(cnn.State != ConnectionState.Closed)
                    cnn.Close();
            }
        }
    }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2011/09/parameter-sniffing-and-plan-reuse-affecting-set-options/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Speaking at the PASS Summit and other Southern California events</title>
		<link>http://www.benjaminnevarez.com/2011/09/speaking-at-the-pass-summit-and-other-southern-california-events/</link>
		<comments>http://www.benjaminnevarez.com/2011/09/speaking-at-the-pass-summit-and-other-southern-california-events/#comments</comments>
		<pubDate>Fri, 09 Sep 2011 06:31:24 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Speaking]]></category>
		<category><![CDATA[Parameter Sniffing]]></category>
		<category><![CDATA[PASS Summit]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[SQLSaturday]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2011/09/speaking-at-the-pass-summit-and-other-southern-california-events/</guid>
		<description><![CDATA[I am currently working on the two sessions that I will be presenting at the PASS Summit: Inside the SQL Server Query Optimizer and Parameter Sniffing: the Query Optimizer vs. the Plan Cache. In addition, I will be presenting these two new sessions in other SQL Server events in Southern California including SQLSatuday #95. First, [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://sqlsaturday.com/eventhome.aspx"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="SQLSaturday #95 - San Diego 2011" border="0" src="http://www.sqlsaturday.com/images/sqlsat95_web.png" width="266" height="130" /></a></p>
<p>I am currently working on the two sessions that I will be presenting at the PASS Summit: <a href="http://www.sqlpass.org/summit/2011/Speakers/CallForSpeakers/SessionDetail.aspx?sid=1645">Inside the SQL Server Query Optimizer</a> and <a href="http://www.sqlpass.org/summit/2011/Speakers/CallForSpeakers/SessionDetail.aspx?sid=1678">Parameter Sniffing: the Query Optimizer vs. the Plan Cache</a>. In addition, I will be presenting these two new sessions in other SQL Server events in Southern California including SQLSatuday #95. </p>
<p>First, I will be speaking at the <a href="http://sql.la/">Los Angeles SQL Server Professionals Group</a> on Thursday September 15th. The meeting will be hosted at the UCLA Anderson School of Management and will start at 6:30 PM. I will present only one session, Inside the SQL Server Query Optimizer, in this meeting. You can find additional information about the meeting and directions on their <a href="http://sql.la/">website</a>.</p>
<p>Two days later, on September 17th, I will be speaking at SQLSaturday #95 in San Diego, CA. Of course, this SQLSaturday will also have many other great speakers and the final schedule is already posted <a href="http://sqlsaturday.com/95/schedule.aspx">here</a>. In addition to presenting both of my sessions described before I will be participating in the <a href="http://sqlsaturday.com/viewsession.aspx?sat=95&amp;sessionid=5515">Ask the Experts &#8211; SQL Server Q&amp;A</a> session coordinated by Thomas Mueller. For more details and directions for SQLSaturday #95 please go to their website <a href="http://sqlsaturday.com/eventhome.aspx">here</a>.</p>
<p>On October 7th I will be presenting my Query Optimizer session at the <a href="http://sqloc.com/">Orange County SQL Server Professionals User Group</a> in Mission Viejo, CA. Details and directions will be posted soon on their website <a href="http://sqloc.com/">here</a>.</p>
<p>Then it is time for the <a href="http://www.sqlpass.org/summit/2011/">PASS Summit</a>, the largest SQL Server and BI conference in the world. The PASS Summit is hosted again this year in Seattle, WA and it is scheduled for October 11-14. The schedule for my two sessions is not final at the moment of writing this but so far it looks like I will be speaking on Wednesday and Friday.</p>
<p>I am flying back from the PASS Summit on Saturday and planning to present my two sessions at the <a href="http://www.socalcodecamp.com/">SoCal Code Camp</a> the following day, Sunday October 16th. The SoCal Code Camp is a community driven event for developers to come and learn from their peers. At this moment they are still accepting sessions so no schedule has been created yet. You can register, find additional information and directions on their website <a href="http://www.socalcodecamp.com/">here</a>.</p>
<p>Finally, although I am not going to be speaking, I will be attending <a href="http://sqlinthecity.red-gate.com/events/los-angeles/">SQL in the City</a> in Los Angeles, CA on October 28th. SQL in the City is a one day SQL Server training event which will include several SQL Server MVPs and you can look at their site <a href="http://sqlinthecity.red-gate.com/events/los-angeles/">here</a> for more details and information.</p>
<p>I look forward to meeting lots of SQL Server professionals on these events.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2011/09/speaking-at-the-pass-summit-and-other-southern-california-events/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Code from my book Inside the SQL Server Query Optimizer</title>
		<link>http://www.benjaminnevarez.com/2011/07/code-from-my-book-inside-the-sql-server-query-optimizer/</link>
		<comments>http://www.benjaminnevarez.com/2011/07/code-from-my-book-inside-the-sql-server-query-optimizer/#comments</comments>
		<pubDate>Mon, 25 Jul 2011 08:11:20 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Books]]></category>
		<category><![CDATA[Query Optimizer]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2011/07/code-from-my-book-inside-the-sql-server-query-optimizer/</guid>
		<description><![CDATA[Recently I’ve been requested the code of my book Inside the Server Query Optimizer so I am including it in this post. The book contains a large number of example SQL queries, all of which are based on the AdventureWorks database and Chapter 6 additionally uses the AdventureWorksDW database. All code has been tested on [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I’ve been requested the code of my book <a href="http://www.amazon.com/Inside-SQL-Server-Query-Optimizer/dp/1906434603">Inside the Server Query Optimizer</a> so I am including it in this post. The book contains a large number of example SQL queries, all of which are based on the AdventureWorks database and Chapter 6 additionally uses the AdventureWorksDW database. All code has been tested on both SQL Server 2008 and SQL Server 2008 R2. Note that these sample databases are not included by default in your SQL Server installation, but can be downloaded from the <a href="http://www.codeplex.com/">CodePlex</a> website.</p>
<div id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:3643e297-ea88-483a-b493-1dc8bb1d4109" class="wlWriterEditableSmartContent" style="margin: 0px; display: inline; float: none; padding: 0px;">
<p>Inside the SQL Server Query Optimizer code &#8211; <a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/InsideQueryOptimizerCode.txt" target="_blank">InsideQueryOptimizerCode.txt</a></p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2011/07/code-from-my-book-inside-the-sql-server-query-optimizer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Query Optimization with Denali Columnstore Indexes</title>
		<link>http://www.benjaminnevarez.com/2011/07/query-optimization-with-denali-columnstore-indexes/</link>
		<comments>http://www.benjaminnevarez.com/2011/07/query-optimization-with-denali-columnstore-indexes/#comments</comments>
		<pubDate>Tue, 19 Jul 2011 02:53:16 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Columnstore Indexes]]></category>
		<category><![CDATA[Column store indexes]]></category>
		<category><![CDATA[Denali]]></category>
		<category><![CDATA[Query Optimizer]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2011/07/query-optimization-with-denali-columnstore-indexes/</guid>
		<description><![CDATA[In a previous post I talked about the new columnstore indexes and their related processing algorithms which are available in SQL Server code-named Denali. In this post I will cover the query processing part of the technology in more detail and will show you some examples that you can test on the recently released CTP3 [...]]]></description>
			<content:encoded><![CDATA[<p>In a previous <a href="http://www.benjaminnevarez.com/2011/04/columnstore-indexes-and-other-new-optimizations-in-denali/">post</a> I talked about the new columnstore indexes and their related processing algorithms which are available in SQL Server code-named Denali. In this post I will cover the query processing part of the technology in more detail and will show you some examples that you can test on the recently released CTP3 (Community Technology Preview) of the product.</p>
<p>Same as with previous versions of SQL Server, in Denali the query optimizer can choose between the available access methods, which now also include columnstore indexes, and as always, this will be a cost-based decision. A new choice the query optimizer will have to make is the selection of an execution mode. The new query processing algorithms mentioned in my previous post will run in what is called a batch execution mode, which is different from the traditional processing mode, now called row mode.</p>
<p>In the row execution mode operators process data one row at a time. The new batch execution mode process data in batches which is more efficient for large amounts of data, like the workloads present on data warehouse queries. Each operator in an execution plan can use the row execution mode and, when columnstore indexes are available, some operators can also use the batch mode. There is both an estimated and an actual execution mode and this information is displayed on the query execution plan as I will show later. It is also worth mentioning that, although columnstore indexes can speed up the performance of data warehouse queries, they are not a good choice for very selective queries returning only a few records. In this case the query optimizer may have to rely on row stores, like clustered or regular nonclustered indexes, to find those records quickly. There are no seeks on columnstore indexes.</p>
<p>Same as with previous versions of SQL Server, you still have the choice to use a hint to force any index in the cases where the query optimizer is not giving you a good execution plan. This can happen for example when the query optimizer is choosing a columnstore index when it shouldn’t or when you want to force a columnstore index when it is not being selected. You can also use the new IGNORE_NONCLUSTERED_COLUMNSTORE_INDEX hint to ask the query optimizer to avoid using any columnstore index.</p>
<p>Let me show you an example which you can test on SQL Server Denali CTP3, currently available for download <a href="https://www.microsoft.com/betaexperience/pd/SQLDCTP3CTA/enus/default.aspx">here</a>. To follow this example you will also need the AdventureWorksDWDenali database, available at <a href="http://msftdbprodsamples.codeplex.com/releases/view/55330">CodePlex</a> and I will use the same <a href="http://msdn.microsoft.com/en-us/library/gg492088(v=SQL.110).aspx">example</a> on BOL to skip the basics and go directly to analyze the batch processing mode (By the way the BOL example didn’t work directly with the AdventureWorksDWDenali database so I had to add a few more columns at the end of the CREATE TABLE statement.)</p>
<p>First, use the following BOL code to create a partition function, a partition scheme and a new partitioned table with a columnstore index</p>
<pre class="code"><span style="color: blue;">USE </span>AdventureWorksDWDenali<span style="color: gray;">;
</span><span style="color: blue;">GO

CREATE PARTITION FUNCTION </span>[ByOrderDateMonthPF]<span style="color: gray;">(</span><span style="color: blue;">int</span><span style="color: gray;">) </span><span style="color: blue;">AS RANGE </span><span style="color: gray;">RIGHT
</span><span style="color: blue;">FOR VALUES </span><span style="color: gray;">(
    </span>20050701<span style="color: gray;">, </span>20050801<span style="color: gray;">, </span>20050901<span style="color: gray;">, </span>20051001<span style="color: gray;">, </span>20051101<span style="color: gray;">, </span>20051201<span style="color: gray;">,
    </span>20060101<span style="color: gray;">, </span>20060201<span style="color: gray;">, </span>20060301<span style="color: gray;">, </span>20060401<span style="color: gray;">, </span>20060501<span style="color: gray;">, </span>20060601<span style="color: gray;">,
    </span>20060701<span style="color: gray;">, </span>20060801<span style="color: gray;">, </span>20060901<span style="color: gray;">, </span>20061001<span style="color: gray;">, </span>20061101<span style="color: gray;">, </span>20061201<span style="color: gray;">,
    </span>20070101<span style="color: gray;">, </span>20070201<span style="color: gray;">, </span>20070301<span style="color: gray;">, </span>20070401<span style="color: gray;">, </span>20070501<span style="color: gray;">, </span>20070601<span style="color: gray;">,
    </span>20070701<span style="color: gray;">, </span>20070801<span style="color: gray;">, </span>20070901<span style="color: gray;">, </span>20071001<span style="color: gray;">, </span>20071101<span style="color: gray;">, </span>20071201<span style="color: gray;">,
    </span>20080101<span style="color: gray;">, </span>20080201<span style="color: gray;">, </span>20080301<span style="color: gray;">, </span>20080401<span style="color: gray;">, </span>20080501<span style="color: gray;">, </span>20080601<span style="color: gray;">,
    </span>20080701<span style="color: gray;">, </span>20080801<span style="color: gray;">, </span>20080901<span style="color: gray;">, </span>20081001<span style="color: gray;">, </span>20081101<span style="color: gray;">, </span>20081201
<span style="color: gray;">)
</span><span style="color: blue;">GO

CREATE PARTITION </span>SCHEME [ByOrderDateMonthRange]
<span style="color: blue;">AS PARTITION </span>[ByOrderDateMonthPF]
<span style="color: gray;">ALL </span><span style="color: blue;">TO </span><span style="color: gray;">(</span>[PRIMARY]<span style="color: gray;">)
</span><span style="color: blue;">GO

</span><span style="color: green;">-- Create a partitioned version of the FactResellerSales table
</span><span style="color: blue;">CREATE TABLE </span>[dbo]<span style="color: gray;">.</span>[FactResellerSalesPtnd]<span style="color: gray;">(
    </span>[ProductKey] [int] <span style="color: gray;">NOT NULL,
    </span>[OrderDateKey] [int] <span style="color: gray;">NOT NULL,
    </span>[DueDateKey] [int] <span style="color: gray;">NOT NULL,
    </span>[ShipDateKey] [int] <span style="color: gray;">NOT NULL,
    </span>[ResellerKey] [int] <span style="color: gray;">NOT NULL,
    </span>[EmployeeKey] [int] <span style="color: gray;">NOT NULL,
    </span>[PromotionKey] [int] <span style="color: gray;">NOT NULL,
    </span>[CurrencyKey] [int] <span style="color: gray;">NOT NULL,
    </span>[SalesTerritoryKey] [int] <span style="color: gray;">NOT NULL,
    </span>[SalesOrderNumber] [nvarchar]<span style="color: gray;">(</span>20<span style="color: gray;">) NOT NULL,
    </span>[SalesOrderLineNumber] [tinyint] <span style="color: gray;">NOT NULL,
    </span>[RevisionNumber] [tinyint] <span style="color: gray;">NULL,
    </span>[OrderQuantity] [smallint] <span style="color: gray;">NULL,
    </span>[UnitPrice] [money] <span style="color: gray;">NULL,
    </span>[ExtendedAmount] [money] <span style="color: gray;">NULL,
    </span>[UnitPriceDiscountPct] [float] <span style="color: gray;">NULL,
    </span>[DiscountAmount] [float] <span style="color: gray;">NULL,
    </span>[ProductStandardCost] [money] <span style="color: gray;">NULL,
    </span>[TotalProductCost] [money] <span style="color: gray;">NULL,
    </span>[SalesAmount] [money] <span style="color: gray;">NULL,
    </span>[TaxAmt] [money] <span style="color: gray;">NULL,
    </span>[Freight] [money] <span style="color: gray;">NULL,
    </span>[CarrierTrackingNumber] [nvarchar]<span style="color: gray;">(</span>25<span style="color: gray;">) NULL,
    </span>[CustomerPONumber] [nvarchar]<span style="color: gray;">(</span>25<span style="color: gray;">) NULL,
    </span>[OrderDate] <span style="color: blue;">datetime </span><span style="color: gray;">NULL,
    </span>[DueDate] <span style="color: blue;">datetime </span><span style="color: gray;">NULL,
    </span>[ShipDate] <span style="color: blue;">datetime </span><span style="color: gray;">NULL
) </span><span style="color: blue;">ON </span>ByOrderDateMonthRange<span style="color: gray;">(</span>OrderDateKey<span style="color: gray;">);
</span><span style="color: blue;">GO

</span><span style="color: green;">-- Copy the data from the FactResellerSales into the new table
</span><span style="color: blue;">INSERT INTO </span>dbo<span style="color: gray;">.</span>FactResellerSalesPtnd <span style="color: blue;">WITH</span><span style="color: gray;">(</span><span style="color: blue;">TABLOCK</span><span style="color: gray;">)
</span><span style="color: blue;">SELECT </span><span style="color: gray;">* </span><span style="color: blue;">FROM </span>dbo<span style="color: gray;">.</span>FactResellerSales<span style="color: gray;">;
</span><span style="color: blue;">GO

</span><span style="color: green;">-- Create the columnstore index
</span><span style="color: blue;">CREATE NONCLUSTERED </span>COLUMNSTORE <span style="color: blue;">INDEX </span>[csindx_FactResellerSalesPtnd]
<span style="color: blue;">ON </span>[FactResellerSalesPtnd]
<span style="color: gray;">(
    </span>[ProductKey]<span style="color: gray;">,
    </span>[OrderDateKey]<span style="color: gray;">,
    </span>[DueDateKey]<span style="color: gray;">,
    </span>[ShipDateKey]<span style="color: gray;">,
    </span>[ResellerKey]<span style="color: gray;">,
    </span>[EmployeeKey]<span style="color: gray;">,
    </span>[PromotionKey]<span style="color: gray;">,
    </span>[CurrencyKey]<span style="color: gray;">,
    </span>[SalesTerritoryKey]<span style="color: gray;">,
    </span>[SalesOrderNumber]<span style="color: gray;">,
    </span>[SalesOrderLineNumber]<span style="color: gray;">,
    </span>[RevisionNumber]<span style="color: gray;">,
    </span>[OrderQuantity]<span style="color: gray;">,
    </span>[UnitPrice]<span style="color: gray;">,
    </span>[ExtendedAmount]<span style="color: gray;">,
    </span>[UnitPriceDiscountPct]<span style="color: gray;">,
    </span>[DiscountAmount]<span style="color: gray;">,
    </span>[ProductStandardCost]<span style="color: gray;">,
    </span>[TotalProductCost]<span style="color: gray;">,
    </span>[SalesAmount]<span style="color: gray;">,
    </span>[TaxAmt]<span style="color: gray;">,
    </span>[Freight]<span style="color: gray;">,
    </span>[CarrierTrackingNumber]<span style="color: gray;">,
    </span>[CustomerPONumber]
<span style="color: gray;">);
</span></pre>
<p>Now run the following query</p>
<pre class="code"><span style="color: blue;">SELECT </span>SalesTerritoryKey<span style="color: gray;">, </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>ExtendedAmount<span style="color: gray;">) </span><span style="color: blue;">AS </span>SalesByTerritory
<span style="color: blue;">FROM </span>FactResellerSalesPtnd
<span style="color: blue;">GROUP BY </span>SalesTerritoryKey<span style="color: gray;">;</span></pre>
<p>This will create the following plan where you can see the new Columnstore Index Scan operator</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image002.jpg"><img style="display: inline; border: 0px;" title="clip_image002" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image002_thumb.jpg" border="0" alt="clip_image002" width="620" height="91" /></a></p>
<p>The properties of the Columnstore Index Scan operator are shown next</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image003.gif"><img style="display: block; float: none; margin-left: auto; margin-right: auto; border: 0px;" title="clip_image003" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image003_thumb.gif" border="0" alt="clip_image003" width="334" height="515" /></a></p>
<p>You may notice that the actual and estimated execution mode is Row (lines 3 and 4 on the list of properties). Row execution mode was selected because the table is not large enough to require the batch execution mode. We can use the undocumented ROWCOUNT and PAGECOUNT options of the UPDATE STATISTICS statement to simulate a larger table as shown next (for more information about how this works see my post about the DTA <a href="http://www.benjaminnevarez.com/2011/05/database-engine-tuning-advisor-and-the-query-optimizer-part-2/">here</a>)</p>
<pre class="code"><span style="color: blue;">UPDATE STATISTICS </span>FactResellerSalesPtnd <span style="color: blue;">WITH ROWCOUNT </span><span style="color: gray;">= </span>10000000<span style="color: gray;">, </span><span style="color: blue;">PAGECOUNT </span><span style="color: gray;">= </span>1000000</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Removing the existing plan (using for example DBCC FREEPROCCACHE) and running the same query again will now show the following plan (only part is shown), which this time is using parallelism.</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image005.jpg"><img style="display: inline; border: 0px;" title="clip_image005" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image005_thumb.jpg" border="0" alt="clip_image005" width="616" height="68" /></a></p>
<p>In addition, by looking at the properties of the Columnstore Index Scan you can notice that this time it is using the batch execution mode</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image006.gif"><img style="display: block; float: none; margin-left: auto; margin-right: auto; border: 0px;" title="clip_image006" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image006_thumb.gif" border="0" alt="clip_image006" width="340" height="523" /></a></p>
<p>You can also use the new IGNORE_NONCLUSTERED_COLUMNSTORE_INDEX hint to disallow the use of a columnstore index. Run the following code</p>
<pre class="code"><span style="color: blue;">SELECT </span>SalesTerritoryKey<span style="color: gray;">, </span><span style="color: magenta;">SUM</span><span style="color: gray;">(</span>ExtendedAmount<span style="color: gray;">) </span><span style="color: blue;">AS </span>SalesByTerritory
<span style="color: blue;">FROM </span>FactResellerSalesPtnd
<span style="color: blue;">GROUP BY </span>SalesTerritoryKey
<span style="color: blue;">OPTION </span><span style="color: gray;">(</span>IGNORE_NONCLUSTERED_COLUMNSTORE_INDEX<span style="color: gray;">);</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>This will show you the following plan which as you can see it is now directly using the FactResellerSalesPtnd table, without using the columnstore index.</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image008.jpg"><img style="display: inline; border: 0px;" title="clip_image008" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/07/clip_image008_thumb.jpg" border="0" alt="clip_image008" width="618" height="85" /></a></p>
<p>Finally, since the number of records and pages of the FactResellerSalesPtnd table was altered for this test, perhaps you want drop it and create a new copy if you need to do some additional testing</p>
<pre class="code"><span style="color: blue;">DROP TABLE </span>FactResellerSalesPtnd</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2011/07/query-optimization-with-denali-columnstore-indexes/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Speaking at the PASS Summit 2011</title>
		<link>http://www.benjaminnevarez.com/2011/06/speaking-at-the-pass-summit-2011/</link>
		<comments>http://www.benjaminnevarez.com/2011/06/speaking-at-the-pass-summit-2011/#comments</comments>
		<pubDate>Sun, 19 Jun 2011 23:59:56 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Speaking]]></category>
		<category><![CDATA[PASS Summit]]></category>
		<category><![CDATA[Query Optimizer]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2011/06/speaking-at-the-pass-summit-2011/</guid>
		<description><![CDATA[I am honored to be speaking at the PASS Summit again this year. I’ve been attending this SQL Server conference every year since 2003 and this will be my fourth year speaking. Same as last year I will again be presenting two sessions. On my first session, Inside the SQL Server Query Optimizer, I will [...]]]></description>
			<content:encoded><![CDATA[<p><img style="display: block; float: none; margin-left: auto; margin-right: auto" src="http://www.sqlpass.org/LinkClick.aspx?fileticket=3sFpHLW1QpQ%3d&amp;tabid=4144&amp;mid=6163" /></p>
<p>I am honored to be speaking at the PASS Summit again this year. I’ve been attending this SQL Server conference every year since 2003 and this will be my fourth year speaking. Same as last year I will again be presenting two sessions. </p>
<p>On my first session, <b>Inside the SQL Server Query Optimizer</b>, I will go into the internals of the Query Optimizer and will show you the steps that it performs in the background covering everything from the time a query is submitted to SQL Server until an execution plan is generated. On my second session, <b>Parameter Sniffing: the Query Optimizer vs. the Plan Cache</b>, I will show you how the Query Optimizer uses parameter sniffing to produce a plan tailored to the current parameters of a query and why in some cases it could be a performance problem, including troubleshooting and solutions to these cases.</p>
<p>The PASS Summit is less than four months away and you can register <a href="http://www.sqlpass.org/summit/2011/">here</a>. I look forward to meeting lots of SQL Server professionals, including those whom I only know via twitter. See you in Seattle in October.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2011/06/speaking-at-the-pass-summit-2011/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Statistics on Computed Columns</title>
		<link>http://www.benjaminnevarez.com/2011/06/statistics-on-computed-columns/</link>
		<comments>http://www.benjaminnevarez.com/2011/06/statistics-on-computed-columns/#comments</comments>
		<pubDate>Wed, 08 Jun 2011 07:20:57 +0000</pubDate>
		<dc:creator>Benjamin Nevarez</dc:creator>
				<category><![CDATA[Computed Columns]]></category>
		<category><![CDATA[Query Optimizer]]></category>
		<category><![CDATA[Statistics]]></category>

		<guid isPermaLink="false">http://www.benjaminnevarez.com/2011/06/statistics-on-computed-columns/</guid>
		<description><![CDATA[Another interesting topic that I usually talk about on my presentations is statistics on computed columns so I will use this post to show you how they work and how they can help you to improve the performance of your queries. A problem faced by some queries using scalar expressions is that they usually cannot [...]]]></description>
			<content:encoded><![CDATA[<p>Another interesting topic that I usually talk about on my presentations is statistics on computed columns so I will use this post to show you how they work and how they can help you to improve the performance of your queries.</p>
<p>A problem faced by some queries using scalar expressions is that they usually cannot benefit from statistics and, without them, the Query Optimizer will use the 30% selectivity guess on inequality comparisons. A solution to this problem can be the use of computed columns, as SQL Server can automatically create and update statistics on these columns which can help the Query Optimizer to create better execution plans. An additional benefit of this solution is that you don&#8217;t need to specify the name of the computed column in your queries for SQL Server to use its statistics. The Query Optimizer automatically matches the computed column definition to an existing scalar expression in a query, so your applications do not need to be changed. Although computed columns have been available in previous versions of SQL Server, the automatic matching feature was only introduced with SQL Server 2005.</p>
<p>To see an example, run this query, which creates the plan shown next:</p>
<pre class="code"><span style="color: blue">SELECT </span><span style="color: gray">* </span><span style="color: blue">FROM </span>Sales<span style="color: gray">.</span>SalesOrderDetail
<span style="color: blue">WHERE </span>OrderQty <span style="color: gray">* </span>UnitPrice <span style="color: gray">&gt; </span>25000</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/06/clip_image0022.jpg"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/06/clip_image002_thumb2.jpg" width="616" height="103" /></a>The estimated number of rows is 36,395.1, which is 30% of the total number of rows, 121,317, although the query returns only 5 records. SQL Server is obviously using a selectivity guess, as it cannot estimate the selectivity of the expression OrderQty * UnitPrice &gt; 25000.</p>
<p>Now create a computed column:</p>
<pre class="code"><span style="color: blue">ALTER TABLE </span>Sales<span style="color: gray">.</span>SalesOrderDetail
<span style="color: blue">ADD </span>cc <span style="color: blue">AS </span>OrderQty <span style="color: gray">* </span>UnitPrice</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>Run the previous SELECT statement again and note that, this time, the estimated number of rows has changed to 84.3101 which is very close to the actual number of rows returned by the query, as shown in the following plan:</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/06/clip_image0042.jpg"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/06/clip_image004_thumb2.jpg" width="616" height="105" /></a>You can optionally test replacing the 25,000 in the query with some other values, like 1,000, 10,000, or 20,000 and verify that the estimated again will be close to the actual number of rows returned.</p>
<p>Note that creating the computed column does not create statistics; these statistics are created the first time that the query is optimized, and you can run the next query to display the information about the statistics objects for the Sales.SalesOrderDetail table:</p>
<pre class="code"><span style="color: blue">SELECT </span><span style="color: gray">* </span><span style="color: blue">FROM </span><span style="color: green">sys</span><span style="color: gray">.</span><span style="color: green">stats
</span><span style="color: blue">WHERE </span><span style="color: magenta">object_id </span><span style="color: gray">= </span><span style="color: magenta">object_id</span><span style="color: gray">(</span><span style="color: red">'Sales.SalesOrderDetail'</span><span style="color: gray">)</span></pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>The newly created statistics object will most likely be at the end of the list. Copy its name and use the following command to display the details about the statistics object (I&#8217;ve used the name of my local object, but you should replace that as appropriate). You can also use &quot;cc&quot; as the name of the object to get the same results. In both cases, the &quot;cc&quot; column should be shown on the Columns field in the density section.</p>
<pre class="code"><span style="color: blue">DBCC SHOW_STATISTICS </span><span style="color: gray">(</span><span style="color: red">'Sales.SalesOrderDetail'</span><span style="color: gray">, </span>_WA_Sys_0000000C_2645B050<span style="color: gray">)</span></pre>
<p>Unfortunately, for the automatic matching feature to work, the expression must be exactly the same as the computed column definition. So, if I change the query to UnitPrice * OrderQty, instead of OrderQty * UnitPrice, the execution plan will show an estimated number of rows of 30% again, as this query will demonstrate:</p>
<pre class="code"><span style="color: blue">SELECT </span><span style="color: gray">* </span><span style="color: blue">FROM </span>Sales<span style="color: gray">.</span>SalesOrderDetail
<span style="color: blue">WHERE </span>UnitPrice <span style="color: gray">* </span>OrderQty <span style="color: gray">&gt; </span>25000</pre>
<p><a href="http://11011.net/software/vspaste"></a></p>
<p>As mentioned, the computed column provides statistics so the Query Optimizer can try to get you a better execution plan. In addition, you can create an index on the existing computed column to provide a better navigational alternative. Create the following index</p>
<pre class="code"><span style="color: blue">CREATE INDEX </span>IX_cc <span style="color: blue">on </span>Sales<span style="color: gray">.</span>SalesOrderDetail<span style="color: gray">(</span>cc<span style="color: gray">)</span></pre>
<p>By running the original SELECT statement again the Query Optimizer will now choose the newly created index and will produce a more efficient plan using an Index Seek/Key Lookup instead of a Clustered Index Scan, as shown next.</p>
<p><a href="http://www.benjaminnevarez.com/wp-content/uploads/2011/06/clip_image006.jpg"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://www.benjaminnevarez.com/wp-content/uploads/2011/06/clip_image006_thumb.jpg" width="616" height="121" /></a>Finally, drop the index and computed column you’ve just created:</p>
<pre class="code"><span style="color: blue">DROP INDEX </span>Sales<span style="color: gray">.</span>SalesOrderDetail<span style="color: gray">.</span>IX_cc
<span style="color: blue">GO
ALTER TABLE </span>Sales<span style="color: gray">.</span>SalesOrderDetail
<span style="color: blue">DROP COLUMN </span>cc</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.benjaminnevarez.com/2011/06/statistics-on-computed-columns/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

