hi I have a requirement,
BEGIN TRANSACTION NAME <name_identifier of the transaction>;
<insert statement> on table ABC
<delete statement> on table ABC
COMMIT;
Now I want both the sql should be done or none.
Either statement should not be executed.
HOw should I achieve this. Since I tried.
create or replace table test_table(user_id integer,ip_address text,user_agent text,email text);
create or replace table test_table_copy(user_id integer,ip_address text,user_agent text,email text);
begin transaction name test_transaction;
insert into test_table_copy values(100,'1.1.1.1','ua_1','abc1#gmail.com');
insert into test_table values(100,'1.1.1.1','ua_1','abc1#gmail.com');
-- will fail as table name does not exist
delete from test_tablee where user_id = 100;
commit;
I am seeing the behaviour as insert happening in spite of the fact delete statement is failing.
How do I achieve here all done or none done thing.
Thanks
To abort transaction you could set: TRANSACTION_ABORT_ON_ERROR to TRUE.
TRUE: The non-autocommit transaction is aborted. All statements issued inside that transaction will fail until a commit or rollback statement is executed to close that transaction.
ALTER SESSION SET TRANSACTION_ABORT_ON_ERROR = TRUE;
Related
I have few SQL statements.
copy into STG_PB(VAR,FILE_NAME, LINE_NUMBER)
from (
select $1,metadata$filename, metadata$file_row_number
from #investor_stage_s3//
)
delete from stg_pb1
insert into stg_pb1 values (....)
So daily I get loads of data from s3 to be loaded into the database.
In my SQL statement, I am using a delete statement, therefore if I get any error in the insert statement, all my previous data will be lost. How DO I make sure, if an error occurs roll back to previous data?
Thanks,
Xi
Using transactions:
A transaction is a sequence of SQL statements that are processed as an atomic unit. All statements in the transaction are either applied (i.e. committed) or undone (i.e. rolled back) together.
and
Allowing Statement Errors to Abort Transactions:
To allow a statement error within a transaction to abort the transaction, set the TRANSACTION_ABORT_ON_ERROR parameter at the session or account level.
ALTER SESSION SET TRANSACTION_ABORT_ON_ERROR = TRUE;
BEGIN TRANSACTION;
-- DML 1
-- DML 2
COMMIT;
Wrapping everything with Snowflake Scripting block:
CREATE OR REPLACE TABLE t1(i INT);
EXECUTE IMMEDIATE $$
BEGIN
BEGIN TRANSACTION;
INSERT INTO t1 SELECT 1;
INSERT INTO t1 SELECT 1/0;
COMMIT;
RETURN 'Success';
EXCEPTION
WHEN OTHER THEN
ROLLBACK;
RETURN 'Error';
END;
$$;
SHOW TRANSACTIONS;
SELECT * FROM t1;
I am using MS SQL Server 2016 where I have implemented a instead of delete trigger. It looks like this:
ALTER TRIGGER MyTrigger ON MyTable INSTEAD OF DELETE AS
BEGIN
IF --some condition
BEGIN
RAISERROR ('Error msg', 16, 1)
ROLLBACK TRAN
RETURN
END
DELETE MyTable FROM MyTable JOIN deleted ON MyTable.id = deleted.id
END
If I execute a DELETE statement on the table 'MyTable' and the condition in the if is not fulfilled the DELETE statement is executed after the if-block. This is absolutely correct. But in the console of SSMS it is written twice that the DELETE statement was executed. So the following is written in the console:
(1 rows affected)
(1 rows affected)
I do not understand why. Why does SSMS indicate twice that a row is affected? I use SSMS version 15.0.18338.0.
This is because there were 2 sets of data effect, the set outside the TRIGGER, and then again inside it, because the initial dataset doesn't perform the DML operation itself. If you don't want to see the latter count, turn NOCOUNT to ON. This, of course, means that if fewer rows are effected in your TRIGGER, you won't know about it in the output from SSMS (but it's just informational anyway).
It is also heavily advised that you don't use ROLLBACK inside a TRIGGER, handle transactions outside the TRIGGER, not inside. RAISERROR isn't recommend either and you should be using THROW for new development work (that's been recommended since 2012!). This results in a TRIGGER like below:
CREATE OR ALTER TRIGGER MyTrigger ON dbo.MyTable INSTEAD OF DELETE AS
BEGIN
SET NOCOUNT ON;
IF EXISTS (SELECT 1 FROM deleted WHERE SomeVal = 'Nonsense')
THROW 95302, N'Error Msg', 16; --Use an error number appropriate for you
ELSE
DELETE MT FROM dbo.MyTable MT JOIN deleted ON MT.id = deleted.id;
END;
GO
I have a chunk of SQL code that has the following format:
SET IMPLICIT_TRANSACTIONS ON
// Insert or Update Statement #1
GO
// Insert or Update Statement #2
GO
IF ##TRANCOUNT > 0 COMMIT TRAN
SET IMPLICIT_TRANSACTIONS OFF
My question: is statement 1 in the same transaction as statement 2 (but that they are in different batches)? I'd believe so based on my reading on Google but I'd like some second opinions.
Thanks!
It depends.
If the both statements are either one of the following :
ALTER TABLE
FETCH
REVOKE
BEGIN TRANSACTION
GRANT
SELECT
CREATE
INSERT
TRUNCATE TABLE
DELETE
OPEN
UPDATE
DROP
then the answer is yes.
Because if the connection is already in an open transaction, the above statements do not start a new transaction.
If, however, Statement 2 is BEGIN TRANSACTION then it will cause two nested transactions to open.
http://msdn.microsoft.com/en-us/library/ms187807(v=sql.100).aspx
And the GO command is just a batch separator , it doesn't start a new transaction.
A transaction can be wrapped around multiple batches.
I am doing this in SQL Server 2005.
I have a table, there is only one column of type int, o you cannot insert char in it.
If I run this, I will have one row inserted into my table.
INSERT INTO TestTable VALUES(3) --success
INSERT INTO TestTable VALUES('b') --fail, cannot insert char
If I run this, I will have ZERO row inserted into my table. So a transactions group 2 tasks into a single execution unit. If one task fail, the whole transaction fail.
BEGIN TRANSACTION
INSERT INTO TestTable VALUES(3)
INSERT INTO TestTable VALUES('b')
COMMIT TRANSACTION
My question is: It seem like ROLLBACK TRANSACTION is useless because I don't have ROLLBACK in the above code but it is still rolled back.... Could anybody help me to understand this?
In SQL Server, all code runs in an implicit transaction by default. If you're submitting both statements in one batch and you have the query option XACT_ABORT on, any failure should roll back the entire transaction. The default behavior is to only rollback the statement that caused the error. If you submit the statements in their own batches (either by highlighting them individually in SSMS and hitting F5 or putting "go" statements between), I think you'll see different behavior.
This:
use test;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRANSACTION;
EXEC sp_RENAME 'table1.asd' , 'ads', 'COLUMN';
INSERT INTO table1 (ads) VALUES (12);
COMMIT
is a simple example that demonstrates what I would like to do.
I want to alter the table in some way and perform inserts/deletes in one transaction (or other modifications to the table).
The problem is that the results from sp_RENAME are never immediately visible to the INSERT statement. I've played with different transaction isolation levels - it's always the same (therefore the transaction never commits).
Normally I would just use GO statements for this to be in separate batches, but I need that in one batch, because...
My real task is to write a script that adds identity and FK to a table (this requires creating another table with the new schema, performing identity inserts from the old one, renaming the table and applying constraints). I need to play it safe - if any part of the procedure fails I have to rollback the whole transaction. This is why I wanted to do something like this:
BEGIN TRAN
--some statement
IF (##ERROR <> 0) GOTO ERR_HANDLER
-- some other statement
IF (##ERROR <> 0) GOTO ERR_HANDLER
COMMIT TRAN
RETURN 0
ERR_HANDLER:
PRINT 'Unexpected error occurred!'
ROLLBACK TRAN
RETURN 1
Since labels work only inside a batch I cannot use GO statements.
So how can I:
make statements(ie. ALTER TABLE, sp_RENAME) have an immediate effect ?
or
write the whole solution some other way so that it is safe to run in production DB ?
The issue is that the parsing of the batch fails when it encounters the reference to the renamed column so the entire batch never gets executed - not that the effects of the transaction are not visible.
You can put your statements referencing the new name of the column in an EXEC('') block to defer compilation until after the column is renamed.
EXEC sp_rename 'table1.asd' , 'ads', 'COLUMN';
EXEC('INSERT INTO table1 (ads) VALUES (12);')