<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://sqlblog.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Search results matching tags 'T-SQL', 'Development', 'Tips', 'Best Practices', and 'SQL Techniques'</title><link>http://sqlblog.com/search/SearchResults.aspx?o=DateDescending&amp;tag=T-SQL,Development,Tips,Best+Practices,SQL+Techniques&amp;orTags=0</link><description>Search results matching tags 'T-SQL', 'Development', 'Tips', 'Best Practices', and 'SQL Techniques'</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>disallow results from triggers</title><link>http://sqlblog.com/blogs/louis_davidson/archive/2009/06/06/disallow-results-from-triggers.aspx</link><pubDate>Sun, 07 Jun 2009 02:38:06 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:14514</guid><dc:creator>drsql</dc:creator><description>&lt;p&gt;A setting that I noticed a while back when looking at sys.configurations was &lt;strong&gt;disallow results from triggers.&lt;/strong&gt;&amp;#160; Triggers are one of my favorite subjects, and you will find a lot of good uses of them in my book (triggers are also well named, as poor usage of them will allow you to shoot your foot off in a heartbeat as I discussed in my earlier post: &lt;a title="http://sqlblog.com/blogs/louis_davidson/archive/2008/07/13/triggers-evil.aspx" href="http://sqlblog.com/blogs/louis_davidson/archive/2008/07/13/triggers-evil.aspx"&gt;http://sqlblog.com/blogs/louis_davidson/archive/2008/07/13/triggers-evil.aspx&lt;/a&gt;). &lt;/p&gt;  &lt;p&gt;One of the main ways you can mess up your system is to have triggers that return results, often by accident, that the client doesn’t know about.&amp;#160; There are three kinds of return values that are interesting:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Result sets &lt;/li&gt;    &lt;li&gt;Raiserror messages &lt;/li&gt;    &lt;li&gt;Rowcount messages &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Also note that this is an overall system configuration. So it will disallow results from triggers in all of your databases.&amp;#160; It is never a good idea to return data from a trigger, but it is actually pretty useful for testing.&amp;#160; In the following code, I will test this setting and show how it will affect returning data to the client from the aforementioned methods.&amp;#160; &lt;/p&gt;  &lt;p&gt;What will the setting do? It will raise an error if you try to do a result set.&amp;#160; It will automatically suppress rowcount messages (without the need for SET NOCOUNT ON), but will allow error messages (I won’t cover it here, but error messages can cause issues with transactions in triggers, particularly when coupled with TRY…CATCH blocks).&lt;/p&gt;  &lt;p&gt;To demonstrate the setting, I will use tempdb.&amp;#160; Checking the system setting for your server, use:&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;SELECT value      &lt;br /&gt;FROM&amp;#160;&amp;#160;&amp;#160; sys.configurations       &lt;br /&gt;WHERE name = 'disallow results from triggers'&lt;/font&gt; &lt;/p&gt;  &lt;p&gt;This will probably return, unless you have discovered the setting before I did, in which case you probably aren’t reading any longer, so there.&lt;/p&gt;  &lt;p&gt;---------    &lt;br /&gt;0 &lt;/p&gt;  &lt;p&gt;To show you the effect of this setting , let's build the following scenario. The trigger will return 3 types of values&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;the contents of the table named triggerResult &lt;/li&gt;    &lt;li&gt;the rowcount of rows affected from an insert statement like you would normally have in a database &lt;/li&gt;    &lt;li&gt;the rowcount of the rows from the insert statement into a temp table &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;To start, we create 2 tables, one as the “main” table, and another that will hold the results of a side effect causing query:&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;--primary test table      &lt;br /&gt;create table triggerResult       &lt;br /&gt;(       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; triggerResultId int primary key       &lt;br /&gt;)&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;--holds our side effect to prove the trigger executed       &lt;br /&gt;create table triggerResultSideEffect       &lt;br /&gt;(       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; triggerResultId int       &lt;br /&gt;)&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Then we will create the trigger that gives us several different types of output    &lt;br /&gt;    &lt;br /&gt;&lt;font size="2" face="Courier New"&gt;create trigger triggerResult$insertTrigger      &lt;br /&gt;on triggerResult       &lt;br /&gt;after insert       &lt;br /&gt;as       &lt;br /&gt;begin       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; --returns a result set       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; select triggerResultId       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; from&amp;#160;&amp;#160; triggerResult&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160; --side effect like you might expect in a trigger      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; insert into triggerResultSideEffect (triggerResultId)       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; select triggerResultId       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; from&amp;#160;&amp;#160; inserted&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160; --just to get a fixed rowcount output      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; declare @test table (value char(1))       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; insert into @test       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; values (1)       &lt;br /&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160; --and a couple of errors      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; raiserror ('Low',10,1)       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; raiserror ('Normal',16,1) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;end      &lt;br /&gt;&lt;/font&gt;Now we will test out the trigger by inserting one row into the triggerResult table:&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;insert into triggerResult      &lt;br /&gt;values (1)&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;This returns (the final rows affected is from the original statement): &lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;triggerResultId      &lt;br /&gt;---------------       &lt;br /&gt;1 &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(1 row(s) affected) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(1 row(s) affected) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(1 row(s) affected) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;Low      &lt;br /&gt;Msg 50000, Level 16, State 1, Procedure triggerResult$insertTrigger, Line 22       &lt;br /&gt;Normal &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(1 row(s) affected) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Now try a multi-row operation, to see the difference &lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;insert into triggerResult      &lt;br /&gt;values (2),(3),(4)&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;This returns the following (which you can clearly now see the different number of rows that were affected by each statement, 4 from the one that returns all of the rows in the table, 3 from the side effect query that inserts the rows from the inserted table.&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;triggerResultId      &lt;br /&gt;---------------       &lt;br /&gt;1       &lt;br /&gt;2       &lt;br /&gt;3       &lt;br /&gt;4 &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(4 row(s) affected) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(3 row(s) affected) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(1 row(s) affected) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;Low      &lt;br /&gt;Msg 50000, Level 16, State 1, Procedure triggerResult$insertTrigger, Line 22       &lt;br /&gt;Normal &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(3 row(s) affected)&lt;/font&gt; &lt;/p&gt;  &lt;p&gt;Now, change the setting to disallow trigger results (you may need to do allow advanced options) &lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;exec sp_configure 'show advanced options',1      &lt;br /&gt;RECONFIGURE       &lt;br /&gt;exec sp_configure 'disallow results from triggers',1       &lt;br /&gt;RECONFIGURE&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Now, just to make sure of where our data is, we know that there are 4 rows in the triggerResult table. Let’s check the triggerResultSideEffect table contents:&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;SELECT *      &lt;br /&gt;FROM&amp;#160;&amp;#160; triggerResultSideEffect       &lt;br /&gt;ORDER&amp;#160; BY triggerResultId &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;This returns, showing all of the values we have inserted: &lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;triggerResultId      &lt;br /&gt;---------------       &lt;br /&gt;1       &lt;br /&gt;2       &lt;br /&gt;3       &lt;br /&gt;4&lt;/font&gt; &lt;/p&gt;  &lt;p&gt;Now, trying to run the statement with the same trigger:&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;insert into triggerResult      &lt;br /&gt;values (5)&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;This will cause the following error message: &lt;/p&gt;  &lt;p&gt;&lt;font color="#800000" size="2" face="Courier New"&gt;Msg 524, Level 16, State 1, Procedure triggerResult$insertTrigger, Line 6      &lt;br /&gt;A trigger returned a resultset and the server option 'disallow results from triggers' is true.&lt;/font&gt; &lt;/p&gt;  &lt;p&gt;Altering the trigger, just remove the statement that returns a result set:&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;alter trigger triggerResult$insertTrigger      &lt;br /&gt;on triggerResult       &lt;br /&gt;after insert       &lt;br /&gt;as       &lt;br /&gt;begin       &lt;br /&gt;&lt;strong&gt;&lt;em&gt;&amp;#160;&amp;#160;&amp;#160; ----returns a result set          &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; --select triggerResultId           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; --from&amp;#160;&amp;#160; triggerResult           &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; --side effect like you might expect in a trigger&lt;/em&gt;&lt;/strong&gt;&amp;#160;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160; insert into triggerResultSideEffect (triggerResultId)      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; select triggerResultId       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; from&amp;#160;&amp;#160; inserted &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160; --just to get a fixed rowcount output      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; declare @test table (value char(1))       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; insert into @test       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; values (1) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;&amp;#160;&amp;#160;&amp;#160; --and a couple of errors      &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; raiserror ('Low',10,1)       &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; raiserror ('Normal',16,1) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;end &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Now, re-executing the statement with no results being returned: &lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;insert into triggerResult      &lt;br /&gt;values (5) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;This simply returns the error message that are returned, and the rows affected message from the insert statement: &lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;Low      &lt;br /&gt;Msg 50000, Level 16, State 1, Procedure triggerResult$insertTrigger, Line 22       &lt;br /&gt;Normal &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(1 row(s) affected)&lt;/font&gt; &lt;/p&gt;  &lt;p&gt;Just to make sure that the rows were created:&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;select *      &lt;br /&gt;from&amp;#160;&amp;#160; triggerResult &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;select *      &lt;br /&gt;from&amp;#160;&amp;#160; triggerResultSideEffect&lt;/font&gt; &lt;/p&gt;  &lt;p&gt;This returns: &lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;triggerResultId      &lt;br /&gt;---------------       &lt;br /&gt;1       &lt;br /&gt;2       &lt;br /&gt;3       &lt;br /&gt;4       &lt;br /&gt;5 &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(5 row(s) affected) &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;triggerResultId      &lt;br /&gt;---------------       &lt;br /&gt;1       &lt;br /&gt;4       &lt;br /&gt;3       &lt;br /&gt;2       &lt;br /&gt;5 &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font size="2" face="Courier New"&gt;(5 row(s) affected)&lt;/font&gt; &lt;/p&gt;  &lt;p&gt;Which shows that the data was inserted..&lt;/p&gt;  &lt;p&gt;Note that books online states: This feature will be removed in the next version of Microsoft SQL Server. Do not use this feature in new development work, and modify applications that currently use this feature as soon as possible. We recommend that you set this value to 1. &lt;/p&gt;  &lt;p&gt;This means that the ability to return result sets from a trigger will go away in the next version of SQL Server.&amp;#160; Returning results from a trigger is certainly not something that has ever been a good practice, so if this does in fact occur, it is a good setting to set in your development servers and begin to use.&lt;/p&gt;</description></item></channel></rss>