Data locks caused by non-committed transactions - sql-server

My web application is connected to a SQL Server 2016 Express database, and we have been plagued by data locks in certain areas of the system.
My colleague noticed just today that, when a KILL process was used to kill a long-running transaction, that several transactions that had ostensibly already been committed were rolled-back.
I have checked using #vladV's script on In SQL Server, how do I know what transaction mode I'm currently using? that in fact the database seems to be in auto-commit mode.
So therefore it must be that that something in the database is opening a new transaction and not committing it.
So I found in the database four stored procedures which contain the following
SET IMPLICIT_TRANSACTIONS ON
... code ...
IF ##TRAN_COUNT>0 COMMIT WORK
Am I right in saying that in some/most situations such a stored procedure would leave transactions open, even after exiting the stored procedure, and that this could be the source of the data-lock problems?
And if so, then could I just remedy the code by doing
SET IMPLICIT_TRANSACTIONS OFF
when the stored procedure exits?

Am I right in saying that in some/most situations such a stored procedure would leave transactions open
Some. Depends on what comes after. With IMPLICIT TRANSACTIONS in SQL Server, transactions are not automatically started until you run a query that reads the database.
could I just remedy the code by doing SET IMPLICIT_TRANSACTIONS OFF
No. That won't end any open transactions.
Note that COMMIT doesn't reduce the ##trancount to 0. It decrements it by 1. So if you have multiple BEGIN TRAN statements, or an explicit BEGIN TRAN after an transaction has implicitly begun, then you will need multiple COMMITs.
You might try
WHILE ##trancount > 0 COMMIT TRANSACTION
which will definitely commit any outstanding transactions.

Related

Automated rollback in Informix 14 database?

I have some scripts including DDL and DML transactions that use an Informix 14 database that I want to run some tests against.
If the tests fail they will often leave the database in an inconsistent state that needs to be manually resolved before the tests can be run again.
I would like to automate this so that the tests do not require manual intervention before running the tests again.
So, is it possible to use rollback and savepoint without locking the database and run some tests on the database?
If you execute BEGIN WORK (or just BEGIN), then everything you do afterwards will be part of the same transaction until you explicitly execute COMMIT WORK (or just COMMIT) or ROLLBACK WORK (or just ROLLBACK) — or until your program exits without explicitly ending the transaction. If your program terminates unexpectedly or carelessly (without explicitly completing the transaction), the transaction will be rolled back.
The transaction will manage all the DDL and DML operations — with the sole exception of some caveats with the TRUNCATE TABLE statement. There are some restrictions on what you can do with the truncated table — basically, it cannot be modified again until the transaction completes.
Of course, this assumes your database is logged. You can't have transactional control in an unlogged database.

Where to put BEGIN TRAN, in the code or in the stored procedure?

