SQL Server: error not thrown for multiple updates - sql-server-2008

I have simple procedure to update 2 tables:
create procedure p_updatetrans(#id int)
as
declare #errmsg Nvarchar(200)
begin tran
update table1
set data = 10
where id = #id
if (##error <> 0 or ##rowcount = 0)
begin
select #errmsg = 'Fail to update table1'
goto error
end
update table2
set isUpdate = true
where id = #id
if (##error <> 0 or ##rowcount = 0)
begin
select #errmsg = 'Fail to update table2'
goto error
end
commit tran
return
error:
raiserror(#errmsg, 16 ,1)
rollback tran
Why do I get no error when there's no record in table2 to update? If I use NoCount On, I can't get ##rowcount data.
Thanks

Related

MySQL AFTER INSERT Trigger not inserting into original table

So basically I have a MySQL after insert trigger which should listen for insert operation, after a new row has been inserted in my monetaryTransactions table, the trigger should check whether the date of the new insert is smaller than the last date present in my other deposits table, and if that date is bigger (i.e newer), it should ALSO insert the record in the deposits table
This is the trigger itself ->
DELIMITER //
CREATE TRIGGER lv_deps_trigger
AFTER INSERT
ON MonetaryTransactions FOR EACH ROW
BEGIN
DECLARE ftdInt tinyint(1);
DECLARE agentName varchar(40);
DECLARE businessUnit varchar(40);
DECLARE parsedUnit varchar(40);
DECLARE depDate DATETIME;
DECLARE payment varchar(255);
IF (NEW.FirstTimeDeposit = 'false') THEN
SET ftdInt = 0;
ELSE
SET ftdInt = 1;
END IF;
SELECT FullName FROM users WHERE SystemUserId = NEW.MTTransactionOwner INTO agentName;
SELECT `Bu Name` FROM users WHERE SystemUserId = NEW.MTTransactionOwner INTO businessUnit;
SELECT ApprovedOn FROM deposits ORDER BY ApprovedOn desc LIMIT 1 INTO depDate;
IF (businessUnit LIKE '%dummy%') THEN
SET parsedUnit = 'dummy';
ELSE
SET parsedUnit = 'dummy';
END IF;
CALL processorFetcher(NEW.new_paymentprocessor, #AttrValue);
SELECT #AttrValue INTO payment;
IF (depDate < NEW.Lv_ApprovedOn ) THEN
IF (NEW.Lv_name IN('Deposit','Withdrawal')) THEN
INSERT INTO deposits(TPAccountID,Brand,AgentName,ApprovedOn,Amount,PaymentMethod,TransactionType,Department,FirstTimeDeposit) VALUES(NEW.TPAccountID, NEW.Department, agentName, NEW.Lv_ApprovedOn, NEW.Lv_Amount, payment, NEW.Lv_name, parsedUnit, ftdInt);
ELSE
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Not a wd/dp';
END IF;
ELSE
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Nothing to insert';
END IF;
END; //
DELIMITER ;
Basically, what it does right now is to check for the date, and only if that depDate < NEW.Lv_ApprovedOn condition returns true it will insert the record in both the deposits table and the monetarytransactions one. If the condition returns false, it will just trigger the else statement (nothing to insert) and it won't insert the record in the MonetaryTransactions.
This is indeed the logical behaviour I assume, however, I am unsure on how exactly I need to rework this so that IF the date is newer, it'll insert the record in both tables, if it's older, it will insert it only in the monetaryTransactions table.
I have attempted to just put an INSERT INTO monetarytransactions... in the ELSE condition but as expected it throws an error that I am not allowed to do that.
Any advice is welcome!
in Your case it would be more suitable to use BEFORE INSERT as it would let the INSERT query in the original table to proceed even if the "IF" statement is false, which is exactly Your case, and of course if it is getting in the IF statement it would INSERT into the original table and the trigger would do its work
DELIMITER //
CREATE TRIGGER lv_deps_trigger
BEFORE INSERT
ON lvr_MonetaryTransactions FOR EACH ROW
BEGIN
DECLARE ftdInt tinyint(1);
DECLARE agentName varchar(40);
DECLARE businessUnit varchar(40);
DECLARE parsedUnit varchar(40);
DECLARE depDate DATETIME;
DECLARE payment varchar(255);
IF (NEW.Lv_FirstTimeDeposit = 'false') THEN
SET ftdInt = 0;
ELSE
SET ftdInt = 1;
END IF;
SELECT FullName FROM lvr_users WHERE SystemUserId = NEW.Lv_MTTransactionOwner INTO agentName;
SELECT `Bu Name` FROM lvr_users WHERE SystemUserId = NEW.Lv_MTTransactionOwner INTO businessUnit;
SELECT ApprovedOn FROM dep0sits ORDER BY ApprovedOn desc LIMIT 1 INTO depDate;
IF (businessUnit LIKE '%Conversion%') THEN
SET parsedUnit = 'Conversion';
ELSE
SET parsedUnit = 'Retention';
END IF;
CALL processorFetcher(NEW.new_paymentprocessor, #AttrValue);
SELECT #AttrValue INTO payment;
IF (depDate < NEW.Lv_ApprovedOn ) THEN
IF (NEW.Lv_name IN('Deposit','Withdrawal')) THEN
INSERT INTO dep0sits(TPAccountID,Brand,AgentName,ApprovedOn,Amount,PaymentMethod,TransactionType,Department,FirstTimeDeposit) VALUES(NEW.TPAccountID, NEW.Department, agentName, NEW.Lv_ApprovedOn, NEW.Lv_Amount, payment, NEW.Lv_name, parsedUnit, ftdInt);
ELSE
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Not a wd/dp';
END IF;
ELSE
SET NEW.Lv_monetarytransactionId = NEW.Lv_monetarytransactionId;
SET NEW.CreatedOn = NEW.CreatedOn;
SET NEW.Lv_name = NEW.Lv_name;
SET NEW.lv_type = NEW.lv_type;
SET NEW.lv_accountid = NEW.lv_accountid;
SET NEW.Lv_Amount = NEW.Lv_Amount;
SET NEW.Lv_ApprovedOn = NEW.Lv_ApprovedOn;
SET NEW.Lv_FirstTimeDeposit = NEW.Lv_FirstTimeDeposit;
SET NEW.CurrencyName = NEW.CurrencyName;
SET NEW.Lv_internalcomment = NEW.Lv_internalcomment;
SET NEW.Lv_MTTransactionOwner = NEW.Lv_MTTransactionOwner;
SET NEW.lv_tpaccountid = NEW.lv_tpaccountid;
SET NEW.TPAccountID = NEW.TPAccountID;
SET NEW.Lv_TransactionApproved = NEW.Lv_TransactionApproved;
SET NEW.Lv_USDValue = NEW.Lv_USDValue;
SET NEW.new_paymentprocessor = NEW.new_paymentprocessor;
SET NEW.Department = NEW.Department;
END IF;
END; //
DELIMITER ;

MySQL Deadlock on stored procedure when inserting

So for some reason, I just got this error message: `Deadlock found when trying to get lock; try restarting transaction, but I'm not quite sure why since I've never gotten this before, and it used to work fine.
Stored procedure
CREATE PROCEDURE `AddTestResultRequirement`(varTestResultId INT, RequirementNameId INT)
BEGIN
declare reqId INT;
declare linkId INT;
set linkId = -1;
IF NOT EXISTS(SELECT * FROM testcaserequirement where nameId = RequirementNameId) THEN
insert into testcaserequirement(id, nameId) values (NULL, RequirementNameId);
set reqId = last_insert_id();
ELSE
set reqId = GetRequirementNameId(RequirementNameId);
END IF;
IF NOT EXISTS(select * from testresultrequirementlink where requirementId = reqId and testresultId = varTestResultId) THEN
insert into testresultrequirementlink(id, requirementId, testresultId) values(NULL, reqId, varTestResultId);
set linkId = last_insert_id();
else
set linkId = GetRequirementTestResultLinkId(varTestResultId, reqId);
end if;
select linkId;
END
Function
CREATE FUNCTION `GetRequirementTestResultLinkId`(varTestResultId INT, RequirementId INT) RETURNS int(11)
BEGIN
RETURN (SELECT id from testresultrequirementlink where testresultid = varTestResultId and requirementId = RequirementId LIMIT 1);
END
Anyone who can see where this deadlock would appear?
I do know that deadlock means that a deadlock is when two transactions are trying to lock two locks at opposite orders, but I still can't figure out why I got it.

Not able to run/execute MySQL Triggers

I am creating Trigger into Mysql Workbanch , but not able to create the trigger.
Can Anyone Help me For This.
DELIMITER $$
create TRIGGER Insert_Test
After Delete ON Test
FOR EACH ROW
BEGIN
START TRANSACTION;
DECLARE v_ID INT;
DECLARE v_Error TINYINT;
DECLARE v_AgentID INT;
SELECT v_Error = 0;
v_ID=Old.id;
BEGIN
DELETE Test2 WHERE id = SELECT id FROM Test where id=v_ID;
Rollback;
SET v_Error = 1;
END
IF v_Error = 0;
THEN
COMMIT;
ELSEIF
v_Error = 1;
THEN
ROLLBACK;
END IF;
END
DELIMITER ;
Sql server Trigger
ALTER TRIGGER [dbo].[tr_DelRecordTypeID] ON [dbo].[luRecordType] FOR DELETE
AS
SET NOCOUNT ON
BEGIN TRANSACTION
DECLARE #ID INT, #GroupTypeID INT, #Error BIT, #Msg VARCHAR(500)
SELECT #Error = 0
SELECT #ID = RecordTypeID FROM deleted
SELECT #GroupTypeID = 30
IF EXISTS ( SELECT g.GroupID
FROM luGroup g,
[luGroupDetail] gd
WHERE g.[GroupID] = gd.[GroupID]
AND g.[GroupTypeID] = #GroupTypeID
AND gd.[MemberID] = #ID )
BEGIN
DELETE [agAgent] WHERE [AgentID] = (SELECT TOP 1 AgentID FROM agAgentPayType)
Rollback transaction
SET #Error = 1
END
IF #Error = 0
BEGIN
COMMIT TRANSACTION
END
ELSE
IF #Error = 1
BEGIN
ROLLBACK TRANSACTION
END
This trigger which i am trying to achieve into mysql workbanch, please check
I shall Be thankful,
Thanks
Aman

THROW after Rollbacking transaction SQL

Hi all I have a stored procedure where I have gone for Transaction as per the requirement, this is my SP
CREATE PROCEDURE ProcName
#Id INT,
#user_id INT
AS
BEGIN
BEGIN TRANSACTION [transName]
BEGIN TRY
DELETE
FROM table1
WHERE UserId= #user_id
UPDATE table2
SET DATE_MODIFIED = GETDATE()
WHERE ID= #Id
COMMIT TRANSACTION [transName]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [transName]
THROW
END CATCH
END
Iam getting an exeception like incorrect syntax near THROW can some tell where I am going wrong
Got the answer
CREATE PROCEDURE ProcName
#Id INT,
#user_id INT
AS
BEGIN
BEGIN TRANSACTION [transName]
BEGIN TRY
DELETE
FROM table1
WHERE UserId= #user_id
UPDATE table2
SET DATE_MODIFIED = GETDATE()
WHERE ID= #Id
COMMIT TRANSACTION [transName]
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
DECLARE #ErrorNumber INT = ERROR_NUMBER();
DECLARE #ErrorLine INT = ERROR_LINE();
SELECT CAST(ERROR_NUMBER() AS NVARCHAR(1000));
THROW;
END CATCH
END

SQL Server - Nested transactions in a stored procedure

Lets say this is the situation:
[Stored Proc 1]
BEGIN
BEGIN TRANSACTION
...
exec sp 2
COMMIT
END
Now, if SP 2 - rolls back for whatever reason, does SP 1 - commit or rollback or throw exception?
Thanks.
It is possible for the work done by SP2 to be rolled back and not loose the work done by SP1. But for this to happen, you must write your stored procedures using a very specific pattern, as described in Exception handling and nested transactions:
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare #trancount int;
set #trancount = ##trancount;
begin try
if #trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if #trancount = 0
commit;
end try
begin catch
declare #error int, #message varchar(4000), #xstate int;
select #error = ERROR_NUMBER(), #message = ERROR_MESSAGE(), #xstate = XACT_STATE();
if #xstate = -1
rollback;
if #xstate = 1 and #trancount = 0
rollback
if #xstate = 1 and #trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, #error, #message) ;
end catch
end
Not all errors are recoverable, there are a number of error conditions that a transaction cannot recover from, the most obvious example being deadlock (your are notified of the deadlock exception after the transaction has already rolled back). Both SP1 and SP# have to be written using this pattern. If you have a rogue SP, or you want to simple leverage existing stored procedures that nilly-willy issue ROLLBACK statements then your cause is lost.
There are no autonomous transactions in SQL Server. You may see ##TRANCOUNT increase beyond 1, but a rollback affects the whole thing.
EDIT asked to point to documentation. Don't know of the topic that documents this explicitly, but I can show it to you in action.
USE tempdb;
GO
Inner proc:
CREATE PROCEDURE dbo.sp2
#trip BIT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION;
PRINT ##TRANCOUNT;
IF #trip = 1
BEGIN
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END
ELSE
BEGIN
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
END
PRINT ##TRANCOUNT;
END
GO
Outer proc:
CREATE PROCEDURE dbo.sp1
#trip BIT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION;
PRINT ##TRANCOUNT;
BEGIN TRY
EXEC dbo.sp2 #trip = #trip;
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE();
END CATCH
PRINT ##TRANCOUNT;
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
PRINT ##TRANCOUNT;
END
GO
So now let's call it and let everything commit:
EXEC dbo.sp1 #trip = 0;
Results:
12110
Now let's call it and roll back the inner procedure:
EXEC dbo.sp1 #trip = 1;
Results:
120 <-- notice that a rollback here rolled back both
Transaction count after EXECUTE indicates a mismatching number
of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
00
If SP2 rolls back the transaction, SP1 will rollback as well.
See: http://msdn.microsoft.com/en-US/library/ms187844(v=sql.105).aspx for details.
In nested transactions, if any of the inner transations rolls back, all its outer transaction will rollback.
Here is a quick and dirty way to nest transactions in stored procedures (using the code from Aaron's answer) that can be useful sometimes. It uses a default parameter to indicate to the inner procedure if it is a nested call, and returns a success/fail result to the outer procedure.
CREATE PROCEDURE dbo.sp2
#trip BIT,
#nested BIT = 0
AS
BEGIN
SET NOCOUNT, XACT_ABORT ON
IF #nested = 0 BEGIN TRAN
PRINT ##TRANCOUNT
IF #trip = 1
BEGIN
IF #nested = 0 ROLLBACK
RETURN 1
END
ELSE
BEGIN
IF #nested = 0 COMMIT
END
PRINT ##TRANCOUNT
RETURN 0
END
GO
The outer procedure checks the success/fail an rolls back the transaction if appropriate.
CREATE PROCEDURE dbo.sp1
#trip BIT
AS
BEGIN
DECLARE #result INT
SET NOCOUNT, XACT_ABORT ON
BEGIN TRAN
PRINT ##TRANCOUNT
BEGIN TRY
EXEC #result = dbo.sp2 #trip = #trip, #nested = 1
IF #result <> 0
BEGIN
ROLLBACK
RETURN 1
END
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH
PRINT ##TRANCOUNT
COMMIT
PRINT ##TRANCOUNT
RETURN 0
END
GO
Every stored procedure must end with the same transaction count with which it entered. If the count does not match, SQL Server will issue error 266, "Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing."
If a stored procedure does not initiate the outermost transaction, it should not issue a ROLLBACK.
If a nested procedure begin a new transaction; but if it detects the need to roll back and the ##TRANSACTION value is greater than 1, it raises an error, returns an error message to the caller via out parameter or return value, and issues a COMMIT instead of a ROLLBACK.
CREATE PROCEDURE [dbo].[Pinner]
-- Add the parameters for the stored procedure here
#ErrorMessage varchar(max) out
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
begin tran
begin try
throw 51000, 'error occured', 1
commit tran
set #ErrorMessage = ''
end try
begin catch
set #ErrorMessage = ERROR_MESSAGE();
if ##TRANCOUNT = 1
rollback tran
if ##TRANCOUNT > 1
commit tran
end catch
END
create PROCEDURE [dbo].[Pouter]
-- Add the parameters for the stored procedure here
#ErrorMessage varchar(max) out
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
begin tran
begin try
EXECUTE [dbo].[Pinner]
#ErrorMessage OUTPUT
if #ErrorMessage <> '' begin
throw 51000, #ErrorMessage, 1
end
commit tran
set #ErrorMessage = ''
end try
begin catch
set #ErrorMessage = ERROR_MESSAGE();
if ##TRANCOUNT = 1
rollback tran
if ##TRANCOUNT > 1
commit tran
end catch
END
DECLARE #ErrorMessage varchar(max)
EXEC [dbo].[Pouter]
#ErrorMessage = #ErrorMessage OUTPUT
SELECT #ErrorMessage as N'#ErrorMessage'
https://www.codemag.com/article/0305111/handling-sql-server-errors-in-nested-procedures
USE [DemoProject]
GO
/****** Object: StoredProcedure [dbo].[Customers_CRUD] Script Date: 11-Jan-17 2:57:38 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[Customers_CRUD]
#Action VARCHAR(10)
,#BId INT = NULL
,#Username VARCHAR(50) = NULL
,#Provincename VARCHAR(50) = NULL
,#Cityname VARCHAR(50) = NULL
,#Number VARCHAR(50) = NULL
,#Name VARCHAR(50) = NULL
,#ContentType VARCHAR(50) = NULL
,#Data VARBINARY(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON;
--SELECT
IF #Action = 'SELECT'
BEGIN
SELECT BId , Username,Provincename,Cityname,Number,Name,ContentType, Data
FROM tblbooking
END
--INSERT
IF #Action = 'INSERT'
BEGIN
INSERT INTO tblbooking(Username,Provincename,Cityname,Number,Name,ContentType, Data)
VALUES (#Username ,#Provincename ,#Cityname ,#Number ,#Name ,#ContentType ,#Data)
END
--UPDATE
IF #Action = 'UPDATE'
BEGIN
UPDATE tblbooking
SET Username = #Username,Provincename = #Provincename,Cityname = #Cityname,Number = #Number,Name = #Name,ContentType = #ContentType,Data = #Data
WHERE BId = #BId
END
--DELETE
IF #Action = 'DELETE'
BEGIN
DELETE FROM tblbooking
WHERE BId = #BId
END
END
GO