i need to update a database where some of the table have changed (columns have been added). I want to perform this action in a proper transaction. If the code executes without any problem then i will commit the changes otherwise i will rollback the database back to its original state.
I want to do something like this:
BEGIN TRANSACTION
...Execute some sql statements here
COMMIT TRANSACTION (When every thing goes well)
ROLLBACK TRANSACTION (When something goes wrong)
Please tell me what is the best way to do this i know there is a ##TranCount variable but dont know its exact purpose.
Thanks.
Begin Transaction
Alter Table dbo.MyTable
Add Col1 varchar(50)
If ##Error = 0
Begin
Commit Transaction
End
Else
Begin
Rollback Transaction
End
##Error gets reset after each SQL Statement so you must check it immediately after each statement has been executed to check for any errors.
Related
I have a simple question. When I start a transaction with BEGIN TRAN and in the middle I write some statements and at the end is write COMMIT TRAN, whats happens if one statement fails? Will the transaction still be committed and I need to check after every statement if ##ERROR <> 0? And what happens if I write at the end ROLLBACK TRAN? Will the transaction rollbacked also when all statements worked properly? I am a bit confused :/
If you use COMMIT TRAN your query will be executed only if there are no errors while ROLLBACK TRAN is used to check if the query is working properly or not, it will definitely rollback even if there is no issue with the query.
i have a loop while in sql which do something as it
begin tran one
do some inserts in others tables
--start loop
begin tran two
--do something
begin try
--if something fail then a trigger does rollback and this return a error (and this goes to catch), then don't i need do the rollbak in catch? this could not be dissable because this is working on production
--something finished ok
commit tran two
end try
begin catch
rollback tran two
end catch
--finished loop
commit
----------
i got this error
Uncommittable transaction is detected at the end of the batch. The
transaction is rolled back.
begin tran one
begin tran two
rollback tran two
doing this code i get this:
Cannot roll back two. No transaction or savepoint of that name was found.
I only want the subquery to rollback the second loop and continue with others records.
Operator rollback rolls back all transaction, for roll back only second loop you you must use savepoints:
begin tran one
-- do some inserts in others tables
--start loop
save tran two -- begin tran two
--do something
begin try
update product set id = 1 --if something fail then a trigger does rollback and this return a error (and this goes to catch), then don't i need do the rollbak in catch? this could not be dissable because this is working on production
--something finished ok
commit tran two
end try
begin catch
rollback tran two
end catch
--finished loop
commit
trigger example:
create table product (id int)
GO
create trigger product_trigger on product for update
as
set xact_abort off
if (select count(*) from inserted i join product p on i.id=p.id)=0 begin
if (##trancount>0) begin
/* rollback */
raiserror('product does not exist', 16, 1)
end
end
In my case, was my code was calling, thru an EF DbContext method, a SQL Server stored procedure, which contained a non-nested transaction.
Since, as #NotMe has already pointed-out, that "there is no such as a nested transaction in SQL Server", I began wondering whether my process was really transaction-nestingless.
Suspecting, that my DbContext had some guilt, I started checking on DbContext options, until DbContext.Configuration.EnsureTransactionsForFunctionsAndCommands = True caught my attention.
So, as soon as I changed it value to True, everything worked successfully.
MyDbContext.Configuration.EnsureTransactionsForFunctionsAndCommands = false;
What happened?
Well, in my opinion, EF's ObjectContext.ExecuteFunction method was managing its own outer transaction as a wrapper to my stored procedure's inner transaction, so, when my stored procedure's ROLLBACK TRAN was hit, there was no pending transaction when EF's COMMIT/ROLLBACK code was hit.
Oddly enough, while gathering some references on EnsureTransactionsForFunctionsAndCommands property, I found that this default behaviour is due to one of the worst (in my opinion) EF team's decision ever, since it collides diretly with every ROLLBACK TRAN inside a T-SQL script.
For further details on EF, check insightfull SO's QA at EF6 wraps every single stored procedure call in its own transaction. How to prevent this?
Basically, everyone should check ##trancount > 0 before issuing a ROLLBACK command, whether named or not, specially inside stored procedure.
CREATE PROCEDURE Proc1 AS
BEGIN
BEGIN TRAN
EXEC Proc2
IF(##trancount > 0)
COMMIT TRAN
END
CREATE PROCEDURE Proc2 AS
BEGIN
BEGIN TRAN
ROLLBACK TRAN
END
For better awareness about Microsoft SQL Server's nested transactions, I would suggest reading the following article
Be careful using ROLLBACK on nested transaction in SQL Server!
Hope it helps someone :-)
Is there a way we can rollback to previous state of the transaction using ROLLBACK without BEGIN TRANSACTION?
delete from table1;
ROLLBACK
Message:
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
Any input would be of great help.
Thanks !!!
To expand on gerrytans answer when you explicitly set IMPLICIT_TRANSACTIONS ON, you can use a ROLLBACK. See the MSDN doco related to this. Note that this isn't the default autocommit transaction mode.
This allows me to run a statement like;
SET IMPLICIT_TRANSACTIONS ON
INSERT INTO my_table (item_type, start_date_time)
VALUES ('TEST', CURRENT_TIMESTAMP)
ROLLBACK
-- Shouldn't return the 'TEST' value inserted above.
SELECT * FROM my_table ORDER BY start_date_time DESC
As SQL server error tells you -- no you can't. And many people would be curious why would you want that in the first place.
Keep in mind SQL server has an implicit transaction -- that is for DML you issue without explicit BEGIN TRAN, SQL server will start and finish a transaction for you behind the screen.
A common usage of ROLLBACK is for error handling. If somewhere in the middle of the transaction you realize you cannot proceed further due to bad user input or other reason -- then a reasonable action is to ROLLBACK to return to the starting point
The worst thing that can happen is leave your data state 'somewhere in the middle'.
You must have a BEGIN TRANSACTION before you can use the ROLLBACK command. You can't go back to the previous state.
I need to provide an auto update feature to my application.
I am having problem in applying the SQL updates. I have the updated SQL statement in my .sql file and what i want to achieve is that if one statment fails then entire script file must be rolled back
Ex.
create procedure [dbo].[test1]
#P1 varchar(200),
#C1 int
as
begin
Select 1
end
GO
Insert into test (name) values ('vv')
Go
alter procedure [dbo].[test2]
#P1 varchar(200),
#C1 int
as
begin
Select 1
end
GO
Now in the above example, if i get the error in third statement of "alter procedure [dbo].[test2]" then i want to rollback the first two changes also which is creating SP of "test1" and inserting data into "test" table
How should i approach this task? Any help will be much appreciated.
If you need any more info then let me know
Normally, you would want to add a BEGIN TRAN at the beginning, remove the GO statements, and then handle the ROLLBACK TRAN/COMMIT TRAN with a TRY..CATCH block.
When dealing with DML though there are often statements that have to be at the start of a batch, so you can't wrap them in a TRY..CATCH block. In that case you need to put together a system that knows how to roll itself back.
A simple system would be just to backup the database at the start and restore it if anything fails (assuming that you are the only one accessing the database the whole time). Another method would be to log each batch that runs successfully and to have corresponding rollback scripts which you can run to put everything back should a later batch fail. This obviously requires much more work (writing an undo script for every script PLUS fully testing the rollbacks) and can also be a problem if people are still accessing the database while the upgrade is happening.
EDIT:
Here's an example of a simple TRY..CATCH block with transaction handling:
BEGIN TRY
BEGIN TRANSACTION
-- All of your code here, with `RAISERROR` used for any of your own error conditions
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
However, the TRY..CATCH block cannot span batches (maybe that's what I was thinking of when I said transactions couldn't), so in your case it would probably be something more like:
IF (OBJECT_ID('dbo.Error_Happened') IS NOT NULL)
DROP TABLE dbo.Error_Happened
GO
BEGIN TRANSACTION
<Some line of code>
IF (##ERROR <> 0)
CREATE TABLE dbo.Error_Happened (my_id INT)
IF (OBJECT_ID('dbo.Error_Happened') IS NOT NULL)
BEGIN
<Another line of code>
IF (##ERROR <> 0)
CREATE TABLE dbo.Error_Happened (my_id INT)
END
...
IF (OBJECT_ID('dbo.Error_Happened) IS NOT NULL)
BEGIN
ROLLBACK TRANSACTION
DROP TABLE dbo.Error_Happened
END
ELSE
COMMIT TRANSACTION
Unfortunately, because of the separate batches from the GO statements you can't use GOTO, you can't use the TRY..CATCH, and you can't persist a variable across the batches. This is why I used the very kludgy trick of creating a table to indicate an error.
A better way would be to simply have an error table and look for rows in it. Just keep in mind that your ROLLBACK will remove those rows at the end as well.
In SQL Server, how many transactions will this produce?
DECLARE #deleted BIGINT
SET #deleted = 100000
WHILE #deleted = 100000
BEGIN
DELETE TOP(100000) FROM MYTABLE WITH (ROWLOCK)
where Col1 = 7048 and COL2 = 39727 and Col3 = 0
SET #deleted = (SELECT ##ROWCOUNT)
END
If I cancel after running this for 10 minutes will it need to roll back?
Would adding a being transaction and end transaction fix this if I don't want it to rollback past one iteration after a cancel?
Would it make any difference if I put it in a stored procedure?
When you don't have the BEGIN TRANSACTION and COMMIT, you have implied transactions. And, each DELETE will be a separate transaction. So, if you cancel the script, it will rollback the current command. But, all previous DELETE steps are already committed.
If you add a BEGIN TRANSACTION before your code and a COMMIT after your code, then you get a single transaction. If you cancel the query, you leave an open transaction, where there is not commit or rollback. In this case, you must submit a ROLLBACK command to start the rollback process.
It will be an implicit transaction. remember ACID? everything in SQL Server is a transaction either implicit or explicit otherwise you wouldn't be able to guarantee ACID
I believe this will execute under a single transaction (which SQL Server creates for you in this case). You could run Profiler to validate this. Putting it in a stored proc will not make any difference. I might suggest you put a Begin Tran (and corresponding End Tran) for each pass through the loop. One thing this will help prevent is your transaction log getting too large.