Let's say I have a stored procedure that is doing 3 inserts. To make sure everything is working fine, I add a begin and commit tran in the stored procedure.
Then from the code side (.NET, C#), the programmer is also creating a transaction.
What will be the best approach for that?
Having in both places?
Having that in the C# code only?
Having that in the stored procedure only?
It's better to only do it in the stored procedure for a number of reasons:
The procedure can keep better control of when to begin and commit the transaction.
It can also control the isolation level, which should usually be set before the transaction starts.
It keeps database code close to the database (somewhat subjective).
If the connection is severed and the server does not realize, a transaction opened by the client may not be committed or rolled back for some time, and could leave locks hanging, causing a huge chain of blocking
The client starting and committing the transaction requires two extra round-trips over the network, which in a low-latency app may be problematic. (SET NOCOUNT ON should be used for the same reason.) The transaction and associated locking is also extended for that time, casuing further blocking problems.
Do use SET XACT_ABORT ON, in case of exceptions this will cause an automatic rollback and prevent them from leaving hanging transactions.
It may still may sense to use client-side transactions, especially with distributed transactions.
Having transactions in both client code and the procedure is silly and wasteful. Choose one or the other option and stick to it.

Breaking a connection to SQL Server halfway through an execution

If I execute a script against SQL, which has multiple transactions in it, but break the connection half way through, what does SQL do? Does it run the script to completion, or just roll-back the transaction it was currently in (or finish it)?
When SQL Server detects that a connection is broken, it should rollback any current transaction and abort the current batch1.
Any preceding committed transactions will still be committed.
1Here I'm trying to draw the distinction between scripts and batches. Many client tools support scripts containing multiple batches (delimited by GOs) and its the batches that get submitted to SQL Server, sequentially.
Well, depends, if there are multiple transactions in the script SQL, do you put commit into each transaction ? for example, if your script like this
BEGIN TRAN --Transaction 1
INSERT TableA VALUES(value1,value2)
COMMIT TRAN
BEGIN TRAN --Transaction 2
INSERT TableB VALUES(value1,value2)
COMMIT TRAN
if your sql server disconnected, half-way of the execution of the whole script, but while on the way of execution of the script and the sql server has completed transaction 1, and that's committed, then transaction 1 is completed, and you can see the result on tableA, but when the sql server running transaction 2 imidiately and few moment later the transaction 2 yet incomplete and the connection gets down, transaction 2 will be rollbacked, so you cant see the result on TableB
Only committed transactions will be executed.
Other ones will do a roll-back.

Is SQL "inherently transactional"?

I just read in Wikipedia, that SQL is inherently transactional.
I took this to mean that every statement made to a SQL DBMS is treated as a transaction by default. Is this correct?
An example I can think of that might make this question relevant would be if you considered an update statement like this:
UPDATE Employee SET salary=salary * 1.1 WHERE type='clerk';
If this were being processed and there was some failure that caused the DBMS to shutdown, on restart, in a transactional sense, wouldn't the rows that records that were updated be rolled back?
At least in SQL Server, if you run a transaction, opening the line with
BEGIN TRAN
and you don't commit or rollback the transaction, it will ask you (if you try to exit the window) if you want to commit the transactions. If something caused everything to crash, and we had an open transaction (meaning, nothing to close it), it would not be considered committed.
Your question demonstrates another reason why many developers will use a COMMIT TRAN only if there are no errors, so every transaction by default, will rollback.
Disclaimer here: I am ONLY referring to SQL Server - I cannot say this would hold true for other SQL databases.
The answer is no. SQL is a language, what you describe is ACID behaviour. Though many database systems behave that way, it is still perfectly possible to create one that uses SQL as language and allows statements to be partially executed.

What happens to connections when taking SQl Server Database Offline?

I have recently tried a big merge of 2 databases. We recreated the schema from Database 2 into Database 1 and created a script to transfer all data from database 2 into Database 1. This script takes about 35 min to run and have transaction handling with:
BEGIN TRANSACTION
...
IF(##error<>0)
COMMIT TRANSACTION
ELSE
ROLLBACK TRANSACTION
The full script is a bit sensitive but here is some SQL that have the same structure: http://pastebin.com/GWJ3ZnkF
We ran the script and all data was transfered without errors. We tested the systems running with the new combined database (removed access rights to the old database).
But as a last task we wanted to take the old database offline to make sure no one used that database. To do this we used:
ALTER DATABASE <dbname> SET OFFLINE WITH ROLLBACK IMMEDIATE
This was bad. After this line of SQL code all data in the combined database that we just copied was suddenly gone. I first asumed it wasn't really finished so the "Rollback immediate" sounds like it have performed a rollback on my transaction..
But why? Wasn't the transaction allready committed?
Also I tried running the same script again a few times but after every attempt no data was copied even if it said the script was successfull. I have no idea why... did it remember my offline rollback somehow?
What is really happening to my connections?
Sounds like you had a pending transaction uncommitted and you forced it to rollback, loosing some of the work. The rest is explained by how your scripts are structured. Is unlikely your script had a single transaction from start to bottom. Only the last transaction was rolled back, so the database was left now in a state in which it is 'half copied'. Probably your script does various checks and this intermediate state sends the script on the 'ELSE' branches where it does not do the proper work (ie. apparently does nothing).
W/o posting the exact script, is all speculation anyway.
Right now you need to restore the database to a consistent state, the one before your data copy. Use the backup you took before the data move (you did take a backup, right?). for extra credit, make sure your script is idempotent and works correctly on a half-updated database.
I'd double-check to make sure that there are no outstanding transactions. Either go through the file and count the number of BEGIN TRANSACTION vs COMMIT TRANSACTION lines, or add a statement to the end of it to SELECT ##TRANCOUNT to ensure that there are no open transactions remaining.
If your data has been committed, there should be no way it can be lost by disconnecting you.
WITH ROLLBACK IMMEDIATE:
All incomplete transactions will be rolled back and any other
connections to the database will be
immediately disconnected.
Sounds like someone got the 2 databases mixed up or maybe there is an outstanding transaction?.... Can you post your entire script?
Ref: ALTER DATABASE.
Rather than only checking ##ERROR, inspect ##TRANCOUNT as well.

Resources