{"id":98571,"date":"2023-11-27T00:38:59","date_gmt":"2023-11-27T00:38:59","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=98571"},"modified":"2023-10-19T20:43:28","modified_gmt":"2023-10-19T20:43:28","slug":"when-an-update-doesnt-update","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/when-an-update-doesnt-update\/","title":{"rendered":"When an update doesn&#8217;t update"},"content":{"rendered":"<p>Tell me if you&#8217;ve heard this one before:<\/p>\n<blockquote>\n<p>I changed data from my application, but when I checked the database, I couldn&#8217;t see the change!<\/p>\n<\/blockquote>\n<p>I&#8217;ve seen this. <strong>Loads<\/strong>. It can be quite perplexing for folks because they expect to see an error message if the insert, update, or delete failed. I put this post together to provide some things you can investigate if this happens to you &#8211; you are sure that you updated the data, but when you check using SQL Server Management Studio (SSMS), your change isn&#8217;t there. For the remainder of the post, I&#8217;m going to use the word &#8220;update&#8221; to mean any change to the data, even <a href=\"https:\/\/sqlblog.org\/merge\" target=\"_blank\" rel=\"noopener\"><code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">MERGE<\/code><\/a> {shudder}.<\/p>\n<p>The causes behind this issue can usually be lumped into three categories:<\/p>\n<h3>1. Misdirection<\/h3>\n<p>Most commonly, the problem is that app is updating a table that is in a different location than the one you&#8217;re checking. Some examples of things you should verify:<\/p>\n<ul>\n<li>That the connections for the application and SSMS:\n<ul>\n<li>Point to the same server\/instance &#8211; <code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">LOCALHOST<\/code> and <code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">LOCALHOST\\SQLEXPRESS<\/code> are not the same, and if the app is running on a different machine or a different domain, it may not be safe to trust identical DNS.<\/li>\n<li>Point to the same database &#8211; in many cases we might have the same table in multiple databases, even on the same server.<\/li>\n<li>Use the same authentication method and credentials &#8211; since the user&#8217;s default schema or the login&#8217;s default database might point you in different directions.<\/li>\n<li>Do not use the dreaded <a href=\"https:\/\/www.sentryone.com\/blog\/aaronbertrand\/bad-habits-attachdbfilename\" target=\"_blank\" rel=\"noopener\">User Instances or <code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">AttachDbFileName<\/code> features<\/a>, since even if all the other connection string attributes are the same, you are definitely looking at two independent copies of the database.<\/li>\n<\/ul>\n<\/li>\n<li>That both the update query and the validation query:\n<ul>\n<li>Explicitly <a href=\"https:\/\/sqlblog.org\/2019\/09\/12\/bad-habits-to-kick-avoiding-the-schema-prefix\" target=\"_blank\" rel=\"noopener\">use schema references<\/a> for all objects.<\/li>\n<li>Do not inject database, server, or table names based on criteria that may differ.<\/li>\n<\/ul>\n<\/li>\n<li>That there isn&#8217;t some background job that runs and cleans up data it deems non-compliant for whatever reason.<\/li>\n<li>That you&#8217;re not assuming some large value has been corrupted because SSMS doesn&#8217;t show <em>the whole value<\/em>. In different contexts, it may only show 256, 8,192, 43,697, or 65,535 characters. <strong>This is usually not a problem with the data or the operation<\/strong> but rather just the way the tool presents data. You can check with <code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">DATALENGTH<\/code> that all of the data is there, or <code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">RIGHT<\/code> to verify that the <em>end<\/em> of the string is intact. See <a href=\"https:\/\/www.mssqltips.com\/sqlservertip\/3185\/validate-the-contents-of-large-dynamic-sql-strings-in-sql-server\/?utm_source=AaronBertrand\" target=\"_blank\" rel=\"noopener\">Validate the contents of large dynamic SQL strings<\/a> for more information, and have a generous look around the maximum characters settings in Tools &gt; Options &gt; Query Results &gt; SQL Server &gt; Results to Grid (max 64K) | Results to Text (max 8K).<\/li>\n<\/ul>\n<h3>2. It&#8217;s not always caching&#8230;<\/h3>\n<p>&#8230;but, sometimes, it&#8217;s caching. Check if any of the following could be true:<\/p>\n<ul>\n<li>You&#8217;re querying from a readable secondary or replication subscriber, and the change just hasn&#8217;t made it there yet.<\/li>\n<li>You&#8217;re using snapshot or <code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">NOLOCK<\/code> and are seeing an earlier (or perhaps even invalid) version of the row.<\/li>\n<li>You&#8217;re not using SSMS to verify. Another application (even the same application!) might display cached results (even data it just updated!). Make sure you have a way to bust the cache and force the application to reload data from the source, and if the source is a secondary of any kind, that you&#8217;ve waited long enough.<\/li>\n<\/ul>\n<h3>3. Swallowed exceptions<\/h3>\n<p>Sometimes the issue is that we <em>assume<\/em> the statement succeeded simply because we didn&#8217;t see an error message. This is not always a safe assumption! Not all errors bubble up to the caller, for example:<\/p>\n<ul>\n<li>If the query sent to SQL Server actually went through an intermediary, like an API or middle tier, it may not expose exceptions by default.<\/li>\n<li>If the query is executed asynchronously, the caller may never see the exception, and doesn&#8217;t wait for success or failure. The update may have still been happening when you checked and, in the event of blocking, may still be blocked as you are reading this.<\/li>\n<li>There may be a trigger that rolled it back (and the application ignored the exception), or an <code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">INSTEAD OF<\/code> trigger that simply didn&#8217;t end up performing the update, in which case the application wouldn&#8217;t even have an exception to ignore.<\/li>\n<li>You may have <code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">TRY\/CATCH<\/code> or other error handling \/ rollback mechanisms in your code (in the query or in the application). <code style=\"font-family: consolas, lucida console, courier new; background: #e4e4e4; padding: 3px 4px 1px 4px; border-radius: 3px;\">CATCH<\/code> could easily be ignoring the exception or raising a generic exception of a lower severity, or a rollback could be happening without raising any exceptions.<\/li>\n<li>At an even more basic level, your code or procedure may have a debug or auto-rollback flag inadvertently set to true.<\/li>\n<\/ul>\n<h3>Conclusion<\/h3>\n<p>There are many reasons why it may <em>seem<\/em> like an update succeeded, but validation suggests it didn&#8217;t. Usually it is the case that either you weren&#8217;t checking the right place, you checked too quickly, or there <em>was<\/em> a failure. Hopefully the above gives a healthy set of things to check if you are ever in this scenario.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Tell me if you&#8217;ve heard this one before: I changed data from my application, but when I checked the database, I couldn&#8217;t see the change! I&#8217;ve seen this. Loads. It can be quite perplexing for folks because they expect to see an error message if the insert, update, or delete failed. I put this post&#8230;&hellip;<\/p>\n","protected":false},"author":341115,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,143531,143539],"tags":[147174],"coauthors":[158980],"class_list":["post-98571","post","type-post","status-publish","format-standard","hentry","category-featured","category-t-sql-programming-sql-server","category-theory-and-design","tag-t-sql-coding"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/98571","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/341115"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=98571"}],"version-history":[{"count":19,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/98571\/revisions"}],"predecessor-version":[{"id":98760,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/98571\/revisions\/98760"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=98571"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=98571"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=98571"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=98571"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}