Trigger to block insert into SQL Server - sql

I have a requirement to prevent insert into table using after insert based on certain condition. When I am calling insert statement directly, it is executing perfectly without any problem. Whereas when am using procedure for insert statement with transaction scope, I am getting this error
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
My code:
create table test
(
id int ,
name varchar(10)
)
create table test1
(
id int ,
name varchar(10)
)
ALTER PROCEDURE test_insert
#id INT, #name NVARCHAR(10)
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO test1 (id, name)
VALUES (#id, #name)
INSERT INTO test (id, name)
VALUES (#id, #name)
COMMIT
END TRY
BEGIN CATCH
ROLLBACK;
DECLARE #errormsg NVARCHAR(MAX)
SELECT #errormsg = ERROR_MESSAGE();
THROW 500001, #errormsg, 1;
END CATCH
end
ALTER TRIGGER TRG_test
ON dbo.test
AFTER INSERT AS
BEGIN
DECLARE #idNum INT
SELECT #idNum = id FROM inserted
IF #idNum = 1
BEGIN
RAISERROR('error', 1,1);
ROLLBACK TRANSACTION
RETURN
END
END
Please let me know if am missing anything

Remove the ROLLBACK TRANSACTION inside the Trigger, remember that DML statements within the trigger will use the transaction context of the statement that fired the trigger, in this case, it would be covered by:
Your SP ==>
BEGIN TRANSACTION
--Fired the trigger and is involved by the same Transaction from the SP
insert into test1 (id,name) values(#id,#name)
insert into test (id,name) values(#id,#name)
COMMIT

Remove the ROLLBACK from the trigger and raise and error with severity 11 or higher so that the stored procedure CATCH block is entered. The code below also uses the simplified version of THROW to reraise the trigger error instead of throwing a new one and uses EXISTS to handle multi-row inserts.
alter proc test_insert #id int, #name nvarchar(10)
as
begin
BEGIN TRY
BEGIN TRANSACTION
insert into test1 (id,name) values(#id,#name)
insert into test (id,name) values(#id,#name)
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH
end
GO
alter TRIGGER TRG_test
ON dbo.test
AFTER INSERT AS
BEGIN
if EXISTS(select 1 from inserted WHERE id = 1)
begin
RAISERROR('error', 16,1);
RETURN
end
END
GO

From my understanding,
alter TRIGGER TRG_test
ON dbo.test
instead of insert as --- Trigger type is changed. Trigged before insert
BEGIN
declare #idNum int
select #idNum = id from inserted
if #idNum <> 1 ------ Condition is changed
begin
/* Do what you want.*/
RETURN
end
END
update 1:
create table test(
id int ,
name varchar(10)
)
create table test11(
id int ,
name varchar(10)
)
alter proc test_insert #id int, #name nvarchar(10)
as
begin
BEGIN TRY
BEGIN TRANSACTION
insert into test11(id,name) values(#id,#name)
insert into test (id,name) values(#id,#name)
COMMIT
END TRY
BEGIN CATCH
ROLLBACK;
declare #errormsg nvarchar(max)
select #errormsg=ERROR_MESSAGE();
THROW 500001, #errormsg, 1;
END CATCH
end
alter TRIGGER TRG_test
ON dbo.test
instead of insert as --- Trigger type is changed. Trigged before insert
BEGIN
declare #idNum int, #name int
select #idNum = id, #name=name from inserted
if #idNum <> 1 ------ Condition is changed
begin
insert into test (id,name) values(#idNum,#name)
end
else begin
print 'You insert invalid value 1'
end
END
test_insert 1, 2 -- error will be arised.
select * from test
select * from test11
test_insert 2, 2 -- insertion is occurs.
select * from test
select * from test11

Related

How to make trigger react on specific SP only

create procedure UpdateIte(#iID int,#qtt int)
as begin
insert into updateItem
values(#iID,#qtt,getdate())
end
and
create procedure releseItem(#eid int,#qty int,#itemId int)
AS BEGIN
declare #no int,#qt int
select #no=qty
from Stock
where itemID=#itemID
if(#no>#qty)
BEGIN
update Stock
set qty=qty-#qty
where itemID=#itemID
select #qt=qty
from Stock
where itemID=#itemID
insert into release
values(#eid,#itemId,#qty,getdate())
if(#qt<=10)
BEGIN
print'Item needs to replace'
END
else
print'Success'
END
else
BEGIN
print'Not Enough items in stock'
END
END
i want to create trigger, when release items that update the release table with employee id. when i create trigger it trigger with both procedures. But i want it to trigger with releaseItem. How do i do that?
create trigger CheckQty
ON Stock
for Update
as begin
declare #qty int, #emp int, #q int,#ItemId int
select #qty= qty, #emp=eid, #ItemId=ItemId
from inserted
select #q=qty
from Stock
where ItemId=#ItemId
if(#qty>0)
begin
insert into releseItem
values (#eid,#ItemId,#qty)
end
else
rollback transaction
end
You can just exececute store procedure in trigger as below.
create trigger CheckQty ON Stock for Update as begin declare #qty int, #emp int, #q int,#ItemId int
select #qty= qty, #emp=eid, #ItemId=ItemId
from inserted
select #q=qty
from Stock
where ItemId=#ItemId
if(#qty>0)
begin
exec releseItem #eid, #ItemId, #qty
end
else
rollback transaction
end

SQL transaction/ErrorHandling/TryandCatch

Taking a SQL programming(2012 version) class and totally stuck, program wont work no matter how many times I try. The requirements (Questions) as well as what I have so far are below. Below the dashed line is another proc I wrote for error handling. Please help me finish this...please!
/*Create a Stored Procedure that accepts StockName, NewOpenPrice, NewClosePrice.
a. If the Stock Name does not EXIST a new record should be added into the dbo.Stocks table
b. If the Stock Name does EXIST, the OpenPrice an ClosePrice will be updated with the newly inserted Prices.
c. Insert and Update statements should be built using a transaction (Repeatable Read Isolation Level)
d. A Try Catch Statement should be used for the Update and Insert statements. If there is an error, the dbo.error_handler Stored Procedure should be called.
*/
CREATE PROCEDURE spc_Stocks
#Name varchar(25), #NewOpenPrice money, #NewClosePrice money
as
BEGIN
CREATE TABLE dbo.Stocks (
StockID int IDENTITY(1,1),
StockName varchar(50),
OpenPrice money,
ClosePrice money )
INSERT INTO dbo.Stocks
SELECT 'Walmart',21.58,22.98 UNION
SELECT 'Target',17.32,15.23 UNION
SELECT 'Taco Bell',4.58,12.98 UNION
SELECT 'Microsoft',7.15,8.15 UNION
SELECT 'Apple',10.79,9.89
Select StockName from stocks
where StockName = #Name
------Name does NOT exist
if (#Name = NULL)
Begin
Insert into dbo.Stocks (StockName)
Values (#Name)
END
----If name DOES exist
ELSE
BEGIN
Begin TRY
Begin SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
UPDATE dbo.Stocks SET OpenPrice =#NewOpenPrice, ClosePrice= #NewClosePrice where StockName = #Name
Commit transaction
END TRY
BEGIN CATCH
?!?!?!?!?!?
*This is my ErrorHandler stored proc query
ALTER PROCEDURE dbo.error_handler
as
BEGIN
DECLARE #errnum INT,
#severity INT,
#errstate INT,
#proc NVARCHAR(126),
#line INT,
#message NVARCHAR(4000)
-- capture the error information that caused the CATCH block to be invoked
SELECT #errnum = ERROR_NUMBER(),
#severity = ERROR_SEVERITY(),
#errstate = ERROR_STATE(),
#proc = ERROR_PROCEDURE(),
#line = ERROR_LINE(),
#message = ERROR_MESSAGE()
end
Create PROCEDURE [dbo].[USP_Stocks]
(#Name varchar(25), #OpenPrice MONEY, #ClosePrice MONEY)
AS
BEGIN
-----Name does NOT exist
IF NOT EXISTS (SELECT StockName FROM [dbo].[Stocks]
WHERE StockName = #Name)
BEGIN
BEGIN TRY
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRAN
INSERT INTO dbo.Stocks (StockName, OpenPrice, ClosePrice)
VALUES (#Name ,#OpenPrice, #ClosePrice)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
EXEC dbo.error_handler
ROLLBACK
END CATCH
END
----If name DOES exist
ELSE
BEGIN
BEGIN TRY
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRAN
UPDATE dbo.Stocks SET OpenPrice =#OpenPrice, ClosePrice= #ClosePrice
where StockName = #Name
Commit transaction
END TRY
BEGIN CATCH
ROLLBACK TRUANSACTION
EXEC dbo.error_handler
END CATCH
END
END

rollback whole txn when any of the insert statement fails in stored procedure

I've a stored procedure where there are two insert statement for two different tables.
create procedure p_multiple_insert
(
// variable declaration
)
declare #temp int = 0;
begin try
begin
// insert statement for TABLE1
set #temp = 1;
if(#temp = 1)
begin
// insert statement for TABLE2
end
end
end try
begin catch
// insert into error table error_number() & error_message()
end catch
If the error occurs with first insert statement block, #temp = 0. So second insert block does not execute and data is also not inserted in the TABLE1.
But if the error occur while inserting into TABLE2, how can I rollback my whole transaction. I mean roll back first insert statment too.
my changes are in UPPER CASE:
create procedure p_multiple_insert
(
-- variable declaration
)
declare #temp int = 0;
BEGIN TRANSACTION
begin try
-- insert statement for TABLE1
set #temp = 1;
if(#temp = 1)
begin
-- insert statement for TABLE2
end
end
COMMIT TRANSACTION
end try
begin catch
ROLLBACK TRANSACTION
-- insert into error table error_number() & error_message()
end catch
BEGIN TRANSACTION at the beginning
COMMIT at the end of try block
ROLLBACK everything in catch-block
My "Standard-Catch-Block" always looks like this:
DECLARE #errmsg NVARCHAR(MAX)
SELECT #errmsg = 'Error executing dbo.StoredProcName: '
+ COALESCE(ERROR_MESSAGE(),'No Message from SQL Server')
RAISERROR(#errmsg,16,1)
ROLLBACK TRANSACTION

How to return an error in a stored procedure and insert into a table

I don't know that much about SQL Server but essentially what I have here is a stored procedure that should insert parameters (id (primary key) and Test data) into a test table, dbo.test
If it fails to add them it should instead add to dbo.Error_Log with the id, test data AND Error information.
My stored procedure looks like this:
#Id int = 0,
#Test_column nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
IF ##ERROR <> 0
BEGIN
INSERT INTO AdventureWorks2012.dbo.Error_Log ([Id], [Data], [Error_description])
SELECT #Id,
#Test_column,
##ERROR
END
ELSE
INSERT INTO AdventureWorks2012.dbo.Test ([Id], [Test_column])
SELECT #Id,
#Test_column
END
And I'm executing it like this:
USE [AdventureWorks2012]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[uspTester]
#Id = 1,
#Test_column = N'data123'
SELECT 'Return Value' = #return_value
GO
SELECT * FROM AdventureWorks2012.dbo.Test
SELECT * FROM AdventureWorks2012.dbo.Error_Log
It returns a populated dbo.Test, an empty dbo.Error_Log and a return value of -4 for some reason
Error is:
Msg 2627, Level 14, State 1, Procedure uspTester, Line 21
Violation of PRIMARY KEY constraint 'PK__Test__3214EC0775996C4D'.
Cannot insert duplicate key in object 'dbo.Test'. The duplicate key
value is (1). The statement has been terminated.
I need to have that error output into dbo.Error_Log and not just stop the operation completely.
The ##ERROR variable "returns the error number for the last Transact-SQL statement executed" i.e. you need to check the ##ERROR after you attempt the insert. Something like this:
BEGIN
SET NOCOUNT ON;
INSERT INTO AdventureWorks2012.dbo.Test ([Id], [Test_column])
SELECT #Id,
#Test_column
IF ##ERROR <> 0
BEGIN
INSERT INTO AdventureWorks2012.dbo.Error_Log ([Id], [Data], [Error_description])
SELECT #Id,
#Test_column,
##ERROR
END
END
Note: If you want the error message instead of the number you are going to have make use of the ERROR_MESSAGE() function: http://msdn.microsoft.com/en-us/library/ms190358.aspx

getting the last inserted id from previous insert statement

I am having a stored procedure with two insert statement, where I want to insert the ID of the first insert statement into the second one.
CREATE PROC [dbo].[Log_Action]
#action_description VARCHAR(MAX),
#creator_id INT,
#entity VARCHAR(50),
#entity_identifier UNIQUEIDENTIFIER
AS
BEGIN
DECLARE #return_value BIT;
BEGIN TRY
INSERT INTO Action_Lookup (action_description)
VALUES (#action_description);
INSERT INTO Audit ([user_id], action_id, CREATED, [guid], entity, entity_identifier)
VALUES (#creator_id, SCOPE_IDENTITY(), GETDATE(), NEWID(), #entity, #entity_identifier);
SET #return_value = 1;
RETURN #return_value;
END TRY
BEGIN CATCH
SET #return_value = 0;
RETURN #return_value;
END CATCH
END
the problem that SCOPE_IDENTITY() returns null, I also tried ##IDENTITY and IDENT_CURRENT but non works.
Try output clause:
CREATE PROC [dbo].[Log_Action]
#action_description VARCHAR(MAX),
#creator_id INT,
#entity varchar(50),
#entity_identifier uniqueidentifier
AS
BEGIN
DECLARE #return_value bit;
BEGIN TRY
INSERT INTO Action_Lookup (action_description)
OUTPUT
#creator_id,
inserted.[id], -- in [] there should be actual name of identity column
GETDATE(),
NEWID(),
#entity,
#entity_identifier
INTO Audit ([user_id], action_id, created, [guid], entity, entity_identifier)
VALUES (#action_description);
set #return_value = 1;
return #return_value;
END TRY
BEGIN CATCH
set #return_value = 0;
return #return_value;
END CATCH
END