When an update doesn’t update

Tell me if you’ve heard this one before:

I changed data from my application, but when I checked the database, I couldn’t see the change!

I’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 together to provide some things you can investigate if this happens to you – you are sure that you updated the data, but when you check using SQL Server Management Studio (SSMS), your change isn’t there. For the remainder of the post, I’m going to use the word “update” to mean any change to the data, even MERGE {shudder}.

The causes behind this issue can usually be lumped into three categories:

1. Misdirection

Most commonly, the problem is that app is updating a table that is in a different location than the one you’re checking. Some examples of things you should verify:

  • That the connections for the application and SSMS:
    • Point to the same server/instance – LOCALHOST and LOCALHOST\SQLEXPRESS 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.
    • Point to the same database – in many cases we might have the same table in multiple databases, even on the same server.
    • Use the same authentication method and credentials – since the user’s default schema or the login’s default database might point you in different directions.
    • Do not use the dreaded User Instances or AttachDbFileName features, since even if all the other connection string attributes are the same, you are definitely looking at two independent copies of the database.
  • That both the update query and the validation query:
    • Explicitly use schema references for all objects.
    • Do not inject database, server, or table names based on criteria that may differ.
  • That there isn’t some background job that runs and cleans up data it deems non-compliant for whatever reason.
  • That you’re not assuming some large value has been corrupted because SSMS doesn’t show the whole value. In different contexts, it may only show 256, 8,192, 43,697, or 65,535 characters. This is usually not a problem with the data or the operation but rather just the way the tool presents data. You can check with DATALENGTH that all of the data is there, or RIGHT to verify that the end of the string is intact. See Validate the contents of large dynamic SQL strings for more information, and have a generous look around the maximum characters settings in Tools > Options > Query Results > SQL Server > Results to Grid (max 64K) | Results to Text (max 8K).

2. It’s not always caching…

…but, sometimes, it’s caching. Check if any of the following could be true:

  • You’re querying from a readable secondary or replication subscriber, and the change just hasn’t made it there yet.
  • You’re using snapshot or NOLOCK and are seeing an earlier (or perhaps even invalid) version of the row.
  • You’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’ve waited long enough.

3. Swallowed exceptions

Sometimes the issue is that we assume the statement succeeded simply because we didn’t see an error message. This is not always a safe assumption! Not all errors bubble up to the caller, for example:

  • 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.
  • If the query is executed asynchronously, the caller may never see the exception, and doesn’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.
  • There may be a trigger that rolled it back (and the application ignored the exception), or an INSTEAD OF trigger that simply didn’t end up performing the update, in which case the application wouldn’t even have an exception to ignore.
  • You may have TRY/CATCH or other error handling / rollback mechanisms in your code (in the query or in the application). CATCH 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.
  • At an even more basic level, your code or procedure may have a debug or auto-rollback flag inadvertently set to true.

Conclusion

There are many reasons why it may seem like an update succeeded, but validation suggests it didn’t. Usually it is the case that either you weren’t checking the right place, you checked too quickly, or there was a failure. Hopefully the above gives a healthy set of things to check if you are ever in this scenario.