Parallel insert stored procedure fails in SQL SERVER - sql-server

The below SP fails when running parallely in multiple threads.
Tried to use different isolation levels in the SP, but still i face the same error.
Violation of UNIQUE KEY constraint
'AK_MerchantTransactionEnd_MerchantReferenceCode_BankReferenceCode'.
Cannot insert duplicate key in object 'dbo.EpayTransaction'. The
duplicate key value is (20160503171525689435, 20160329221725169, 0).
Table has UNIQUE Constraint for MerchantReferenceCode_BankReferenceCode
CREATE PROCEDURE [dbo].[uspUpdateEpayConfirmation]
#merchantReferenceCode VARCHAR(30) ,
#bankGatewayCode VARCHAR(30) ,
#transactionNumber VARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE #timestamp DATETIME;
SET #timestamp = GETDATE();
IF EXISTS ( SELECT 1
FROM [dbo].EpayTransaction WITH (NOLOCK)
WHERE [dbo].EpayTransaction.[MerchantReferenceCode] = #merchantReferenceCode
)
BEGIN
RETURN 0;
END;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
-- update the epayment transaction information with the merchent reference code
-- updating the status of the transaction
UPDATE [dbo].[CustomerTransaction]
SET [dbo].[CustomerTransaction].[ModifiedBy] = 1 ,
[dbo].[CustomerTransaction].[ModifiedOn] = #timestamp
WHERE [dbo].[CustomerTransaction].[MerchantReferenceCode] = #merchantReferenceCode;
-- adding a record to EpayTransaction table to conclude the transaction is successful
INSERT INTO [dbo].EpayTransaction
( [dbo].EpayTransaction.[BankReferenceCode] ,
[dbo].EpayTransaction.[BankTransactionDate] ,
[dbo].EpayTransaction.[MerchantReferenceCode]
)
VALUES ( #bankGatewayCode ,
#timestamp ,
#merchantReferenceCode
);
COMMIT TRANSACTION;
RETURN 1;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
-- Raise an error with the details of the exception
DECLARE #errMsg NVARCHAR(4000) ,
#errSeverity INT;
SELECT #errMsg = ERROR_MESSAGE() ,
#errSeverity = ERROR_SEVERITY();
RAISERROR(#errMsg, #errSeverity, 1);
END CATCH;
END;

Related

Insert values statement can contain only constant literal values or variable references error while running below procedure

I am getting:
Insert values statement can contain only constant literal values or variable references error
while running the below procedure at insertion in dbo.DB_time_log table.
CREATE PROCEDURE [dbo].[sp_chm_dedupe_import] AS
BEGIN
SET NOCOUNT ON;
DECLARE #StartTime DATETIME;
DECLARE #EndTime DATETIME;
SET #StartTime = GETDATE();
PRINT 'Procedure Started at :'
+ RTRIM(CAST(GETDATE() AS nvarchar(30)));
BEGIN TRAN
BEGIN TRY
INSERT INTO [CHM].[UCIC_PAN_INDIA_TEST_TS]
SELECT
CAST(UCID AS bigint),CAST(UCIC_OLD AS bigint),CAST(HID AS bigint),
CAST(HID_OLD AS bigint),CAST(CID AS VARCHAR(20)),CAST(BU AS VARCHAR(10)),
CAST(TIME_STAMP AS VARCHAR(20))
FROM [dbo].[CHM_EXT_All_India_DL]
OPTION (LABEL = 'CHM_AllIndia_EXT_Data_Load');
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRAN;
PRINT 'ROLLBACK';
END
END CATCH;
IF ##TRANCOUNT >0
BEGIN
PRINT 'COMMIT';
COMMIT TRAN;
END
SET #EndTime = GETDATE();
INSERT INTO dbo.DB_time_log
VALUES
(
'SP_CHM_DEDUPE_IMPORT',
#StartTime,
#EndTime,
GETDATE()
);
PRINT 'Procedure Ended at :'
+ RTRIM(CAST(GETDATE() AS nvarchar(30)));
SET NOCOUNT OFF;
END
GO

SQL Server trigger is not firing on insert?

For some reason this trigger does not fire.
The trigger is active and data is being inserted to p2 every 20 seconds. I have also tried try catch blocks and there is no exception.
ALTER TRIGGER [dbo].[load_to_p_2]
ON [dbo].[p2]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [Log].[dbo].[Log] ([type], [message], [created_at])
VALUES ('trigger load_to_p_2', '----', GETUTCDATE());
END
And this is the code that inserts the data into p2:
ALTER PROCEDURE [dbo].[LoadTop2]
AS
SET NOCOUNT ON
DECLARE #r INT;
SET #r = 1;
WHILE #r > 0
BEGIN
BEGIN TRANSACTION;
DELETE TOP (1000)
FROM pa3_log
OUTPUT deleted.id, deleted.browser INTO p2 (id, browser)
SET #r = ##ROWCOUNT;
COMMIT TRANSACTION;
END

Transaction does not rollback all changes

I've ran into a procedure in SQL Server 2017 that has a transaction within a try-catch block. It isn't nested, just got an identity table filled and cycled using cursor. So try-catch is within a loop, some other procedure is called. Sometimes that procedure fails with constraint violation error and it's perfectly fine to save whatever succeeded prior to it's inner exception. And then I bumped into commit in catch clause. It made me wondering and I written this code:
DECLARE #Table TABLE (ID INT NOT NULL PRIMARY KEY)
DECLARE #Input TABLE (ID INT)
INSERT INTO #Input
VALUES (1), (1), (2), (NULL), (3)
DECLARE #Output TABLE (ID INT)
--SET XACT_ABORT OFF
DECLARE #ID int
DECLARE [Sequence] CURSOR LOCAL FAST_FORWARD FOR
SELECT ID FROM #Input
OPEN [Sequence]
FETCH NEXT FROM [Sequence] INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
BEGIN TRAN
DECLARE #Msg nvarchar(max) = 'Inserting ''' + TRY_CAST(#ID as varchar(11)) + ''''
RAISERROR (#Msg, 0, 0) WITH NOWAIT
-- Order is important
--INSERT INTO #Table VALUES (#ID)
INSERT INTO #Output VALUES (#ID)
INSERT INTO #Table VALUES (#ID)
COMMIT TRAN
END TRY
BEGIN CATCH
SET #Msg = 'Caught ' + CAST(ERROR_NUMBER() as varchar(11)) + ' : ' + ERROR_MESSAGE()
RAISERROR (#Msg, 1, 1) WITH NOWAIT
IF XACT_STATE() = -1
BEGIN
SET #Msg = 'Uncommitable transaction [-1]'
RAISERROR (#Msg, 1, 1) WITH NOWAIT
ROLLBACK TRAN
END
IF XACT_STATE() = 1
BEGIN
SET #Msg = 'Commitable transaction [1]'
RAISERROR (#Msg, 1, 1) WITH NOWAIT
COMMIT TRAN
END
END CATCH
FETCH NEXT FROM [Sequence] INTO #ID
END
SELECT * FROM #Table
SELECT * FROM #Output
So as I tried interchanging the order of #Output and #Table inserts, I got different results, no matter what XACT_ABORT is set to or whether I commit or rollback transaction in the catch block. I was always sure, that everything gets rolled back and both #Output and #Table tables will be equal....
What I am doing wrong here? I this a default transaction behavior?
This is a fun one, but your code does what I'd expect it to. Table variables do not obey transactional semantics. Temporary tables do though! So if you need the ability to roll back mutations to your temporary "thing", use a table and not a variable.
Note though that your sequence will still have values pulled from it. Even it you also put that in the transaction.
As Ben Thul reminded, only temporary or normal tables should be used here. So when exception is caught and XACT_STATE() = 1 (Commitable transaction), COMMIT will keep whatever succeeded and ROLLBACK will undo the whole thing.
IF XACT_STATE() = 1
BEGIN
SET #Msg = 'Commitable transaction [1]'
RAISERROR (#Msg, 1, 1) WITH NOWAIT
COMMIT TRAN -- Keep changes or undo everything (ROLLBACK)
END
Output table results:
ROLLBACK: [1,2,3]
COMMIT : [1,1,2,NULL,3]

Retrieving a particular value in SQL Server

I have this stored procedure that has a value:
BEGIN TRY
BEGIN TRANSACTION;
-- Insert statements for procedure here
DECLARE #return_value int
DECLARE #media_id uniqueidentifier
'INSERT SQL STATEMENT HERE
SELECT [media_id] FROM [media] WHERE 1 = 1
-- One row affected
RETURN 1
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION;
END
-- Rollback, no row affected
RETURN 0
END CATCH;
I want to call the [media_id] value from another stored procedure. How do I get that value?
Table Definition
CREATE TABLE MY_EMPLOYEE
(EMPID INT, NAME VARCHAR(20),
LANGUAGEID INT , ID UNIQUEIDENTIFIER DEFAULT NEWID())
GO
Stored Procedure
ALTER PROCEDURE usp_ProcName
#Emp_ID INT = null,
#Name VARCHAR(20) = null,
#LanguageID int = null,
#NewID UNIQUEIDENTIFIER OUTPUT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
-- Insert statements for procedure here
INSERT INTO [Practice_DB].[dbo].[MY_EMPLOYEE](EMPID, NAME, LANGUAGEID)
VALUES (#Emp_ID, #Name, #LanguageID);
-- Populating the OUTPUT variable using the other variables that were passed
-- for INSERT statement.
SELECT #NewID = ID
FROM [Practice_DB].[dbo].[MY_EMPLOYEE]
WHERE EMPID = #Emp_ID
-- One row affected
COMMIT TRANSACTION
RETURN 1
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION;
END
-- Rollback, no row affected
RETURN 0
END CATCH
END
GO
Calling Stored Procedure
DECLARE #value int, #ID VARCHAR(100)
EXECUTE #value = usp_ProcName
#Emp_ID = 50,
#Name = 'John',
#LanguageID = 50,
#NewID = #ID OUTPUT --<-- passing this variable with OUTPUT key word this will be
-- populated inside the Procedure and then you can SELECT it or
-- whatever you want to do with this value.
SELECT #ID
SELECT #value
This should help: Return Data from a Stored Procedure
I think this should work
BEGIN TRY
BEGIN TRANSACTION;
-- Insert statements for procedure here
DECLARE #return_value int
DECLARE #media_id uniqueidentifier
--get the value from the stored procedure here
Declare #SQL varchar(max)
set #SQL='INSERT SQL STATEMENT HERE
SELECT #media_id FROM [media] WHERE 1 = 1'
exec #SQL
-- One row affected
RETURN 1
END TRY

How to know TSQL Stored Procedure Update Executed

How can I check if my TSQL stored procedure updated within the stored procedure in order to create a proper message?
Example:
ALTER PROCEDURE [dbo].[pUpdate]
#id uniqueidentifier,
#status int,
#message VARCHAR(100) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
UPDATE [database].[dbo].[user]
SET status = #status
WHERE Id = #id
END
IF (SUCCESSFUL)
BEGIN
#message = 'Success!'
END
What are some possible ways to check if successful without using the parameters again?
This is what I currently use:
SELECT COUNT(*)
WHERE status = #status AND id = #id
Are there any other ways? I want to know for my knowledge and reference. Thanks.
Have you checked out ##ROWCOUNT? Might be what you're looking for (see this for details: http://technet.microsoft.com/en-us/library/ms187316.aspx). Basically it returns the number of rows affected by the last statement. I'd imagine if it were not "successful", it would be zero rows.
ALTER PROCEDURE [dbo].[pUpdate]
#id uniqueidentifier,
#status int,
#message VARCHAR(100) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
UPDATE [database].[dbo].[user]
SET status = #status
WHERE Id = #id
END
IF (##ROWCOUNT > 0)
BEGIN
#message = 'Success!'
END
ELSE
BEGIN
#message = 'Not success!'
END
You can use a try catch block and log the success or failure to a table.
BEGIN TRY
BEGIN TRANSACTION
-- Add Your Code Here
-- Log Success to a log table
COMMIT
END TRY
BEGIN CATCH
-- Log failure to a log table
ROLLBACK
END CATCH
I would use ##ERROR system variable to check whether the last sentence was successfull (error # = 0) or not (error # > 0 ):
USE Database;
GO
BEGIN
UPDATE TableName
SET ColumnA = 4
WHERE ColumnB = 1;
END
IF (##ERROR = 0)
BEGIN
PRINT N'Successfull Update';
GO
END
You can go deeper into Microsoft MSDN here: http://technet.microsoft.com/es-es/library/ms188790.aspx
ALTER PROCEDURE [dbo].[pUpdate]
#id uniqueidentifier,
#status int,
#message VARCHAR(100) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
UPDATE [database].[dbo].[user]
SET status = #status
WHERE Id = #id
END
IF (##ROWCOUNT > 0)
BEGIN
SELECT #message = 'Success!'
END
ELSE
BEGIN
SELECT #message = 'Not success!'
END

Resources