Can i return SQL procedure before committing the transaction? - sql

Can i use return statement in a sql transaction procedure?
Sql Procedure
ALTER PROCEDURE [dbo].[uspProcessStudentRecord]
AS
Begin Transaction
insert into dbo.Student(name,address) values('ABC','INDIA');
return;
Commit Transaction
Is it a good practice of writing return inside a Transaction?

Is it a good practice of writing return inside a Transaction?
No. In fact you will get this SQL Server error and the transaction will remain uncommitted:
Transaction count after EXECUTE indicates a mismatching number of
BEGIN and COMMIT statements. Previous count = 0, current count = 1.
When a transaction is started in a stored procedure, the best practice is to COMMIT or ROLLBACK prior to returning. Also, it's a good practice to specify SET XACT_ABORT ON in procs with explicit transactions to avoid inadvertently leaving a transaction open after a timeout.

rollback transaction will undo it.
returning it will do nothing if you didn't commit it first.
But i do not see any point of doing what you are doing now in your statement unless you are going to use a try catch or some other statement.

If you really do want to be able to commit even if the transaction is rolled back (and the only viable reason I can think of is for logging purposes) there are a couple of options open to you:
Use the Service Broker with event notifications link
Use a linked server pointing back to same DB instance without distributed transaction promotion - this will commit in a completely isolated transaction.
But please first reconsider if this is something you really need to do!

Related

SQL Server How to always rollback the entire transaction

I have a somewhat unusual need for my transaction handling.
I am working on a mutation test framework for SQL Server, for this I need to run my tests inside a transaction, so that the database always is in the state it started when the tests finish.
However I have the problem that users can code inside the test procedures and may call rollback transaction that may or may not be inside a (nested) transaction (savepoint).
high level it looks like this
start transaction
initialize test
run test with user code
may or may not contain:
- start tran
- start tran savename
- commit tran
- commit tran savename
- rollback tran
- rollback tran savename
output testresults
rollback transaction
Is there a way to make sure I can at last always roll back to the initial state? I have to take in account that users can call stored procedures/triggers that maybe nested and can all contain transaction statements. With all my solutions the moment a user uses rollback tran in their test code they escape the transaction and not everything will be cleaned
What I want is that if a user calls rollback only their part of the transaction is rolled back and my transaction that I start before the initialization of the test is still intact.
If it is possible I want to prevent to force my users to use a transaction template that uses savepoints when a transaction already exists.
I do not think this is possible without any communication/rules for the user code. Despite what you do, if the user's code runs as many COMMITs as there are ##TRANCOUNT at that time, the transaction will be commited and there will be nothing you can do about that.
One way you could do this is if you check/enforce the user code to, instead of using COMMIT, change that to if ##TRANCOUNT>=2 COMMIT. This will make sure the TRUE data commit can only be done by YOUR COMMIT command. Of course, it happens you never really want to commit, so you just rollback and it's over.
You mention:
What I want is that if a user calls rollback only their part of the
transaction is rolled back
Please note that nested transactions are kind of a myth. Refer to this excellent article. Long story short: "Nested" BEGIN TRANSACTIONs and COMMITs do actually nothing except change the value of the system var ##TRANCOUNT so that some organisation can be made through procedures.
I don't think it is possible to rollback a part of transaction and keep the other transaction intact. The moment we rollback the transaction, the entire transaction gets rolled back.
As pointed out by serveral others nested transactions are not supported in SQL Server. I have decided to minimize the number of state changing statements after the users code and that I clean those statements at the start of a test batch. This way it doesn't really matter that I can't rollback my own transaction, since the heavy lifting will be rolled back either by me or the user.
I also decided to fail the test when the starting ##TRANCOUNT and the ending ##TRANCOUNT dont match up so that no test can pass when there is something wrong with the users transactions.
The current system however will still struggle with a user doing a COMMIT TRAN. That is a problem for another time.
I believe the actual need is to write the test cases for T-SQL. If that is correct then I feel you don't have to reinvent the wheel and start using the open source test framework T-SQLT https://tsqlt.org/

