<?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 'Merge' and 'Triggers'</title><link>http://sqlblog.com/search/SearchResults.aspx?o=DateDescending&amp;tag=Merge,Triggers&amp;orTags=0</link><description>Search results matching tags 'Merge' and 'Triggers'</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>It's 2008, and @@ROWCOUNT ain't what it used to be. Don't use it in triggers.</title><link>http://sqlblog.com/blogs/steve_kass/archive/2009/04/29/It_2700_s-2008-and-rowcount-ain_2700_t-what-it-used-to-be.-Don_2700_t-use-it-in-triggers_2100_.aspx</link><pubDate>Wed, 29 Apr 2009 04:57:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:13623</guid><dc:creator>skass</dc:creator><description>&lt;SPAN style="WIDOWS:2;TEXT-TRANSFORM:none;TEXT-INDENT:0px;BORDER-COLLAPSE:separate;FONT:16px 'Times New Roman';WHITE-SPACE:normal;ORPHANS:2;LETTER-SPACING:normal;WORD-SPACING:0px;-webkit-text-size-adjust:auto;-webkit-border-horizontal-spacing:0px;-webkit-border-vertical-spacing:0px;-webkit-text-decorations-in-effect:none;-webkit-text-stroke-width:0px;" class=Apple-style-span&gt;
&lt;DIV style="PADDING-BOTTOM:8px;PADDING-LEFT:8px;PADDING-RIGHT:8px;FONT-FAMILY:Arial, Helvetica, sans-serif;FONT-SIZE:10pt;PADDING-TOP:8px;"&gt;
&lt;P&gt;During SQL Server 2008 beta testing, Aaron Bertrand &lt;A title="Connect Item 298395" href="https://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=298395"&gt;noted&lt;/A&gt; that the value of @@rowcount inside a trigger could be unexpected, if the triggering statement was MERGE.&lt;/P&gt;
&lt;P&gt;The consequences of this can be pretty bad, but fortunately there's a simple workaround. You need to do something if anyone might invoke MERGE against tables with triggers that contain @@rowcount checks &lt;/P&gt;
&lt;P&gt;A MERGE statement can cause as many as three triggers to fire. Within each of them, the value of @@rowcount is the number of rows affected by the entire MERGE statement, i.e. the total number rows inserted, updated, &lt;I&gt;or&lt;/I&gt; deleted by the various merge clauses.&lt;/P&gt;
&lt;P&gt;Books Online mentions this in the article on MERGE: &lt;I&gt;When used after MERGE, @@ROWCOUNT (Transact-SQL) returns the total number of rows inserted, updated, and deleted to the client.&lt;/I&gt;&lt;/P&gt;
&lt;P&gt;Elsewhere, however, Books Online continues to give old advice that as of 2008 is not good advice. For example, in the article Multirow considerations for DML triggers, Books Online says, &lt;I&gt;For example, the&lt;SPAN class=Apple-converted-space&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;CODE&gt;@@ROWCOUNT&lt;/CODE&gt;&lt;SPAN class=Apple-converted-space&gt;&amp;nbsp;&lt;/SPAN&gt;function can be used in the logic of the trigger to distinguish between a single and a multirow insert.&lt;/I&gt;&lt;/P&gt;
&lt;P&gt;Not so, I'm afraid. The value of @@rowcount in an INSERT trigger, say, will always be at least equal to the number of rows inserted, but it can be greater. For example, a MERGE statement could have an INSERT action that doesn't occur, but an UPDATE one that updates 3 rows. The INSERT trigger will be called, because the MERGE statement's INSERT section "inserted zero rows."&lt;/P&gt;
&lt;P&gt;Here's an example from AdventureWorks2008 to show what can go wrong.&lt;/P&gt;
&lt;P&gt;I've added a GrandTotal table containing one row and column. GrandTotal.gt is supposed to keep track of the grand total of all purchase SubTotals.&lt;/P&gt;
&lt;P&gt;This code creates the table and gives it a value that's initially correct for the data in AdventureWorks2008:&lt;/P&gt;&lt;FONT color=#0000ff&gt;&lt;/FONT&gt;
&lt;P&gt;&lt;FONT color=#0000ff&gt;USE&lt;/FONT&gt; AdventureWorks2008&lt;FONT color=#808080&gt;;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;GO&lt;BR&gt;&amp;nbsp; CREATE&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;TABLE&lt;/FONT&gt; GrandTotal&lt;FONT color=#808080&gt;(&lt;BR&gt;&lt;/FONT&gt;&amp;nbsp; gt &lt;FONT color=#0000ff&gt;DECIMAL&lt;/FONT&gt;&lt;FONT color=#808080&gt;(&lt;/FONT&gt;18&lt;FONT color=#808080&gt;,&lt;/FONT&gt;2&lt;FONT color=#808080&gt;)&lt;BR&gt;);&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;INSERT&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;INTO&lt;/FONT&gt; GrandTotal;&lt;BR&gt;&lt;FONT color=#0000ff&gt;SELECT&lt;/FONT&gt; &lt;FONT color=#ff00ff&gt;sum&lt;/FONT&gt;&lt;FONT color=#808080&gt;(&lt;/FONT&gt;SubTotal&lt;FONT color=#808080&gt;);&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;FROM&lt;/FONT&gt; Purchasing&lt;FONT color=#808080&gt;.&lt;/FONT&gt;PurchaseOrderHeader&lt;FONT color=#808080&gt;;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;GO&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#0000ff&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;&lt;FONT color=#000000&gt;This code creates a trigger to update the grand total whenever new line items are inserted into the table:&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#0000ff&gt;CREATE&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;TRIGGER&lt;/FONT&gt; NewPODetail3&lt;BR&gt;&lt;FONT color=#0000ff&gt;ON&lt;/FONT&gt; Purchasing&lt;FONT color=#808080&gt;.&lt;/FONT&gt;PurchaseOrderDetail&lt;BR&gt;&lt;FONT color=#0000ff&gt;FOR&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;INSERT&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;AS&lt;BR&gt;IF&lt;/FONT&gt; &lt;FONT color=#ff00ff&gt;@@ROWCOUNT&lt;/FONT&gt; &lt;FONT color=#808080&gt;&amp;gt;&lt;/FONT&gt; 0&lt;BR&gt;&lt;FONT color=#0000ff&gt;UPDATE&lt;/FONT&gt; GrandTotal &lt;FONT color=#0000ff&gt;SET&lt;BR&gt;&lt;/FONT&gt;gt &lt;FONT color=#808080&gt;+=&lt;/FONT&gt;&lt;FONT color=#0000ff&gt; &lt;/FONT&gt;&lt;FONT color=#808080&gt;(&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;SELECT&lt;/FONT&gt; &lt;FONT color=#ff00ff&gt;SUM&lt;/FONT&gt;&lt;FONT color=#808080&gt;(&lt;/FONT&gt;LineTotal&lt;FONT color=#808080&gt;) &lt;/FONT&gt;&lt;FONT color=#0000ff&gt;FROM&lt;/FONT&gt; inserted);&lt;BR&gt;&lt;FONT color=#0000ff&gt;GO&lt;/FONT&gt;&lt;/P&gt;&lt;FONT color=#0000ff&gt;&lt;/FONT&gt;
&lt;DIV&gt;
&lt;P&gt;&lt;FONT color=#0000ff&gt;&lt;FONT color=#000000&gt;And this MERGE statement (enclosed in a rolled-back transaction here so that no data in the database is modified by this test script) has the unintended result of updating the grand total from $63,791,994.84 to NULL. &lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#0000ff&gt;begin&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;tran&lt;BR&gt;select&lt;/FONT&gt; &lt;FONT color=#808080&gt;*&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;From&lt;/FONT&gt; GrandTotal&lt;FONT color=#808080&gt;;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;merge&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;into&lt;/FONT&gt; Purchasing&lt;FONT color=#808080&gt;.&lt;/FONT&gt;PurchaseOrderDetail &lt;FONT color=#0000ff&gt;as&lt;/FONT&gt; P&lt;BR&gt;&lt;FONT color=#0000ff&gt;using &lt;/FONT&gt;&lt;FONT color=#808080&gt;(&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;&amp;nbsp; select&lt;/FONT&gt; 2&lt;FONT color=#808080&gt;,&lt;/FONT&gt;&lt;FONT color=#ff0000&gt;'20010715'&lt;/FONT&gt;&lt;FONT color=#808080&gt;,&lt;/FONT&gt;100&lt;FONT color=#808080&gt;,&lt;/FONT&gt;318&lt;FONT color=#808080&gt;,&lt;/FONT&gt;100&lt;FONT color=#808080&gt;,&lt;/FONT&gt;318&lt;FONT color=#808080&gt;,&lt;/FONT&gt;0&lt;FONT color=#808080&gt;,&lt;/FONT&gt;&lt;FONT color=#ff0000&gt;'20090101'&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#808080&gt;)&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;as&lt;/FONT&gt; T&lt;FONT color=#808080&gt;(&lt;/FONT&gt;PurchaseOrderID&lt;FONT color=#808080&gt;,&lt;/FONT&gt;DueDate&lt;FONT color=#808080&gt;,&lt;/FONT&gt;OrderQty&lt;FONT color=#808080&gt;,&lt;/FONT&gt;ProductID&lt;FONT color=#808080&gt;,&lt;/FONT&gt;UnitPrice&lt;FONT color=#808080&gt;,&lt;/FONT&gt;ReceivedQty&lt;FONT color=#808080&gt;,&lt;/FONT&gt;RejectedQty&lt;FONT color=#808080&gt;,&lt;/FONT&gt;ModifiedDate&lt;FONT color=#808080&gt;)&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;on&lt;/FONT&gt; T&lt;FONT color=#808080&gt;.&lt;/FONT&gt;PurchaseOrderID &lt;FONT color=#808080&gt;=&lt;/FONT&gt; P&lt;FONT color=#808080&gt;.&lt;/FONT&gt;PurchaseOrderID&lt;BR&gt;&lt;FONT color=#0000ff&gt;when&lt;/FONT&gt; &lt;FONT color=#808080&gt;matched&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;then&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;&amp;nbsp; update&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;set&lt;/FONT&gt; ModifiedDate &lt;FONT color=#808080&gt;=&lt;/FONT&gt; T&lt;FONT color=#808080&gt;.&lt;/FONT&gt;ModifiedDate&lt;BR&gt;&lt;FONT color=#0000ff&gt;when&lt;/FONT&gt; &lt;FONT color=#808080&gt;not&lt;/FONT&gt; &lt;FONT color=#808080&gt;matched&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;by&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;target&lt;/FONT&gt; &lt;FONT color=#808080&gt;and&lt;/FONT&gt; PurchaseOrderID &lt;FONT color=#808080&gt;&amp;lt;&lt;/FONT&gt; 0 &lt;FONT color=#0000ff&gt;then&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT color=#0000ff&gt;insert &lt;/FONT&gt;&lt;FONT color=#808080&gt;(&lt;/FONT&gt;PurchaseOrderID&lt;FONT color=#808080&gt;,&lt;/FONT&gt;DueDate&lt;FONT color=#808080&gt;,&lt;/FONT&gt;OrderQty&lt;FONT color=#808080&gt;,&lt;/FONT&gt;ProductID&lt;FONT color=#808080&gt;,&lt;/FONT&gt;UnitPrice&lt;FONT color=#808080&gt;,&lt;/FONT&gt;ReceivedQty&lt;FONT color=#808080&gt;,&lt;/FONT&gt;RejectedQty&lt;FONT color=#808080&gt;,&lt;/FONT&gt;ModifiedDate&lt;FONT color=#808080&gt;)&lt;BR&gt;&amp;nbsp; &lt;/FONT&gt;&lt;FONT color=#0000ff&gt;values &lt;/FONT&gt;&lt;FONT color=#808080&gt;(&lt;/FONT&gt;3&lt;FONT color=#808080&gt;,&lt;/FONT&gt;&lt;FONT color=#ff0000&gt;'20010715'&lt;/FONT&gt;&lt;FONT color=#808080&gt;,&lt;/FONT&gt;100&lt;FONT color=#808080&gt;,&lt;/FONT&gt;318&lt;FONT color=#808080&gt;,&lt;/FONT&gt;100&lt;FONT color=#808080&gt;,&lt;/FONT&gt;318&lt;FONT color=#808080&gt;,&lt;/FONT&gt;0&lt;FONT color=#808080&gt;,&lt;/FONT&gt;&lt;FONT color=#ff0000&gt;'20090101'&lt;/FONT&gt;&lt;FONT color=#808080&gt;);&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;select&lt;/FONT&gt; &lt;FONT color=#808080&gt;*&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;From&lt;/FONT&gt; GrandTotal&lt;BR&gt;&lt;FONT color=#0000ff&gt;rollback&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;tran&lt;/FONT&gt;&lt;FONT color=#808080&gt;;&lt;BR&gt;&lt;/P&gt;&lt;/FONT&gt;
&lt;P&gt;Oops.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;MERGE is only beginning to get&amp;nbsp;used, and for a variety of triggers, a too-large @@rowcount won't cause a problem. But I think the number of people who might get caught by this problem will grow.&lt;/P&gt;
&lt;P&gt;Don't use @@rowcount in triggers, unless you're certain it doesn't cause problems if it's higher than the real rowcount you want (which might be zero). As an alternative, Itzik suggested to me this (for INSERT; the others are similar):&lt;/P&gt;&lt;FONT color=#0000ff&gt;&lt;/FONT&gt;
&lt;P&gt;&lt;FONT color=#0000ff&gt;declare&lt;/FONT&gt; @rc &lt;FONT color=#0000ff&gt;int&lt;/FONT&gt;&lt;FONT color=#808080&gt;;&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;with&lt;/FONT&gt; Two &lt;FONT color=#0000ff&gt;as &lt;/FONT&gt;&lt;FONT color=#808080&gt;(&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;select&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;top &lt;/FONT&gt;&lt;FONT color=#808080&gt;(&lt;/FONT&gt;2&lt;FONT color=#808080&gt;)&lt;/FONT&gt; &lt;FONT color=#808080&gt;*&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;from&lt;/FONT&gt; inserted&lt;FONT color=#808080&gt;)&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff&gt;select&lt;/FONT&gt; @rc &lt;FONT color=#808080&gt;=&lt;/FONT&gt; &lt;FONT color=#ff00ff&gt;count&lt;/FONT&gt;&lt;FONT color=#808080&gt;(*)&lt;/FONT&gt; &lt;FONT color=#0000ff&gt;from&lt;/FONT&gt; Two&lt;FONT color=#808080&gt;;&lt;/FONT&gt;&lt;/P&gt;&lt;FONT color=#808080&gt;&lt;/FONT&gt;&lt;/DIV&gt;The value of @rc will then be 0, 1, or 2, depending on whether 0, 1, or more than 1 rows were inserted.&lt;/DIV&gt;
&lt;P&gt;Steve Kass&lt;BR&gt;&lt;A href="http://www.stevekass.com/"&gt;http://www.stevekass.com&lt;/A&gt;&lt;/P&gt;&lt;/SPAN&gt;</description></item></channel></rss>