{"id":2805,"date":"2016-06-20T09:25:24","date_gmt":"2016-06-20T16:25:24","guid":{"rendered":"http:\/\/3.209.169.194\/blogs\/kimberly\/?p=2805"},"modified":"2016-06-21T03:15:04","modified_gmt":"2016-06-21T10:15:04","slug":"sp_settraceflag","status":"publish","type":"post","link":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/sp_settraceflag\/","title":{"rendered":"Setting CE TraceFlags on a query-by-query (or session) basis"},"content":{"rendered":"<p><strong>IMPORTANT NOTE:<\/strong> Be sure to read the ENTIRE post as there&#8217;s a workaround that QUERYTRACEON is ALLOWED inside of stored procedures&#8230; just NOT in an adHoc statement. Not documented but I think this is excellent! There are still some uses of what I&#8217;ve written but this specific use is much easier because of this undoc&#8217;ed &#8220;feature.&#8221; :-)<\/p>\n<p>While the new cardinality estimator\u00a0can offer benefits for <em>some<\/em> queries, it might not be perfect for <strong>ALL<\/strong> queries. Having said that, there is an OPTION clause that allows you to set the CE for that query. The setting to use depends on the CE that you\u2019re running under currently. And, as of SQL Server 2016, even determining this can be difficult.\u00a0At any given time, there are multiple settings that might affect your CE.<\/p>\n<p>In SQL Server 2014, your CE\u00a0was set by the database compatibility model. If you\u2019re running with compatibility mode 120 or higher, then you\u2019re using the new CE. If you\u2019re running with compatibility mode 110 or lower, then you\u2019re using the Legacy CE. In SQL Server 2016, the database compatibility mode is not the only setting that can affect the CE that you\u2019re using. In SQL Server 2016, they added &#8216;database scoped configuations&#8217; and introduced:<\/p>\n<p>ALTER DATABASE SCOPED CONFIGURATION LEGACY_CARDINALITY_ESTIMATION = { ON | OFF | PRIMARY}<\/p>\n<p>Having said that, an administrator\u00a0can always override this setting by setting\u00a0one of the CE\u00a0trace flags globally [using DBCC TRACEON (TF, -1) \u2013 but I don\u2019t recommend this!].<\/p>\n<p style=\"padding-left: 30px;\">To use the LegacyCE when the database is set to the new CE, use Trace Flag 9481.<br \/>\nTo use the New CE when the database is set to the LegacyCE, use Trace Flag 2312.<\/p>\n<p>Generally, I recommend that most people STAY with the LegacyCE until they\u2019ve thoroughly tested the new CE. Then, and only then, change the compatibility mode. But, even with extensive testing, you might still want some queries to run with the LegacyCE while most run with the new CE (or, potentially the opposite). What I like most about the addition of the new CE is that we have the ability to set EITHER!<\/p>\n<p>But, before setting this (or, overriding how the database is set), let\u2019s make sure we know how it\u2019s set currently\u2026\u00a0If you\u2019re wondering which CE you\u2019re running under, you can see it within the graphical showplan (in the Properties [F4] window, use: CardinalityEstimationModelVersion OR search for that within the showplan XML).<\/p>\n<p><a href=\"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-content\/uploads\/2016\/06\/CardinalityEstimationModelVersion.jpg?809ee6\"><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-2806 alignnone\" src=\"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-content\/uploads\/2016\/06\/CardinalityEstimationModelVersion.jpg?809ee6\" alt=\"CardinalityEstimationModelVersion\" width=\"929\" height=\"675\" srcset=\"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-content\/uploads\/2016\/06\/CardinalityEstimationModelVersion.jpg 929w, https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-content\/uploads\/2016\/06\/CardinalityEstimationModelVersion-300x218.jpg 300w, https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-content\/uploads\/2016\/06\/CardinalityEstimationModelVersion-900x654.jpg 900w\" sizes=\"auto, (max-width: 929px) 100vw, 929px\" \/><\/a><\/p>\n<p>Above all, what I like most is \u2013 CHOICE.\u00a0I can even set this for a specific query:<\/p>\n<p>SELECT m.*<br \/>\nFROM [dbo].[Member] AS [m]<br \/>\nWHERE [m].[firstname] LIKE &#8216;Kim%&#8217;<br \/>\nOPTION (QUERYTRACEON 9481);<\/p>\n<p>However, the bad news is the <strong>QUERYTRACEON<\/strong> is limited to SysAdmin only <strong>(be sure to read the UPDATEs at the end of this post)<\/strong>. Jack Li (Microsoft CSS) wrote a great article about a problem they solved by using a logon trigger to change the CE for an entire session:\u00a0<a href=\"https:\/\/blogs.msdn.microsoft.com\/psssql\/2015\/12\/30\/wanting-your-non-sysadmin-users-to-enable-certain-trace-flags-without-changing-your-app\/\" target=\"_blank\">Wanting your non-sysadmin users to enable certain trace flags without changing your app?<\/a>\u00a0Now, I do want to caution you that setting master to trustworthy is not something you should take lightly. But, you should NOT let anyone other than SysAdmin have any other rights in master (other than the occasional EXEC on an added user-defined SP). Here\u00a0are a couple of\u00a0posts to help warn you of the danger:<\/p>\n<p style=\"padding-left: 30px;\"><a href=\"http:\/\/akawn.com\/blog\/2012\/10\/a-warning-about-the-trustworthy-database-option\/\" target=\"_blank\">A warning about the TRUSTWORTHY database option<\/a><br \/>\n<a href=\"https:\/\/support.microsoft.com\/en-us\/kb\/2183687\" target=\"_blank\" class=\"broken_link\">Guidelines for using the TRUSTWORTHY database setting in SQL Server<\/a><\/p>\n<p>Having said that, what I really want is\u00a0to set this on a query by query basis AND I don\u2019t want to elevate the rights of an entire stored procedure (in order to execute DBCC TRACEON). So, I decided that I could create a procedure in master, set master\u00a0to trustworthy (<em>with the caution and understanding of the above references\/articles<\/em>), and then I can reference it within my stored procedures NOT having to use use\u00a03-part naming (for the sp_ version of the procedure):<\/p>\n<p>ALTER DATABASE master<br \/>\nSET TRUSTWORTHY ON;<br \/>\nGO<\/p>\n<p>USE master;<br \/>\nGO<\/p>\n<p>ALTER PROCEDURE sp_SetTraceFlag<br \/>\n    (@TraceFlag int,<br \/>\n     @OnOff     bit = 0)<br \/>\nWITH EXECUTE AS OWNER<br \/>\nAS<br \/>\nDECLARE @OnOffStr char(1) = @OnOff;<br \/>\n&#8212; Sysadmins can add supported trace flags and then use this<br \/>\n&#8212; from their applications<br \/>\nIF @TraceFlag NOT IN (<br \/>\n            9481 &#8212; LegacyCE if database is compat mode 120 or higher<br \/>\n          , 2312 &#8212; NewCE if database compat mode 110 or lower<br \/>\n                      )<br \/>\n    BEGIN<br \/>\n        RAISERROR(&#8216;The Trace Flag supplied is not supported. Please contact your system administrator to determine inclusion of this trace flag: %i.&#8217;, 16, 1, @TraceFlag);<br \/>\n        RETURN<br \/>\n    END<br \/>\nELSE<br \/>\n    BEGIN<br \/>\n        DECLARE @ExecStr nvarchar(100);<br \/>\n        IF @OnOff = 1<br \/>\n            SELECT @ExecStr = N&#8217;DBCC TRACEON(&#8216; + CONVERT(nvarchar(4), @TraceFlag) + N&#8217;)&#8217;;<br \/>\n        ELSE<br \/>\n            SELECT @ExecStr = N&#8217;DBCC TRACEOFF(&#8216; + CONVERT(nvarchar(4), @TraceFlag) + N&#8217;)&#8217;;<br \/>\n        &#8212; SELECT (@ExecStr)<br \/>\n        EXEC(@ExecStr)<br \/>\n        &#8212; RAISERROR (N&#8217;TraceFlag: %i has been set to:%s (1 = ON, 0 = OFF).&#8217;, 10, 1, @TraceFlag, @OnOffStr);<br \/>\n    END;<br \/>\nGO<\/p>\n<p>GRANT EXECUTE ON sp_SetTraceFlag TO PUBLIC;<br \/>\nGO<\/p>\n<p>As for using this procedure, you have TWO options.\u00a0If you can modify the stored procedure then you can wrap a single statement with the change in trace flag. But, to make it take effect, you\u2019ll need to recompile that statement. So, if your stored procedure looks like the following:<\/p>\n<p>CREATE PROCEDURE procedure<br \/>\n( params )<br \/>\nAS<br \/>\nstatement;<br \/>\nstatement; &lt;&lt;&lt;&#8212; problematic statement<br \/>\nstatement;<br \/>\nstatement;<br \/>\nGO<\/p>\n<p>Then you can change this to:<\/p>\n<p>CREATE PROCEDURE procedure<br \/>\n( params )<br \/>\nAS<br \/>\nstatement;<br \/>\nEXEC sp_SetTraceFlag 2312, 1<br \/>\nstatement OPTION (RECOMPILE); &lt;&lt;&lt;&#8212; MODIFIED problematic statement<br \/>\nEXEC sp_SetTraceFlag 2312, 0<br \/>\nstatement;<br \/>\nstatement;<br \/>\nGO <\/p>\n<p>If you don\u2019t want to set master to trustworthy then you can add a similar procedure to msdb (which is <strong>already<\/strong> set to TRUSTWORTHY) and then use 3-part naming to reference it.<\/p>\n<p>USE msdb;<br \/>\nGO<\/p>\n<p>CREATE PROCEDURE msdbSetTraceFlag<br \/>\n    (@TraceFlag int,<br \/>\n     @OnOff bit = 0)<br \/>\nWITH EXECUTE AS OWNER<br \/>\nAS<br \/>\nDECLARE @OnOffStr char(1) = @OnOff;<br \/>\n&#8212; Sysadmins can add supported trace flags and then use this<br \/>\n&#8212; from their applications<br \/>\nIF @TraceFlag NOT IN (<br \/>\n              9481 &#8212; LegacyCE if database is compat mode 120 or higher<br \/>\n            , 2312 &#8212; NewCE if database compat mode 110 or lower<br \/>\n                     )<br \/>\n     BEGIN<br \/>\n         RAISERROR(&#8216;The Trace Flag supplied is not supported. Please contact your system administrator to determine inclusion of this trace flag: %i.&#8217;, 16, 1, @TraceFlag);<br \/>\n         RETURN<br \/>\n     END<br \/>\nELSE<br \/>\n     BEGIN<br \/>\n         DECLARE @ExecStr nvarchar(100);<br \/>\n         IF @OnOff = 1<br \/>\n             SELECT @ExecStr = N&#8217;DBCC TRACEON(&#8216; + CONVERT(nvarchar(4), @TraceFlag) + N&#8217;)&#8217;;<br \/>\n         ELSE<br \/>\n             SELECT @ExecStr = N&#8217;DBCC TRACEOFF(&#8216; + CONVERT(nvarchar(4), @TraceFlag) + N&#8217;)&#8217;;<br \/>\n         &#8212; SELECT (@ExecStr)<br \/>\n         EXEC(@ExecStr)<br \/>\n         &#8212; RAISERROR (N&#8217;TraceFlag: %i has been set to:%s (1 = ON, 0 = OFF).&#8217;, 10, 1, @TraceFlag, @OnOffStr);<br \/>\n     END;<br \/>\nGO<\/p>\n<p>GRANT EXECUTE ON msdbSetTraceFlag TO PUBLIC;<br \/>\nGO<\/p>\n<p>To use this, you&#8217;ll need to use 3-part naming:<\/p>\n<p>CREATE PROCEDURE procedure<br \/>\n( params )<br \/>\nAS<br \/>\nstatement;<br \/>\nEXEC msdb.dbo.msdbSetTraceFlag 2312, 1<br \/>\nstatement OPTION (RECOMPILE); &lt;&lt;&lt;&#8212; MODIFIED problematic statement<br \/>\nEXEC msdb.dbo.msdbSetTraceFlag 2312, 0<br \/>\nstatement;<br \/>\nstatement;<br \/>\nGO <\/p>\n<p>Finally, another option is to wrap a statement with the change in trace flag.<\/p>\n<p>EXEC sp_SetTraceFlag 2312, 1;<br \/>\nGO<br \/>\nSTATEMENT or PROCEDURE<br \/>\nGO<br \/>\nEXEC sp_SetTraceFlag 2312, 0;  &#8212; don&#8217;t remember to turn it back off!<br \/>\nGO<\/p>\n<p>Now, you have strategic access to EITHER CE and you don&#8217;t have to elevate anyone&#8217;s specific rights to SysAdmin. You can even let developers use the changes to the new CE or the LegacyCE in their code which is incredibly\u00a0useful!<\/p>\n<p><strong>UPDATE:<\/strong> If you can&#8217;t change the code then another excellent option would be use to a plan guide (permissions are only at the object\/database level:<em> To create a plan guide of type OBJECT, requires ALTER permission on the referenced object. To create a plan guide of type SQL or TEMPLATE, requires ALTER permission on the current database.<\/em>). Here&#8217;s a good post on how to add QUERYTRACEON into a plan guide:\u00a0<a href=\"https:\/\/spaghettidba.com\/2013\/02\/08\/using-querytraceon-in-plan-guides\/\" target=\"_blank\">Using QUERYTRACEON in plan guides<\/a> by <a href=\"http:\/\/twitter.com\/spaghettidba\" target=\"_blank\" class=\"broken_link\">Gianluca Sartori<\/a>.<\/p>\n<p><span style=\"color: #ff0000;\"><strong>UPDATE #2:<\/strong><\/span> Wow! A bit of digging and I stumbled on this ConnectItem: <a href=\"https:\/\/connect.microsoft.com\/SQLServer\/feedback\/details\/1619568\/querytraceon-with-no-additional-permissions\" target=\"_blank\">QUERYTRACEON with no additional permissions<\/a>\u00a0and after I up-voted it, I noticed the workaround by Dan Guzman:\u00a0<em>There is currently undocumented behavior in SQL 2014 that allows use of QUERYTRACEON in a stored procedure without sysadmin permissions regardless of module signing or EXECUTE AS. This is a workaround in situations where the query is already in a stored procedure or when a problem ad-hoc query needing the trace flag can be encapsulated in a proc. However, until it&#8217;s documented, this workaround is at-your-own-risk.<\/em>\u00a0<strong>I did NOT know this&#8230;<\/strong>\u00a0So, QUERYTRACEON CAN be used\u00a0<span style=\"text-decoration: underline;\"><strong>in a stored procedure<\/strong><\/span> even without SysAdmin permissions (I actually like this!). And, I tested this in BOTH 2014 AND 2016 and it works! It&#8217;s not documented nor is it recommended but I&#8217;m happy to see that you do NOT need to do this for QUERYTRACEON. It looks like it evens works in earlier versions. Most of us ran into problems with the clause erroring on adhoc statements so we just didn&#8217;t expect it to work INSIDE of a proc. <strong>Well&#8230; better late than never!<\/strong><\/p>\n<p>Thanks for reading,<br \/>\nKimberly<\/p>\n<h1 class=\"single-title\"><\/h1>\n","protected":false},"excerpt":{"rendered":"<p>IMPORTANT NOTE: Be sure to read the ENTIRE post as there&#8217;s a workaround that QUERYTRACEON is ALLOWED inside of stored procedures&#8230; just NOT in an adHoc statement. Not documented but I think this is excellent! There are still some uses of what I&#8217;ve written but this specific use is much easier because of this undoc&#8217;ed [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[95,97,94,91,96,72,78],"tags":[],"class_list":["post-2805","post","type-post","status-publish","format-standard","hentry","category-application-development","category-cardinality-estimator","category-database-development","category-sql-server-2014","category-sql-server-2016","category-statistics","category-tips"],"_links":{"self":[{"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/posts\/2805","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/comments?post=2805"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/posts\/2805\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/media?parent=2805"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/categories?post=2805"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/kimberly\/wp-json\/wp\/v2\/tags?post=2805"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}