Are T-SQL rollbacks deterministic or garbage collected?

In our Miscrosoft Sql Server 2008 database, I found some stored procedures that do this:
BEGIN TRY
BEGIN TRANSACTION
query1
query2
query3
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
RAISERROR
END CATCH
I said to my coworker that this is functionally identical to this:
BEGIN TRANSACTION
query1
query2
query3
COMMIT TRANSACTION
If, say, query2 fails, you never hit the COMMIT line, so of course SqlServer rolls back, and because of the error, it throws it back to the client as well. Since the CATCH block does the same thing as the default, I argued that we don't need the TRY/CATCH block at all.
My co-worker agrees that the ROLLBACK will happen eventually, but it could be some time later, and could hold resources or lock records for some non-deterministic amount of time, and this could cause problems.
So my question is: if a stored procedure fails in a transaction, when does that transaction get rolled back?
The rollback won't be triggered with your solution in the expected way.
You have to add
set xact_abort on
to your query.
For further information see an old answer and the Microsoft documentation
SQL Server will happily leave the transaction open forever as long as the client connection remains open. An explicit rollback is a good practice in that it doesn't wait for the client connection to be physically closed or the pooled connection reused. A simple THROW (SQL 2012 and later) in the CATCH block will rollback the transaction and raise the error.
I recommend the SET XACT_ABORT ON option hash suggested to mitigate a another issue. If a client timeout occurs during query execution, no further code in the batch (including the ROLLBACK) will execute and the transaction will be left open. However, the transaction will still get rolled back with SET XACT_ABORT ON.

How to rollback an implicit SSMS transaction (statement with go at the end)?

Question:
Normally, you can undo a sql command with rollback.
BEGIN TRY
BEGIN TRANSACTION
/* run all your SQL statements */
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
My question now:
If 'one' did this
UPDATE TABLE_X SET FIELD_X = 'bla'
GO
in SSMS (notice the go at the end) and forgot to specify the WHERE clause,
is it possible (and how) to rollback the implicit transaction that SSMS executed this command in (statements with go at the end are implicitly executed in a transaction) ?
Note:
I didn't do that, but a colleague of mine did a few days ago (without go).
I undid the damage he did (fortunately I made a backup 0.5 hours before he did that), but for the future, it would be good to know this, because this happened to me once, too.
No, you can't, not easily. Restoring from backup is the best option.
see the link below, I think it will help you
How to recover the old data from table
thanks
Arun
GO does not specify the end of an implicit transaction, but the end of a batch. That's why you won't be able (unfortunately) to ROLLBACK your UPDATE after a GO.
From the MSDN page on GO:
GO is not a Transact-SQL statement; it is a command recognized by the
sqlcmd and osql utilities and SQL Server Management Studio Code
editor.
SQL Server utilities interpret GO as a signal that they should send
the current batch of Transact-SQL statements to an instance of SQL
Server. The current batch of statements is composed of all statements
entered since the last GO, or since the start of the ad hoc session or
script if this is the first GO.
The UPDATE command will only be seen as the start of an implicit transaction if you have specified SET IMPLICIT_TRANSACTIONS ON; (see here). In that case, a number of commands (CREATE, DELETE, UPDATE etcetera) will automatically start a new implicit transaction, and that transaction will not end until you issue a ROLLBACK or a COMMIT.
(See for more info on the difference between transactions and batches in SQL Server for example this question on ServerFault: SQL Server: Statements vs. Batches vs. Transactions vs. Connections.)

when commit will affect actually tables while procedure call?

