Stored Procedure Concurrency failure issue with Inserting Primary key value - sql

I am executing stored procedure with multiple request at same time then it fails and i am getting below error.
"Violation of PRIMARY KEY constraint 'PK_catalogs_mstr'. Cannot insert > duplicate key in object 'dbo.catalogs_mstr'. The duplicate key value is > (1).\r\nThe statement has been terminated."
My Table has Catalog_Code column as Primary key with out Auto increment. I am trying to generate value for this column with in stored procedure.
CREATE PROCEDURE [dbo].[usp_CreateCatalogDefination]
#AccountId INT,
#UserId INT,
#CatalogName NVARCHAR(100),
#DataSourceTypeId SMALLINT,
#AttributeGroupId SMALLINT,
#ReferenceCatalogCode INT,
#ImageName NVARCHAR (100),
#ImageFile NVARCHAR (100),
#ImagePath NVARCHAR (100),
#Result int OUTPUT
AS
BEGIN
SET NOCOUNT ON
/* Declaration Block */
DECLARE #ErrorMessage NVARCHAR(4000)
DECLARE #ErrorSeverity INT
DECLARE #ErrorState INT
DECLARE #ValidReferenceCatalogCode INT
BEGIN TRY
BEGIN TRANSACTION
IF EXISTS(SELECT id from accounts_mstr WHERE id=#AccountId AND account_type_id =2) /* Account should be of Seller Type */
BEGIN
IF EXISTS(SELECT id from users_mstr WHERE id=#UserId) /* Check for Valid User Id */
BEGIN
/* Generate Catalog Code */
DECLARE #CatalogCode INT
SET #CatalogCode = (SELECT TOP 1 catalog_code FROM catalogs_mstr Order By catalog_code desc)
IF(#CatalogCode IS NULL)
BEGIN
SET #CatalogCode = 1
END
ELSE
BEGIN
SET #CatalogCode = #CatalogCode + 1
END
IF EXISTS(SELECT id from attribute_groups WHERE id=#AttributeGroupId) /* Check for attribute group */
BEGIN
INSERT INTO [dbo].[catalogs_mstr](
[catalog_code],
[seller_account_id],
[user_id],
[catalog_name],
[datasource_type_id],
[attribute_group_id],
[image_name],
[image_file],
[image_path]
)
VALUES (
#CatalogCode,
#AccountId,
#UserId,
LTRIM(RTRIM(#CatalogName)),
#DataSourceTypeId,
#AttributeGroupId,
LTRIM(RTRIM(#ImageName)),
LTRIM(RTRIM(#ImageFile)),
LTRIM(RTRIM(#ImagePath))
)
COMMIT TRANSACTION
SET #Result=1 /* Success */
RETURN #Result
END
ELSE
BEGIN
ROLLBACK TRANSACTION
SET #Result=2 /* Invalid Attribute Group Id */
RETURN #Result
END
END
ELSE
BEGIN
ROLLBACK TRANSACTION
SET #Result=4 /* Invalid User Id */
RETURN #Result
END
END
ELSE
BEGIN
ROLLBACK TRANSACTION
SET #Result=5 /* Invalid Seller Account Id */
RETURN #Result
END
END TRY
BEGIN CATCH
--USE CATCH BLOCK FOR RAISING ERROR TO APPLICATION END
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
SELECT #ErrorMessage = 'Error : ' + HOST_NAME() + ' : ' + OBJECT_NAME(##PROCID) + ' : '
+ ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
RAISERROR (
#ErrorMessage,
#ErrorSeverity,
#ErrorState
);
RETURN
END CATCH
SET NOCOUNT OFF
END

Related

Code review - Transaction without begin try in procedure

I'm doing a code review for a new procedure in sql
and i have two question please:
The commit transaction is encapsulated in the whole procedure - i think it should be only in the insert statement.
If there is a begin transaction, there should be also a error handling.
I am adding the code before
BEGIN
DECLARE #paymentSpecificationId BIGINT ;
BEGIN TRANSACTION
SET #paymentSpecificationId = (SELECT Id FROM [dbo].[PaymentSpecifications]
WHERE PaymentSpecificationGuid = #PaymentSpecificationGuid) ;
IF(#paymentSpecificationId > 0)
BEGIN
SELECT #paymentSpecificationId AS Results;
COMMIT TRANSACTION;
RETURN;
END
INSERT INTO [dbo].[PaymentSpecifications]
(
[CurrencyBalanceId],
[PaymentSpecificationGuid],
[PaymentSpecificationTypeId],
[ExternalId],
[Reference],
[ExternalOriginatorId],
[EventTimestamp],
[Created]
)
VALUES
(
#CurrencyBalanceId,
#PaymentSpecificationGuid,
#PaymentSpecificationTypeId,
#ExternalId,
#Reference,
#ExternalOriginatorId,
#EventTimestamp,
#Created
)
SELECT SCOPE_IDENTITY() AS Results;
COMMIT TRANSACTION;
END
and the code after my suggestion fix
BEGIN
BEGIN TRY
DECLARE #paymentSpecificationId BIGINT ;
SET #paymentSpecificationId = (SELECT Id FROM [dbo].[PaymentSpecifications]
WHERE PaymentSpecificationGuid = #PaymentSpecificationGuid) ;
IF(#paymentSpecificationId > 0)
BEGIN
SELECT #paymentSpecificationId AS Results;
RETURN;
END
BEGIN TRANSACTION
INSERT INTO [dbo].[PaymentSpecifications]
(
[CurrencyBalanceId],
[PaymentSpecificationGuid],
[PaymentSpecificationTypeId],
[ExternalId],
[Reference],
[ExternalOriginatorId],
[EventTimestamp],
[Created]
)
VALUES
(
#CurrencyBalanceId,
#PaymentSpecificationGuid,
#PaymentSpecificationTypeId,
#ExternalId,
#Reference,
#ExternalOriginatorId,
#EventTimestamp,
#Created
)
SELECT SCOPE_IDENTITY() AS Results;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
DECLARE #ErrMsg nvarchar(4000), #ErrSeverity int
SELECT #ErrMsg = ERROR_MESSAGE(),
#ErrSeverity = ERROR_SEVERITY()
RAISERROR(#ErrMsg, #ErrSeverity, 1)
END CATCH
END
GO
thanks

Problems with ROLLBACK TRANSACTION inside try/catch

I'm having this error when I try to execute this code:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
I know the problem is on the sp [dbo].[QueueInsert], the sp has an error and it goes directly to the catch. It's funny because the first one (EXEC [dbo].[BacthInsert]) is inserting a summary record and after running the sp, I see the record, so is doing a commit and not doing a rollback. What's wrong with this code? thanks!
CREATE PROCEDURE [cnfg].[SendEmail]
(
,#Employees [dbo].[Employees] readonly
,#ApplicationName NVARCHAR(256)
,#ErrorMsg NVARCHAR(300) = NULL OUTPUT
)
AS
BEGIN
DECLARE #ReturnVal INT
DECLARE #ApplicationId UNIQUEIDENTIFIER
DECLARE #NewIdBatch INT
DECLARE #ID INT
DECLARE #EmployeeId INT
DECLARE #Index INT = 1
DECLARE #Total INT
SET NOCOUNT ON;
SET XACT_ABORT ON;
SET #ReturnVal = 0;
SET #ErrorMsg = '';
SET #ApplicationId = [GetId](#ApplicationName);
IF (#ApplicationId IS NULL)
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'The Application Name does not exist in the database';
Goto ProcedureExit
END
----------------------------------------------------------
BEGIN TRY -- Start Main TRY
----------------------------------------------------------
BEGIN TRANSACTION;
EXEC [dbo].[BacthInsert]
#ParameterId = 1
,#ID = #NewSendEmailBatchId OUTPUT
,#ErrorMsg = #ErrorMsg OUTPUT
IF ( #ErrorMsg <> '' )
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'There was an error trying to insert data into [dbo].[BacthInsert] table';
RAISERROR(#ErrorMsg, 16, 1)
END
SELECT ROW_NUMBER() OVER ( ORDER BY EmployeeId ) Row,
EmployeeId
INTO #EmpIds
FROM #Employees
SELECT #Total = COUNT(*) FROM #EmpIds
WHILE ( #Index <= #Total )
BEGIN
SELECT #EmployeeId=EmployeeId FROM #EmpIds WHERE Row = #Index
EXEC [dbo].[QueueInsert]
#SendEmailBatchId = #NewIdBatch
,#ID = #ID OUTPUT
,#ErrorMsg = #ErrorMsg OUTPUT
IF ( #ErrorMsg <> '' )
BEGIN
SET #ReturnVal = 1;
SET #ErrorMsg = 'There was an error trying to insert data into [dbo].[QueueInsert] table';
RAISERROR(#ErrorMsg, 16, 1)
END
SET #Index+=1;
END
COMMIT TRANSACTION;
----------------------------------------------------------
END TRY -- End Main TRY
----------------------------------------------------------
----------------------------------------------------------
BEGIN CATCH -- Start Main CATCH
----------------------------------------------------------
SELECT ERROR_MESSAGE()
IF (XACT_STATE()) = -1
BEGIN
ROLLBACK TRANSACTION;
END;
----------------------------------------------------------
END CATCH -- End Main CATCH
----------------------------------------------------------
ProcedureExit:
RETURN #ReturnVal;
END

does it run anything after a catch block?

I'm working with SQL Server Express 2012 and I have this stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[usp_AsyncExecActivated]
as
begin
set nocount on;
declare #h uniqueidentifier
, #messageTypeName sysname
, #messageBody varbinary(max)
, #xmlBody xml
, #procedureName sysname
, #startTime datetime
, #finishTime datetime
, #execErrorNumber int
, #execErrorMessage nvarchar(2048)
, #xactState smallint
, #token uniqueidentifier;
begin transaction;
begin try;
receive top(1)
#h = [conversation_handle]
, #messageTypeName = [message_type_name]
, #messageBody = [message_body]
from [AsyncExecQueue];
if (#h is not null)
begin
if (#messageTypeName = N'DEFAULT')
begin
-- The DEFAULT message type is a procedure invocation.
-- Extract the name of the procedure from the message body.
--
select #xmlBody = CAST(#messageBody as xml);
select #procedureName = #xmlBody.value(
'(//procedure/name)[1]'
, 'sysname');
update dbo.Configurations with (serializable) set conf_value = 1
where sp_name = #procedureName
if ##rowcount = 0
begin
insert dbo.Configurations(sp_name, conf_value) values (#procedureName, 1)
end
save transaction usp_AsyncExec_procedure;
select #startTime = GETUTCDATE();
begin try
exec #procedureName;
end try
begin catch
-- This catch block tries to deal with failures of the procedure execution
-- If possible it rolls back to the savepoint created earlier, allowing
-- the activated procedure to continue. If the executed procedure
-- raises an error with severity 16 or higher, it will doom the transaction
-- and thus rollback the RECEIVE. Such case will be a poison message,
-- resulting in the queue disabling.
--
select #execErrorNumber = ERROR_NUMBER(),
#execErrorMessage = ERROR_MESSAGE(),
#xactState = XACT_STATE();
if (#xactState = -1)
begin
rollback;
raiserror(N'Unrecoverable error in procedure %s: %i: %s', 16, 10,
#procedureName, #execErrorNumber, #execErrorMessage);
end
else if (#xactState = 1)
begin
rollback transaction usp_AsyncExec_procedure;
end
end catch
select #finishTime = GETUTCDATE();
select #token = [conversation_id]
from sys.conversation_endpoints
where [conversation_handle] = #h;
if (#token is null)
begin
raiserror(N'Internal consistency error: conversation not found', 16, 20);
end
update [AsyncExecResults] set
[start_time] = #starttime
, [finish_time] = #finishTime
, [error_number] = #execErrorNumber
, [error_message] = #execErrorMessage
where [token] = #token;
if (0 = ##ROWCOUNT)
begin
raiserror(N'Internal consistency error: token not found', 16, 30);
end
end conversation #h;
end
else if (#messageTypeName = N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog')
begin
end conversation #h;
end
else if (#messageTypeName = N'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
begin
declare #errorNumber int
, #errorMessage nvarchar(4000);
select #xmlBody = CAST(#messageBody as xml);
with xmlnamespaces (DEFAULT N'http://schemas.microsoft.com/SQL/ServiceBroker/Error')
select #errorNumber = #xmlBody.value ('(/Error/Code)[1]', 'INT'),
#errorMessage = #xmlBody.value ('(/Error/Description)[1]', 'NVARCHAR(4000)');
-- Update the request with the received error
select #token = [conversation_id]
from sys.conversation_endpoints
where [conversation_handle] = #h;
update [AsyncExecResults] set
[error_number] = #errorNumber
, [error_message] = #errorMessage
where [token] = #token;
end conversation #h;
end
else
begin
raiserror(N'Received unexpected message type: %s', 16, 50, #messageTypeName);
end
end
commit;
end try
begin catch
declare #error int
, #message nvarchar(2048);
select #error = ERROR_NUMBER()
, #message = ERROR_MESSAGE()
, #xactState = XACT_STATE();
if (#xactState <> 0)
begin
rollback;
end;
update dbo.Configurations with (serializable)
set conf_value = 0
where sp_name = #procedureName
raiserror(N'Error: %i, %s', 1, 60, #error, #message) with log;
end catch
update dbo.Configurations with (serializable) set conf_value = 0
where sp_name = #procedureName
end
I have to do this:
update dbo.Configurations with (serializable)
set conf_value = 0
where sp_name = #procedureName
Every time before stored procedure ends. I'll check dbo.Configurations to see if usp_AsyncExecActivated is running or not.
Do I have to add that update on CATCH BLOCK and after the CATCH BLOCK?
I'm not sure if after catch block runs anything else or it ends stored procedure execution.
It depends on severity, if there's a session sborting error it will stop executing the procedure. Otherwise, it will continue after CATCH block. See this simplified example:
create proc x
as
begin try
select 1/0
end try
begin catch
select error_message()
raiserror (N'Received unexpected message type', 16, 50);
end catch
select 'after catch'
go
exec x;

The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction?

Hello Every One The Following Error Comes To Me Suddenly When I Changed Simple And Few Thing On My Stored Procedure Code And It Was Working Great And If I Cleared The Updated Code It Works Fine Again So I Do Not Know What Is The Reason
And Here Is The Error "The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction"
Thanks In Advance
ALTER Procedure [dbo].[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]
#English Bit = 0 ,
#ID BigInt = NULL Output ,
#Company_ID SmallInt = NULL ,
#Cust_ID NVarChar (20) = NULL ,
#Cust_Cat NVarChar (10) = NULL ,
#Debit_Limit Decimal (18,3) = NULL ,
#From_Date NVarChar (19) = NULL ,
#To_Date NVarChar (19) = NULL ,
#User_ID NVarChar(50) = NULL
As
BEGIN
Create Table #Errors (ErrorNumber int Not Null, ErrorValue nvarchar(300) Collate Arabic_CI_AS Null);
Begin Tran trn_INV_CustDebLim_setupInsert;
Begin Try
Insert Into INV_Cust_Debit_Limit_setup
( Company_ID , Cust_ID , Cust_Cat , Debit_Limit , From_Date , To_Date )
Values
( #Company_ID , #Cust_ID , #Cust_Cat , #Debit_Limit , #From_Date , #To_Date )
set #ID = ##IDENTITY
-- From Here Is New Part --
declare #str nvarchar(50)
IF(#Cust_ID IS NOT NULL)
set #str = 'xxxxxxxxxxxxxxxxxxxxxxxxxxx ' + #Cust_ID
IF(#Cust_Cat IS NOT NULL)
set #str += 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx ' + #Cust_Cat
set #str += ' xxxxx' +#Debit_Limit + ' xxxx xx' +#From_Date
IF(#English = 1)
BEGIN
IF(#Cust_ID IS NOT NULL)
set #str = 'Credit Limit Added On Customer ' + #Cust_ID
IF(#Cust_Cat IS NOT NULL)
set #str += 'Credit Limit Added On Customer Category ' + #Cust_Cat
set #str = ' With Value ' +#Debit_Limit + ' From Date ' +#From_Date
END
exec usp_GLApplicationAudit #Company_ID , NULL , 10 , #User_ID , #str ,#ID ,10, NULL , NULL ,NULL ,NULL,NULL,'SAl'
-- To Here Is New Part --
End Try
Begin Catch
Insert #Errors Values(1002,ERROR_MESSAGE());
Goto Finished;
End Catch
-- Return Error Records
Finished:
-- retrieve Errors and commit/Rollbak Trans.
If (Select count(E.ErrorNumber)
From #Errors E Left Join GVT_Errors G
On E.ErrorNumber = G.Err_Number
Where G.Err_Type=0 ) > 0
Begin
-- The Following are Errors
Select E.ErrorNumber As [Err_No], G.MessageA + ': ' + E.ErrorValue As [Err_Desc], Err_Source, dbo.fn_GetItemDescription('Err_Type',Cast(Err_Type As nvarchar), null, null, null, null, 0) As Err_Type_Desc, Err_Type, SeverityLevel As [Severity], CategoryID
From #Errors E Left Join GVT_Errors G
On E.ErrorNumber = G.Err_Number;
Rollback Tran trn_INV_CustDebLim_setupInsert;
End
Else
Begin
-- The Following Not Errors They are Warnings or Information
Select E.ErrorNumber As [Err_No], G.MessageA + ': ' + E.ErrorValue As [Err_Desc], Err_Source, dbo.fn_GetItemDescription('Err_Type',Cast(Err_Type As nvarchar), null, null, null, null, 0) As Err_Type_Desc, Err_Type, SeverityLevel As [Severity], CategoryID
From #Errors E Left Join GVT_Errors G
On E.ErrorNumber = G.Err_Number;
Commit Tran trn_INV_CustDebLim_setupInsert;
End
DROP Table #Errors;
END
In the CATCH code you must check the state of XACT_STATE() and act accordingly. For a procedure template that handles transactions and try/catch blocks correctly see 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

sql server The multi-part identifier " " could not be bound stored procedure

I create a stored procedure in which I call two functions .
I'm getting an error:
Msg 4104, Level 16, State 1, Procedure Add_Translation, Line 25
The multi-part identifier ".word" could not be bound.
This is my stored procedure:
ALTER PROCEDURE [dbo].[Add_Translation]
#english nvarchar(70),
#kurdish nvarchar(70),
#english_category int,
#kurdish_category int,
#result int out
AS
BEGIN
SET NOCOUNT ON;
if #english is not null and #kurdish is not null and #english_category is not null and #kurdish_category is not null
begin
declare #intEnId int=-1;
exec #intEnId = Check_English_word #text = #english;
declare #identityEnglish int;
declare #identityKurdish int;
if #intEnId = -1
begin
insert into english_table values(#english, #english_category);
set #identityEnglish = SCOPE_IDENTITY();
end
else
begin
set #identityEnglish = (select e.english_id from english_table e where UPPER(.word)=UPPER(#english));
end
declare #intKuId int=-1;
exec #intKuId=Check_Kurdish_Word #word=#kurdish;
if #intKuId =-1
begin
insert into kurdish_table values(#kurdish, #kurdish_category);
set #identityKurdish = SCOPE_IDENTITY();
end
else
begin
set #identityKurdish = (select k.kurdish_id from kurdish_table k where upper(k.word)=upper(#kurdish));
end
declare #translated int =0;
exec #translated = Check_Translation #english_id = #identityEnglish, #kurdish_id = #identityKurdish;
if #translated=0
begin
insert into transactions values(#identityEnglish, #identityKurdish);
set #result = 1;
end
else
begin
set #result = 2;
end
end
else
begin
set #result = 0;
end
END
Here is the first function:
ALTER FUNCTION [dbo].[Check_English_word]
(
#text nvarchar(70)
)
RETURNS int
AS
BEGIN
DECLARE #Result int
set #Result=-1;
if #text is not null
begin
SELECT #Result = e.english_id
from english_table e
where UPPER(e.word) = UPPER(#text);
end
RETURN #Result
END
Second function:
ALTER FUNCTION [dbo].[Check_Kurdish_Word]
(
#word nvarchar(70)
)
RETURNS int
AS
BEGIN
DECLARE #Result int
set #Result=-1;
if #word is not null
begin
SELECT #Result = k.kurdish_id
from kurdish_table k
where UPPER(k.word) = UPPER(#word);
end
RETURN #Result
END
You are missing an e in this line
set #identityEnglish=(select e.english_id from english_table e
where UPPER(.word)=UPPER(#english));
Change it to
set #identityEnglish=(select e.english_id from english_table e
where UPPER(e.word)=UPPER(#english));
Also, it is good practice to specify columns when doing an insert -
ie
insert into english_table values(#english,#englsih_category);
should be
insert into english_table (word, category) values(#english,#englsih_category);