SQL Server: how transactions work - sql

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.

Related

Multiple Begin Transactions

I came across this today and accident and was wondering a few things. The basic code setup is
Begin Transaction
Update Table
set column to value
Begin transaction
Update Table
set column to value
I have played with it a little and found you can not do a commit after a doing a rollback, but you can do a commit before a rollback, however the rollback negates the commit. I guess my question is, is there any purpose/use for this? I see not other then making my DBA slap me for bad code lol
The short answer is that the intent behind the design of nested transactions is to allow you to code reusable procedures (or blocks of code) where the 2 following situations can be handled automatically without having to write the code differently for both cases:
You can start and end a transaction if none has been started yet.
Or, if a transaction is already in progress, then you just participate in the on-going transaction.
So let's say you like to code all your reusable procedures in a transactional manner, like this (pseudo code):
create procedure Foo
begin transaction
perform DML 1
perform DML 2
perform DML 3
-- other stuff
commit transaction
end procedure
create procedure Blah
begin transaction
perform DML 1
perform DML 2
perform DML 3
-- other stuff
commit transaction
end procedure
But now, let's say that you now need the Blah procedure to incorporate what Foo does. Obviously, you wouldn't want to copy-paste the contents of Foo in Blah. A simple call to Foo makes more sense for reusability's sake, like this:
create procedure Blah
begin transaction
perform DML 1
perform DML 2
-- include a call to Foo here
Foo();
perform DML 3
-- other stuff
commit transaction
end procedure
In the above case, without any changes to Foo's code, the call to Blah will still behave as one big transaction, which is probably what you want.
It's exactly for cases like these that inner commits don't actually do anything. They really only serve the purpose of flagging that everything was ok up until that point. But the real commit only happens when the outer transaction commits everything.
Imagine if every commit actually committed the transaction, then, to ensure that you don't corrupt the outer transaction, you would have to add extra conditions at the beginning of every procedure to check if a transaction is already started, and only start one if none is found. So, every procedure would have to be coded something like this to ensure it's safe for calling within other procedures:
create procedure Foo
didIStartATransaction = false
if ##trancount = 0 then
begin transaction
didIStartATransaction = true
end if
perform DML 1
perform DML 2
perform DML 3
-- other stuff
if didIStartATransaction then
commit transaction
end if
end procedure
create procedure Blah
didIStartATransaction = false
if ##trancount = 0 then
begin transaction
didIStartATransaction = true
end if
perform DML 1
perform DML 2
perform DML 3
-- other stuff
if didIStartATransaction then
commit transaction
end if
end procedure
That said, nested transactions can still be dangerous if one of the procedures forgets to symmetrically start and commit a transaction.
And personally, I prefer to not have any transaction control statements in any of my procedures, and just have the calling code manage the transaction. I feel a lot safer that way.
Please take a look at SAVEPOINT syntax. This allows you to set points in a transaction you can rollback to.
https://msdn.microsoft.com/en-us/library/ms188378.aspx
Within there lies your answer :)
What you are making is a nested transation.
This is what's happening:
If you rollback an inner transaction then you rollback the outermost transaction too. On the other hand, if you commit an inner transaction your data modification doesn't happen until even then outermost transaction is committed.
More information here.
Obviously using a nested transaction in your code doesn't make sense at all. But think about this code:
BEGIN TRAN
UPDATE MyTable
SET Col1 = 'Val1';
EXEC dbo.SomeStoredProcedureUsingTransactions;
IF ##TRANCOUNT > 0
COMMIT TRAN;

How to return back in SQL statement

I have run SQL server command (update command).
the command has been performed successfully and the table has been updated
is there any way to take back in that command ?
note: no backup taken
If you had originally asked the question how do I do an UPDATE with the possibility of ROLLBACK I would tell you you should do your ad-hoc updates like this.
BEGIN TRANSACTION
UPDATE blah
SET value = newvalue
WHERE condition = someothervalue
--COMMIT TRANSACTION
Then if the results are as expected run the COMMIT TRANSACTION. If they are not than you could do a ROLLBACK TRANSACTION. However since you already did the updates and have no backups or recovery plan you are pretty much out of luck.
After you have already executed an update command the only way back would be via restoring a backup.
Something I do when writing any modification scripts is to wrap the command in a transaction and then either run a rollback or a commit depending on if the query performed as suspected.
Example:
--start the transaction only execute the first three lines, this leaves the transaction open
BEGIN TRANSACTION
UPDATE TABLEA
SET COL1 = "newValue"
--examine data and based on the results run one of these two lines
ROLLBACK TRANSACTION
COMMIT TRANSACTION

Auto update with script file with transaction

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.

Execute Statements in a Transaction - Sql Server 2005

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.

Does a transaction affect all queries?

I started a transaction using BEGIN TRANSACTION in Management Studio but I forgot to ROLLBACK or COMMIT it for about 10 minutes. I freaked out and went back to ROLLBACK my changes. Did this rollback all queries that went through the server during that time or just through my user/connection?
Just your connection :-)
(Edit: rather your transaction, since the BEGIN TRANSACTION. If you did updates before the BEGIN TRANSACTION in the same session, they will of course not be rolled back)
BUT: It could have given SELECTs of other sessions the wrong answer depending on what lock types and query hints that were being used...
An example:
In one SQL Studio session, do the following:
CREATE TABLE a(a INT)
INSERT INTO a VALUES(1)
BEGIN TRANSACTION
UPDATE a
SET a = 2
SELECT *, ##TRANCOUNT
FROM a
-> You will see '2, 1' as result
Open a new session (tab in Sql studio)
Do:
SELECT *, ##TRANCOUNT
FROM a (NOLOCK)
You will see '2, 0'
Now, in first session, do
ROLLBACK TRANSACTION
SELECT *, ##TRANCOUNT
FROM a
-> transaction rolled back, and you see '1, 0'
-> a select in second session will also show '1, 0'
so: If you use (NOLOCK) hint, you can get uncommitted data as result -> which might lead to very unexpected effects :-)
Dont forget:
DROP TABLE a
when you're done ;)
It should only affect your transaction, so only things that were done in your session during that time.
You're fine. All the other queries will go through just fine.
It should roll back all queries made in the transaction, so it is more specific than your user\connection and definitely not all queries on the box.
You need to review the ACID properties of transactions. You see that there is nothing to worry about if a transaction is rolled back or committed it has no effect on the outcome of other transactions.
Your rollback affects only your transaction. The I in ACID.
However, the rows, pages or whole table you locked will affect other users if they want to use them. It depends on:
what they want to do
lock timeout
client command timeout