I am working with ms sql with struts framework.
While calling procedure I put autocommit false in program.
when the procedure run I have to commit one seperate transaction and it must be affect the table externally
But it never be save until conn.commit() statement execute in program.
Is it any other way to commit the transaction in procedure itself, to affect the table on the end of the single transaction in procedure?
Pl. tell me if you know.
T.Saravanan
You should start and commit/rollback a transaction at the same level, otherwise you are introducing a lot of unpredictable paths - and frankly some bad design. So: if you need to commit at the server, use BEGIN TRAN / COMMIT TRAN in the TSQL to handle the transaction locally.
Note, though, that TSQL exception/error handling is not as rich as handling errors at a caller such as java/C#. If the problem is that you want to disassociate this work from another unrelated transaction, then it depends on how your calling code works:
if it is using connection-level transactions, then you will need to use a separate connection; just run the transaction on a different connection using the java/C#/whatever transaction API (i.e. the same as your existing code, by the sound of it, but on a different connection)
if it is using things like scope-based transactions (TransactionScope in C#; not sure about java etc - but this is an LTM or DTC transaction) then you can explicitly create a new scope that is bound to either a new (isolated) transaction, or the nil-transaction (i.e. the inner scope is not enlisted)
As for affecting the tables... SQL Server generally does optimistic changes, i.e. yes the changes are applied immediately (so that commit is cheap, and rollback is more expensive) - however, the isolation level will generally prevent other SPIDs from seeing the data. A competing SPID with a low isolation level (or using the NOLOCK hint) will see the uncommitted data, but this may be a phantom/non-repeatable read if the data eventually gets rolled back.

Should I commit or rollback a read transaction?

I have a read query that I execute within a transaction so that I can specify the isolation level. Once the query is complete, what should I do?
Commit the transaction
Rollback the transaction
Do nothing (which will cause the transaction to be rolled back at the end of the using block)
What are the implications of doing each?
using (IDbConnection connection = ConnectionFactory.CreateConnection())
{
using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
{
using (IDbCommand command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandText = "SELECT * FROM SomeTable";
using (IDataReader reader = command.ExecuteReader())
{
// Read the results
}
}
// To commit, or not to commit?
}
}
EDIT: The question is not if a transaction should be used or if there are other ways to set the transaction level. The question is if it makes any difference that a transaction that does not modify anything is committed or rolled back. Is there a performance difference? Does it affect other connections? Any other differences?
You commit. Period. There's no other sensible alternative. If you started a transaction, you should close it. Committing releases any locks you may have had, and is equally sensible with ReadUncommitted or Serializable isolation levels. Relying on implicit rollback - while perhaps technically equivalent - is just poor form.
If that hasn't convinced you, just imagine the next guy who inserts an update statement in the middle of your code, and has to track down the implicit rollback that occurs and removes his data.
If you haven't changed anything, then you can use either a COMMIT or a ROLLBACK. Either one will release any read locks you have acquired and since you haven't made any other changes, they will be equivalent.
If you begin a transaction, then best practice is always to commit it. If an exception is thrown inside your use(transaction) block the transaction will be automatically rolled-back.
Consider nested transactions.
Most RDBMSes do not support nested transactions, or try to emulate them in a very limited way.
For example, in MS SQL Server, a rollback in an inner transaction (which is not a real transaction, MS SQL Server just counts transaction levels!) will rollback the everything which has happened in the outmost transaction (which is the real transaction).
Some database wrappers might consider a rollback in an inner transaction as an sign that an error has occured and rollback everything in the outmost transaction, regardless whether the outmost transaction commited or rolled back.
So a COMMIT is the safe way, when you cannot rule out that your component is used by some software module.
Please note that this is a general answer to the question. The code example cleverly works around the issue with an outer transaction by opening a new database connection.
Regarding performance: depending on the isolation level, SELECTs may require a varying degree of LOCKs and temporary data (snapshots). This is cleaned up when the transaction is closed. It does not matter whether this is done via COMMIT or ROLLBACK. There might be a insignificant difference in CPU time spent - a COMMIT is probably faster to parse than a ROLLBACK (two characters less) and other minor differences. Obviously, this is only true for read-only operations!
Totally not asked for: another programmer who might get to read the code might assume that a ROLLBACK implies an error condition.
IMHO it can make sense to wrap read only queries in transactions as (especially in Java) you can tell the transaction to be "read-only" which in turn the JDBC driver can consider optimizing the query (but does not have to, so nobody will prevent you from issuing an INSERT nevertheless). E.g. the Oracle driver will completely avoid table locks on queries in a transaction marked read-only, which gains a lot of performance on heavily read-driven applications.
ROLLBACK is mostly used in case of an error or exceptional circumstances, and COMMIT in the case of successful completion.
We should close transactions with COMMIT (for success) and ROLLBACK (for failure), even in the case of read-only transactions where it doesn't seem to matter. In fact it does matter, for consistency and future-proofing.
A read-only transaction can logically "fail" in many ways, for example:
a query does not return exactly one row as expected
a stored procedure raises an exception
data fetched is found to be inconsistent
user aborts the transaction because it's taking too long
deadlock or timeout
If COMMIT and ROLLBACK are used properly for a read-only transaction, it will continue to work as expected if DB write code is added at some point, e.g. for caching, auditing or statistics.
Implicit ROLLBACK should only be used for "fatal error" situations, when the application crashes or exits with an unrecoverable error, network failure, power failure, etc.
Just a side note, but you can also write that code like this:
using (IDbConnection connection = ConnectionFactory.CreateConnection())
using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
using (IDbCommand command = connection.CreateCommand())
{
command.Transaction = transaction;
command.CommandText = "SELECT * FROM SomeTable";
using (IDataReader reader = command.ExecuteReader())
{
// Do something useful
}
// To commit, or not to commit?
}
And if you re-structure things just a little bit you might be able to move the using block for the IDataReader up to the top as well.
If you put the SQL into a stored procedure and add this above the query:
set transaction isolation level read uncommitted
then you don't have to jump through any hoops in the C# code. Setting the transaction isolation level in a stored procedure does not cause the setting to apply to all future uses of that connection (which is something you have to worry about with other settings since the connections are pooled). At the end of the stored procedure it just goes back to whatever the connection was initialized with.
Given that a READ does not change state, I would do nothing. Performing a commit will do nothing, except waste a cycle to send the request to the database. You haven't performed an operation that has changed state. Likewise for the rollback.
You should however, be sure to clean up your objects and close your connections to the database. Not closing your connections can lead to issues if this code gets called repeatedly.
If you set AutoCommit false, then YES.
In an experiment with JDBC(Postgresql driver), I found that if select query breaks(because of timeout), then you can not initiate new select query unless you rollback.
Do you need to block others from reading the same data? Why use a transaction?
#Joel - My question would be better phrased as "Why use a transaction on a read query?"
#Stefan - If you are going to use AdHoc SQL and not a stored proc, then just add the WITH (NOLOCK) after the tables in the query. This way you dont incur the overhead (albeit minimal) in the application and the database for a transaction.
SELECT * FROM SomeTable WITH (NOLOCK)
EDIT # Comment 3: Since you had "sqlserver" in the question tags, I had assumed MSSQLServer was the target product. Now that that point has been clarified, I have edited the tags to remove the specific product reference.
I am still not sure of why you want to make a transaction on a read op in the first place.
In your code sample, where you have
// Do something useful
Are you executing a SQL Statement that changes data ?
If not, there's no such thing as a "Read" Transaction... Only changes from an Insert, Update and Delete Statements (statements that can change data) are in a Transaction... What you are talking about is the locks that SQL Server puts on the data you are reading, because of OTHER transactions that affect that data. The level of these locks is dependant on the SQL Server Isolation Level.
But you cannot Commit, or ROll Back anything, if your SQL statement has not changed anything.
If you are changing data, then you can change the isolation level without explicitly starting a transation... Every individual SQL Statement is implicitly in a transaction. explicitly starting a Transaction is only necessary to ensure that 2 or more statements are within the same transaction.
If all you want to do is set the transaction isolation level, then just set a command's CommandText to "Set Transaction Isolation level Repeatable Read" (or whatever level you want), set the CommandType to CommandType.Text, and execute the command. (you can use Command.ExecuteNonQuery() )
NOTE: If you are doing MULTIPLE read statements, and want them all to "see" the same state of the database as the first one, then you need to set the isolation Level top Repeatable Read or Serializable...