will this approach work for executing ddl in a transaction? - sql

I'm creating DDL for all of the objects in my db for propogation to the upper environments. I want to ensure an all or nothing transaction including all database object creates/alters and data load inserts. Is it as simple as this or are there any caveats?
BEGIN TRANSACTION
-- include create script generated from ssms
-- include insert data load scripts
COMMIT
I normally just use transactions for multi-table inserts etc.

Try this:
USE [DatabaseName]
GO
BEGIN TRANSACTION Tran1
BEGIN TRY
INSERT ...
COMMIT TRANSACTION Tran1
END TRY
BEGIN CATCH
-- ROLLBACK first.
ROLLBACK TRANSACTION Tran1
END CATCH
GO
Be advised that, if this fails and any of the tables that have identities where data was inserted but subsequently rolled back, then the identity values will need to be RESEEDED. For some reason, the incrementing of Identity values occurs outside of transactions.

Yes, you generally execute both DDL and DML within a transaction to ensure all-or-none. However, some DDL (e.g. CREATE DATABASE) cannot be executed in a transaction and others (e.g. CREATE SCHEMA) must be the only statement in a batch.
Barring those restrictions, you can use both transactions and structured error handling in your T-SQL script:
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
-- include create script generated from ssms
-- include insert data load scripts
COMMIT;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK;
THROW;
END;

Related

Memory Optimized Table and a disk table in another database in one transaction

I have a SQL Transaction like this:
BEGIN Transaction [Trans1]
BEGIN TRY
Insert Into MemoryOptimizedTable
Update [OtherDatabase].[dbo].[DiskTable]
COMMIT TRANSACTION [Trans1]
END TRY
BEGIN CATCH
END CATCH
This Transaction gives this error on the update line:
A user transaction that accesses memory optimized tables or natively
compiled modules cannot access more than one user database or
databases model and msdb, and it cannot write to master.
What alternatives do I have with regards to making sure both the Insert and Update statements succeed or they both fail? I mean is there an alternative to a transaction? or maybe a workaround?

Cannot roll back subtransaction. No transaction or savepoint of that name was found

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 :-)

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.

Confusion with the GO statement, uncommitted transactions and alter procedure

I would like to get to the bottom of this because it's confusing me. Can anyone explain when I should use the GO statement in my scripts?
As I understand it the GO statement is not part of the T-SQL language, instead it is used to send a batch of statements to SQL server for processing.
When I run the following script in Query Analyser it appears to run fine. Then I close the window and it displays a warning:
"There are uncommitted transactions. Do you wish to commit these transactions before closing the window?"
BEGIN TRANSACTION;
GO
ALTER PROCEDURE [dbo].[pvd_sp_job_xxx]
#jobNum varchar(255)
AS
BEGIN
SET NOCOUNT ON;
UPDATE tbl_ho_job SET delete='Y' WHERE job = #job;
END
COMMIT TRANSACTION;
GO
However if I add a GO at the end of the ALTER statement it is OK (as below). How come?
BEGIN TRANSACTION;
GO
ALTER PROCEDURE [dbo].[pvd_sp_xxx]
#jobNum varchar(255)
AS
BEGIN
SET NOCOUNT ON;
UPDATE tbl_ho_job SET delete='Y' WHERE job = #job;
END
GO
COMMIT TRANSACTION;
GO
I thought about removing all of the GO's but then it complains that the alter procedure statement must be the first statement inside a query batch? Is this just a requirement that I must adhere to?
It seems odd because if I BEGIN TRANSACTION and GO....that statement is sent to the server for processing and I begin a transaction.
Next comes the ALTER procedure, a COMMIT TRANSACTION and a GO (thus sending those statements to the server for processing with a commit to complete the transaction started earlier), how come it complains when I close the window still? Surely I have satisfied that the alter procedure statement is the first in the batch. How come it complains about are uncommitted transactions.
Any help will be most appreciated!
In your first script, COMMIT is part of the stored procedure...
The BEGIN and END in the stored proc do not define the scope (start+finish of the stored proc body): the batch does, which is the next GO (or end of script)
So, changing spacing and adding comments
BEGIN TRANSACTION;
GO
--start of batch. This comment is part of the stored proc too
ALTER PROCEDURE [dbo].[pvd_sp_job_xxx]
#jobNum varchar(255)
AS
BEGIN --not needed
SET NOCOUNT ON;
UPDATE tbl_ho_job SET delete='Y' WHERE job = #job;
END --not needed
--still in the stored proc
COMMIT TRANSACTION;
GO--end of batch and stored procedure
To check, run
SELECT OBJECT_DEFINITION(OBJECT_ID('dbo.pvd_sp_job_xxx'))
Although this is a old post, the question is still in my mind after I compiled one of my procedure successfully without any begin transaction,commit transaction or GO. And the procedure can be called and produce the expected result as well.
I am working with SQL Server 2012. Does it make some change
I know this is for an answer. But words are too small to notice in comment section.

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.