I Have a stored procedure and its task is that, It will take a file and transfer the records into TEMP table after that will do validation accordingly and then it will put the records into Main Staging table.(Aditionally we have 3 more tables for validation purpose and those are StockIntegrityCheck, IntegrityErrorLog, InterfaceIntegrity )
Problem: When an empty file has come(0KB file), It is not supposed to put any entry any of the tables. but it is putting an entry in InteggrityErrorLog Table which is not needed. I want to correct the stored procedure accordingly?
Here is SP:
USE [PARAM.DB]
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo]. [usp_LDMDB_GMIH_DSOA_STAGING]') AND type in (N'P'))
DROP PROCEDURE [dbo].[usp_LDMDB_GMIH_DSOA_STAGING]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
/*
Description: This program will insert records from LDMDB_GMIH_DSO_A_STAGING_TEMP table to LDMDB_GMIH_DSO_A_STAGING table
*/
CREATE PROCEDURE [dbo].[usp_LDMDB_GMIH_DSOA_STAGING]
AS
DECLARE
#DATA VARCHAR(200),
#HDRDATA VARCHAR(200),
#FLAG INT,
#ErrStmt VARCHAR(500),
#ProcessDate datetime,
/* Stock Integrity Changes for I0180 Starts */
#Integritycheck INT,
#Totalcount VARCHAR(10),
#Totalquantity VARCHAR(10),
#TrackingID VARCHAR(20),
#count INT,
#quantity INT;
/* Stock Integrity Changes for I0180 Ends */
BEGIN
BEGIN TRANSACTION
select #ProcessDate = GETDATE()
/* Stock Integrity Changes for I0180 Starts */
SET #count = 0;
SET #quantity = 0;
SET #Integritycheck = (SELECT Value from dbo.LDMStockIntegrityCheck where interfacename = 'I0180' and filename = 'PIZ.HW.NDC.CALLOFF.FILE.LDM');
/* Stock Integrity Changes for I0180 Ends */
DECLARE CUR_JOB CURSOR LOCAL
FOR
SELECT DATA FROM LDMDB_GMIH_DSO_A_STAGING_TEMP ORDER BY ROW_ID
OPEN CUR_JOB
IF ##ERROR <> 0
BEGIN
SET #ErrStmt= 'Error while Opening the Cursor'
GOTO ERRHDL
END
-- LOOPING THROUGH EACH RECORD.
FETCH NEXT FROM CUR_JOB INTO #DATA
IF ##ERROR <> 0
BEGIN
SET #ErrStmt= 'Error while Fetching the Record From Cursor'
GOTO ERRHDL
END
WHILE ##FETCH_STATUS = 0
BEGIN
SET #FLAG = 0
IF(SUBSTRING (#DATA,1,1)= '1')
BEGIN
SET #HDRDATA = #DATA
SET #FLAG = 0
/* Stock Integrity Changes for I0180 Starts */
IF (#Integritycheck = 1 )
BEGIN
SET #TrackingID = SUBSTRING(#data,19,20);
SET #Totalcount = CONVERT(INT,SUBSTRING(#data,39,10));
SET #Totalquantity = CONVERT(INT,SUBSTRING(#data,49,15));
END
/* Stock Integrity Changes for I0180 Ends */
END
IF(SUBSTRING (#DATA,1,1)= '5')
BEGIN
SET #FLAG = 1
/* Stock Integrity Changes for I0180 Starts */
IF (#Integritycheck = 1 )
BEGIN
SET #count= #count +1 ;
SET #quantity = #quantity + CONVERT(INT,SUBSTRING(#data,45,09)) ;
END
/* Stock Integrity Changes for I0180 Ends */
END
IF (#FLAG = 1)
BEGIN
INSERT INTO LDMDB_GMIH_DSO_A_STAGING
( FileCreationDate,
GenerationNumber,
WAREHOUSE_CODE,
ALLOC_RUN_DATE,
SUPP_CODE,
SUPP_LOC_CODE,
PICKSHEET_NUM,
UPC,
DELIVERY_POINT,
STORE_NUM,
VALUE_TOTAL_SEND,
COUNTRY_CODE,
DISPATCH_PRIORITY,
QTY,
ProcessDate
)
SELECT LTRIM(RTRIM(SUBSTRING(convert( varchar(20),(CONVERT(datetime, SUBSTRING (#HDRDATA,2,8), 111)),121),1,10))) FileCreationDate,
LTRIM(RTRIM(SUBSTRING (#HDRDATA,10,9))) GenerationNumber,
LTRIM(RTRIM(SUBSTRING (#DATA,2,3))) WAREHOUSE_CODE ,
LTRIM(RTRIM(SUBSTRING(convert( varchar(20),(CONVERT(datetime, SUBSTRING (#DATA,5,8), 111)),121),1,10))) ALLOC_RUN_DATE,
RIGHT(REPLICATE('0',10) + LTRIM(RTRIM(SUBSTRING (#DATA,13,4))),5) SUPP_CODE,
LTRIM(RTRIM(SUBSTRING (#DATA,17,5))) SUPP_LOC_CODE,
LTRIM(RTRIM(SUBSTRING (#DATA,22,6))) PICKSHEET_NUM,
LTRIM(RTRIM(SUBSTRING (#DATA,29,8))) UPC,
LTRIM(RTRIM(SUBSTRING (#DATA,37,4))) DELIVERY_POINT,
LTRIM(RTRIM(SUBSTRING (#DATA,41,4))) STORE_NUM,
(SELECT CASE WHEN cast(SUBSTRING (#DATA,45,9) AS NUMERIC) < 0 THEN ('-' + LTRIM(RTRIM(SUBSTRING (#DATA,46,8)))) ELSE LTRIM(RTRIM(SUBSTRING (#DATA,45,9))) END) VALUE_TOTAL_SEND,
LTRIM(RTRIM(SUBSTRING (#DATA,54,3))) COUNTRY_CODE,
LTRIM(RTRIM(SUBSTRING (#DATA,57,2))) DISPATCH_PRIORITY,
LTRIM(RTRIM(SUBSTRING (#DATA,59,9))) QTY,
#ProcessDate
IF ##ERROR <> 0
BEGIN
SET #ErrStmt= 'Error while Inserting the Records into the Staging Table'
GOTO ERRHDL
END
END
FETCH NEXT FROM CUR_JOB INTO #DATA
END
CLOSE CUR_JOB
DEALLOCATE CUR_JOB
IF (#Integritycheck = 0)
BEGIN
COMMIT TRANSACTION
END
FINISH:
BEGIN
/* Stock Integrity Changes for I0180 Starts */
IF (#Integritycheck = 0 )
BEGIN
SET #ErrStmt= 'Inserting the Records into the Staging Table Successful'
PRINT 'Inserting the Records into the Staging Table Successful'
Return
END
ELSE
BEGIN
IF ((#count = #Totalcount)AND(#quantity = #Totalquantity))
BEGIN
INSERT INTO dbo.LDMInterfaceIntegrity VALUES ('I0180','PIZ.HW.NDC.CALLOFF.FILE.LDM',#TrackingID,#Totalquantity,#Totalcount,#ProcessDate);
COMMIT TRANSACTION;
BEGIN
SET #ErrStmt= 'Inserting the Records into the Staging Table Successful';
Return
END
END
ELSE
BEGIN
ROLLBACK TRANSACTION;
IF (#count <> #Totalcount)
BEGIN
INSERT INTO dbo.LDMIntegrityErrorLog VALUES ('I0180','PIZ.HW.NDC.CALLOFF.FILE.LDM',#TrackingID,'Count Mismatch',#ProcessDate);
PRINT 'Count mismatch';
END
ELSE
BEGIN
INSERT INTO dbo.LDMIntegrityErrorLog VALUES ('I0180','PIZ.HW.NDC.CALLOFF.FILE.LDM',#TrackingID,'Quantity Mismatch',#ProcessDate);
PRINT 'Quantity Mismatch';
END
RETURN;
END
END
/* Stock Integrity Changes for I0180 Ends */
END
ERRHDL:
BEGIN
ROLLBACK TRANSACTION
RETURN
END
END
Use RAISERROR ... WITH NOWAIT as per this (among many others): http://www.mssqltips.com/sqlservertip/1660/using-the-nowait-option-with-the-sql-server-raiserror-statement/
At certain error levels, RAISERROR isn't actually considered an error, so it can be used as an alternative to PRINT.
Sql server management studio provide facility to debug the store procedure . by debugging the store procedure you can check value of each variable and which statement executed during execution of the store procedure .
just put the debug point in statement in store procedure and press debug .
Check http://www.youtube.com/watch?v=618LE_FZCxI
Related
I have an sql query which check for existence of some records, if those records exist rise error for them otherwise insert them to database. In my query as I need to return error messages for every record, I need to select some custom texts, problem is that they are showing as separate tables, not in one table, which I want (as I am calling this query from nodejs app and it returns an array for me so it only returns first table (error message) for me).
I searched and reach these two options:
1- Use UNION (which is not solving my case)
2- Insert all records in another table and then get all it's record (which isn't beautiful! :) )
DECLARE #errorCOUNT int
SET #errorCOUNT = 0
BEGIN TRANSACTION [Tran1]
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat1')
BEGIN
SELECT 'This is error for is = 4' As err
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat1')
END
----------------------------
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat2')
BEGIN
SELECT 'This is error for is = 5' AS err
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat2')
END
----------------------------
IF #errorCOUNT > 0
BEGIN
ROLLBACK TRANSACTION [Tran1]
END
ELSE
BEGIN
COMMIT TRANSACTION [Tran1]
END
As I mentioned I want all these select statements to be shown in one table so they return to my server as one array.
I just think it is good to mention that my query completes in a loop, so it may have different amount of IF...ELSE (between --- lines).
I hope I was clear. Thanks in advance.
Try this one, would work:
BEGIN TRANSACTION [Tran1]
DECLARE #err AS TABLE ( msg NVARCHAR(MAX) NOT NULL )
DECLARE #errorCOUNT AS INT = 0
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat1')
BEGIN
INSERT INTO #err (msg) VALUES ('This is error for is = 4')
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat1')
END
IF EXISTS (SELECT * FROM Categories WHERE CategoryName = 'myCat2')
BEGIN
INSERT INTO #err (msg) VALUES ('This is error for is = 5')
SET #errorCOUNT = #errorCOUNT + 1
END
ELSE
BEGIN
INSERT INTO Categories VALUES ('myCat2')
END
IF #errorCOUNT > 0
BEGIN
SELECT * FROM #err
ROLLBACK TRANSACTION [Tran1]
END
ELSE
BEGIN
COMMIT TRANSACTION [Tran1]
END
I don't understand what you're really want to do there, but here is a tip using MERGE statement and OUTPUT clause maybe it's what you're after
DECLARE #T TABLE(CategoryName VARCHAR(45));
MERGE INTO T
USING (VALUES('MyCat1'), ('MyCat2')) TT(CategoryName)
ON T.CategoryName = TT.CategoryName -- Or <> instead of =
WHEN NOT MATCHED THEN
INSERT VALUES(TT.CategoryName)
OUTPUT TT.CategoryName INTO #T;
SELECT CASE WHEN CategoryName = 'MyCat1'
THEN 'This is error for is = 4'
WHEN CategoryName = 'MyCat2'
THEN 'This is error for is = 5'
END Res
FROM #T;
Also, I don't think you need to the #ErrorCount variable, since you already have ##ROWCOUNT which you can use it instead.
Here is a db<>fiddle where you can see how it's working.
I've got a stored procedure that i'm having some issues with.
I'm trying to lookup against my table GOTWVotes and if VotedBy hasn't voted before write the vote to the table(this is working) however if VotedBy has voted before not to write to the table and return VoteCount as 1.
Although it doesn't write to the table when VotedBy exists the value of VoteCountalways appears to be 0
Any help would be appreciated
CREATE PROCEDURE [dbo].[Votes]
#VotedMember BIGINT,
#VotedBy BIGINT
AS
DECLARE #votecount INT
BEGIN TRY
BEGIN TRANSACTION t_Transaction
SELECT TOP 1 * FROM [GOTWVotes] WITH (TABLOCKX)
SELECT #votecount = COUNT(*) FROM [dbo].[GOTWVotes]
WHERE [VotedBy] = #VotedBy
IF #votecount = 0
INSERT INTO
[dbo].[GOTWVotes] ([VotedMember],[VotedBy])
VALUES
(#VotedMember, #VotedBy)
COMMIT TRANSACTION t_Transaction
END TRY
BEGIN CATCH
SET #votecount = -1
ROLLBACK TRANSACTION t_Transaction
END CATCH
RETURN #votecount
You can do following code
CREATE PROCEDURE [dbo].[Votes]
#VotedMember BIGINT,
#VotedBy BIGINT,
#votecount INT OUTPUT
AS
BEGIN TRY
BEGIN TRANSACTION t_Transaction
SET #votecount = 0
IF NOT EXISTS(SELECT 1 FROM [dbo].[GOTWVotes]
WHERE [VotedBy] = #VotedBy)
BEGIN
INSERT INTO
[dbo].[GOTWVotes] ([VotedMember],[VotedBy])
VALUES
(#VotedMember, #VotedBy)
END
ELSE
BEGIN
SELECT #votecount = COUNT(*) FROM [dbo].[GOTWVotes]
WHERE [VotedBy] = #VotedBy
END
COMMIT TRANSACTION t_Transaction
END TRY
BEGIN CATCH
SET #votecount = -1
ROLLBACK TRANSACTION t_Transaction
END CATCH
To Call above Stored procedure you need to write following code
DECLARE #votecount int
EXEC dbo.Votes #VotedMember = 0, -- bigint
#VotedBy = 0, -- bigint
#votecount = #votecount OUTPUT -- int
SELECT #votecount
This seems so complicated. I am thinking something like this:
CREATE PROCEDURE [dbo].[Votes] (
#VotedMember BIGINT,
#VotedBy BIGINT,
#retval int output
)
BEGIN
INSERT INTO dbo.GOTWVotes (VotedMember, VotedBy)
SELECT v.VotedMember, v.VotedBy
FROM (VALUES (#VotedMember, #VotedBy)) v(VotedMember, VotedBy)
WHERE NOT EXISTS (SELECT 1
FROM dbo.GOTWVotes gwv
WHERE gwv.VotedBy = v.VotedBy
);
SET #retval = ##ROWCOUNT;
END;
I suspect that what you want to do could also be handled with constraints.
my supervisor asked me to not put transactions and commit etc in this code because he says that it's useless to put transactions in this procedure. He's well experienced and i can't directly argue with him so need your views on it ?
ALTER PROCEDURE [Employee].[usp_InsertEmployeeAdvances](
#AdvanceID BIGINT,
#Employee_ID INT,
#AdvanceDate DATETIME,
#Amount MONEY,
#MonthlyDeduction MONEY,
#Balance MONEY,
#SYSTEMUSER_ID INT,
#EntryDateTime DATETIME = NULL,
#ProcedureType SMALLINT)
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION [Trans1]
IF EXISTS
(
SELECT *
FROM Employee.Advance
WHERE AdvanceID = #AdvanceID
)
BEGIN
--UPDATION OF THE RECORD
IF #ProcedureType = 1
BEGIN
SET #Amount = #Amount * -1;
END
UPDATE Employee.Advance
SET
Employee_ID = #Employee_ID,
AdvanceDate = #AdvanceDate,
Amount = #Amount,
MonthlyDeduction = #MonthlyDeduction,
Balance = #Balance,
EntryDateTime = GETDATE()
WHERE AdvanceID = #AdvanceID
END
ELSE
BEGIN
DECLARE #LastRecordID INT
DECLARE #LastBalance MONEY
SET #LastRecordID =
(
SELECT MAX(EA.AdvanceID)
FROM Employee.Advance EA
WHERE EA.Employee_ID = #Employee_ID
)
SET #LastBalance =
(
SELECT EA.Balance
FROM Employee.Advance EA
WHERE EA.AdvanceID = ISNULL(#LastRecordID, 0)
)
IF(#ProcedureType = 0) --Advances
BEGIN
SET #Balance = ISNULL(#LastBalance, 0) + #Amount
INSERT INTO Employee.Advance
(Employee_ID,
AdvanceDate,
Amount,
MonthlyDeduction,
Balance,
User_ID,
EntryDateTime
)
VALUES
(#Employee_ID,
#AdvanceDate,
#Amount,
#MonthlyDeduction,
#Balance,
#SYSTEMUSER_ID,
GETDATE())
END
ELSE --Receivings
BEGIN
IF NOT EXISTS
(
SELECT *
FROM Employee.Advance EA
WHERE EA.Employee_ID = #Employee_ID
AND EA.Balance > 0
AND EA.AdvanceID =
(
SELECT MAX(AdvanceID)
FROM Advance
WHERE Employee_ID = #Employee_ID
)
)
BEGIN
RAISERROR('This Employee has no advances history', 16, 1)
RETURN
--Select 0
END
ELSE
BEGIN
SET #Balance = ISNULL(#LastBalance, 0) - #Amount
INSERT INTO Employee.Advance
(Employee_ID,
AdvanceDate,
Amount,
MonthlyDeduction,
Balance,
User_ID,
EntryDateTime
)
VALUES
(#Employee_ID,
#AdvanceDate,
-1 * #Amount,
#MonthlyDeduction,
#Balance,
#SYSTEMUSER_ID,
GETDATE())
END
END
END
COMMIT TRANSACTION [Trans1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Trans1]
END CATCH
END
ALTER PROCEDURE [Employee].[usp_InsertEmployeeAdvances]
(
#AdvanceID BIGINT,
#Employee_ID INT,
#AdvanceDate DATETIME,
#Amount MONEY,
#MonthlyDeduction MONEY,
#Balance MONEY,
#SYSTEMUSER_ID INT,
#EntryDateTime DATETIME = NULL,
#ProcedureType SMALLINT
)
AS BEGIN
SET NOCOUNT ON
IF EXISTS (
SELECT 1
FROM Employee.Advance
WHERE AdvanceID = #AdvanceID
)
BEGIN
UPDATE Employee.Advance
SET
Employee_ID = #Employee_ID,
AdvanceDate = #AdvanceDate,
Amount = CASE WHEN #ProcedureType = 1 THEN -#Amount ELSE #Amount END,
MonthlyDeduction = #MonthlyDeduction,
Balance = #Balance,
EntryDateTime = GETDATE()
WHERE AdvanceID = #AdvanceID
END
ELSE BEGIN
DECLARE
#LastRecordID INT
, #LastBalance MONEY
, #IsBalance BIT
SELECT #LastRecordID = MAX(AdvanceID)
FROM Employee.Advance
WHERE Employee_ID = #Employee_ID
SELECT
#LastBalance = Balance,
#IsBalance = CASE WHEN Balance > 0 THEN 1 ELSE 0 END
FROM Employee.Advance
WHERE AdvanceID = ISNULL(#LastRecordID, 0)
IF ISNULL(#IsBalance, 0) = 0 BEGIN
RAISERROR('This Employee has no advances history', 16, 1)
RETURN
END
ELSE BEGIN
INSERT INTO Employee.Advance(Employee_ID, AdvanceDate, Amount, MonthlyDeduction, Balance, [User_ID], EntryDateTime)
SELECT
#Employee_ID,
#AdvanceDate,
CASE WHEN #ProcedureType = 0 THEN #Amount ELSE -#Amount END,
#MonthlyDeduction,
ISNULL(#LastBalance, 0) + CASE WHEN #ProcedureType = 0 THEN #Amount ELSE -#Amount END,
#SYSTEMUSER_ID,
GETDATE()
END
END
END
This is a changed answer as I did not read the whole question
Without a transaction if this was called at the same time and record did not exists then both could insert and one would likely get #Balance wrong
yes a transaction serves a purpose
I'd never put transactions in a particular bit of work simply because you never know when it is going to be incorporated into another. Only the caller can know that. To use the well worn example, you might think that creating an order and adding items should be a transaction which is fair enough. Down the track some calling function might want to include the results of a credit check or account creation as part of it. So basically at the low level you cannot know* if you're in the context of a transaction or not so there's not much point in being strict about committing. Similarly, rolling back is a bit wrong - you never know how fatal a specific error is in the context of the caller, so just throw the exception and let the client decide how to manage it.
*well you can, but it's often easier not to care.
As the stored procedure is written right now a transaction isn't going to add anything. Only one of the modifications will ever run and if it fails then it will roll itself back anyway. Also of importance to note is that nothing happens after the modifications. If there were some additional code after one of the updates then that would likely necessitate a transaction.
However, what is the cost of having that transaction? Do you expect that this code will never change? A change in the future might make a transaction necessary without it being obvious. Given the nature of your stored procedure I'm finding it hard to believe that there's a downside to the transaction being there.
To handle RAISERROR inside of your TRY..CATCH block:
BEGIN TRANSACTION
BEGIN TRY
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
ROLLBACK TRANSACTION
EXEC dbo.LogErrorAndRethrow
END CATCH
The code for LogErrorAndRethrow then looks like:
CREATE PROCEDURE dbo.LogErrorAndRethrow
#LogError BIT = 1
AS
BEGIN
DECLARE
#error_number INT,
#error_message NVARCHAR(MAX),
#error_severity INT,
#error_state INT,
#error_procedure NVARCHAR(200),
#error_line INT
SELECT
#error_number = ERROR_NUMBER(),
#error_message = ERROR_MESSAGE(),
#error_severity = ERROR_SEVERITY(),
#error_state = ERROR_STATE(),
#error_procedure = COALESCE(ERROR_PROCEDURE(), '<Unknown>'),
#error_line = ERROR_LINE()
IF (#LogError = 1)
BEGIN
EXEC dbo.InsertAuditLog
#UserID = 0,
#AuditType = 'Error',
#ModuleName = #error_procedure,
#Message = #error_message
END
-- Rebuild the error message with parameters so that all information appears in the RAISERROR
SELECT #error_message = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + ERROR_MESSAGE()
RAISERROR(#error_message, #error_severity, 1, #error_number, #error_severity, #error_state, #error_procedure, #error_line)
END
InsertAuditLog basically just inserts into a log table.
Is this a good way to add TRANSACTION to my code. I have to first update my code and if it fails then insert should also not work. Have a look please if it's a correct way or not. If not then please improve.
Begin Transaction[Transaction1]
Begin Try
IF(#ServiceInfoToJobStatus = 1)
Update ServiceInfo
Set ServiceInfoToJobStatus= 0 --To set all current jobs to prior because one person cannot have many jobs selected as current
Where #ServiceInfoToJobStatus = 1 AND ServiceInfo.fk_PersonalInfo_ServiceInfo_PID= #fk_PersonalInfo_ServiceInfo_PID
Set #ServiceInfoEntryDateTime= (Select GetDate())
Insert into dbo.ServiceInfo
(
ServiceInfoInitialDesignation,
ServiceInfoInitialBPS,
fk_Districts_ServiceInfo_InitialDistrictID,
ServiceInfoJobStatus,
ServiceInfoFromDate,
ServiceInfoDepartment,
fk_PersonalInfo_ServiceInfo_PID,
ServiceInfoServiceType ,
ServiceInfoOffice ,
ServiceInfoCadre ,
fk_WebUsers_ServiceInfo_UserID,
ServiceInfoEntryDateTime,
ServiceInfoToDesignation ,
ServiceInfoToBPS ,
fk_Districts_ServiceInfo_ToDistrictID ,
ServiceInfoToJobStatus ,
ServiceInfoToDate ,
ServiceInfoToDepartment ,
ServiceInfoToServiceType ,
ServiceInfoToOffice ,
ServiceInfoToCadre
)
Values
(
#ServiceInfoInitialDesignation,
#ServiceInfoInitialBPS,
#fk_Districts_ServiceInfo_InitialDistrictID,
#ServiceInfoJobStatus,
#ServiceInfoFromDate,
#ServiceInfoDepartment,
#fk_PersonalInfo_ServiceInfo_PID,
#ServiceInfoServiceType ,
#ServiceInfoOffice ,
#ServiceInfoCadre ,
#fk_WebUsers_ServiceInfo_UserID,
Convert(varchar, #ServiceInfoEntryDateTime, 113),
#ServiceInfoToDesignation ,
#ServiceInfoToBPS ,
#fk_Districts_ServiceInfo_ToDistrictID ,
#ServiceInfoToJobStatus ,
#ServiceInfoToDate ,
#ServiceInfoToDepartment ,
#ServiceInfoToServiceType ,
#ServiceInfoToOffice ,
#ServiceInfoToCadre
)
Set #ReturnStatus = 1
Commit Transaction[Transaction1]
End Try
Begin Catch
ROLLBACK Transaction[Transaction1]
Set #ReturnStatus= 0
Set #ReturnStatusMessage= (Select ERROR_MESSAGE())
End Catch
It is recommended to use add transaction (try/catch block) if the code updates or inserts data in to the table. In your case the answer is pretty yes. But in your transaction block , there is no way to recognize whether the data insert / update completed.
I would use following syntax with ##error added.
BEGIN TRY
BEGIN TRAN
//YOUR CODE
IF ##ERROR = 0
BEGIN
COMMIT TRAN;
END
END TRY
BEGIN CATCH
SELECT ##ERROR AS ERROR
ROLLBACK TRAN;
END CATCH
For me it is unclear what update fails means (error or no rows updated), but it does not matter much, as you can test for both pretty easy:
DECLARE #UpdateErrorNo INT
DECLARE #UpdateCount INT
DECLARE #InsertErrorNo INT
Begin Transaction[Transaction1]
Begin Try
IF(#ServiceInfoToJobStatus = 1)
Update ServiceInfo
Set ServiceInfoToJobStatus= 0 --To set all current jobs to prior because one person cannot have many jobs selected as current
Where #ServiceInfoToJobStatus = 1 AND ServiceInfo.fk_PersonalInfo_ServiceInfo_PID= #fk_PersonalInfo_ServiceInfo_PID
SET #UpdateErrorNo = ##ERROR
SET #UpdateCount = ##ROWCOUNT
Set #ServiceInfoEntryDateTime= (Select GetDate())
Insert into dbo.ServiceInfo
(
-- skipped for readability
)
Values
(
-- skipped for readability
)
SET #InsertErrorNo = ##ERROR
Set #ReturnStatus = 1
Commit Transaction[Transaction1]
End Try
Begin Catch
ROLLBACK Transaction[Transaction1]
Set #ReturnStatus= 0
Set #ReturnStatusMessage= (Select ERROR_MESSAGE())
End Catch
-- here you can read `#UpdateErrorNo`, `#InsertErrorNo` to check for errors
The tricky part is that ##ERROR is set after each statement, so if you have a case like this (no try/catch):
BEGIN TRAN
INSERT ... -- fails
UPDATE ... -- success
-- ##ERROR will not show that it is a failure
COMMIT
it might lead to unexpected results.
declare #cdt datetime = getdate()
begin try
begin transaction
Update dbo.ServiceInfo
Set ServiceInfoToJobStatus= 0 --To set all current jobs to prior because one person cannot have many jobs selected as current
Where #ServiceInfoToJobStatus = 1
AND fk_PersonalInfo_ServiceInfo_PID= #fk_PersonalInfo_ServiceInfo_PID
Insert into dbo.ServiceInfo
(
ServiceInfoInitialDesignation,
ServiceInfoInitialBPS,
fk_Districts_ServiceInfo_InitialDistrictID,
ServiceInfoJobStatus,
ServiceInfoFromDate,
ServiceInfoDepartment,
fk_PersonalInfo_ServiceInfo_PID,
ServiceInfoServiceType ,
ServiceInfoOffice ,
ServiceInfoCadre ,
fk_WebUsers_ServiceInfo_UserID,
ServiceInfoEntryDateTime,
ServiceInfoToDesignation ,
ServiceInfoToBPS ,
fk_Districts_ServiceInfo_ToDistrictID ,
ServiceInfoToJobStatus ,
ServiceInfoToDate ,
ServiceInfoToDepartment ,
ServiceInfoToServiceType ,
ServiceInfoToOffice ,
ServiceInfoToCadre
)
Values
(
#ServiceInfoInitialDesignation,
#ServiceInfoInitialBPS,
#fk_Districts_ServiceInfo_InitialDistrictID,
#ServiceInfoJobStatus,
#ServiceInfoFromDate,
#ServiceInfoDepartment,
#fk_PersonalInfo_ServiceInfo_PID,
#ServiceInfoServiceType ,
#ServiceInfoOffice ,
#ServiceInfoCadre ,
#fk_WebUsers_ServiceInfo_UserID,
Convert(varchar, #cdt, 113),
#ServiceInfoToDesignation ,
#ServiceInfoToBPS ,
#fk_Districts_ServiceInfo_ToDistrictID ,
#ServiceInfoToJobStatus ,
#ServiceInfoToDate ,
#ServiceInfoToDepartment ,
#ServiceInfoToServiceType ,
#ServiceInfoToOffice ,
#ServiceInfoToCadre
)
return ...
commit transaction
end try
begin catch
if(##TRANCOUNT > 0)
rollback transaction
return ...
end catch
That's what I use:
Begin Try
Begin Tran
-- Do your thing... some DML statements
Commit Tran
End Try
Begin Catch
If ( ##TranCount > 0 )
Rollback Tran
End Catch
I'm using SQL Server 2008. In my stored procedure I can't rollback transaction
on ELSE condition in WHILE.
Error:
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
How to rollback transaction while using cursor?
There are my stored procedure:
BEGIN TRANSACTION
IF ##ERROR=0
BEGIN
SET NOCOUNT ON
DECLARE #pjamount float,
#pjdatej date,
#pjaccap varchar(10),
#acode varchar(13),
#btot float,
#cloc varchar(10),
#dqty int,
#qtyorder int,
#qtypurch int
SELECT
#pjdatej = (DATEADD(dd, Supplier_M_Limit, Purchasing_M_Date)),
#pjaccap = Supplier_M_Acc
FROM
tblPurchasing_M
JOIN
tblPurchOrder_M ON tblPurchasing_M.PurchOrder_M_ID = tblPurchOrder_M.PurchOrder_M_ID
JOIN
tblSupplier_M ON tblPurchOrder_M.Supplier_M_ID = tblSupplier_M.Supplier_M_ID
WHERE
Purchasing_M_ID = #pjid
SET #pjamount = (SELECT SUM(Purchasing_D_Price * Purchasing_D_Qty)
FROM tblPurchasing_D
WHERE Purchasing_M_ID = #pjid)
INSERT INTO tblRepAPHistory (Depo_M_ID,
RepAPHistory_Trans,
RepAPHistory_Amount,
RepAPHistory_Date,
RepAPHistory_DateJ,
RepAPHistory_DateL,
RepAPHistory_Stats,
SetCategory_ID)
VALUES (#depoid, #pjcode, #pjamount, #pjdate, #pjdatej, NULL, 0, #cateid)
INSERT INTO tblRepTransactions (Depo_M_ID,
RepTransactions_Code,
RepTransactions_Acc,
RepTransactions_AmountD,
RepTransactions_AmountK,
RepTransactions_Desc, SetCategory_ID)
VALUES (#depoid, #pjcode, #pjaccap, '0', #pjamount,
'AP Pembelian '+#pjcode, #cateid)
IF (CURSOR_STATUS('Global','approvepj'))>0
BEGIN
SET NOCOUNT ON
CLOSE approvepj
DEALLOCATE approvepj
END
DECLARE approvepj CURSOR
FOR
SELECT Purchasing_D_Inv AS A,
(Purchasing_D_Qty*Purchasing_D_Price) AS B,
Purchasing_D_Loc AS C, Purchasing_D_Qty AS D
FROM tblPurchasing_D
WHERE Purchasing_M_ID = #pjid
OPEN approvepj
FETCH NEXT FROM approvepj
INTO #acode, #btot, #cloc, #dqty
WHILE ##FETCH_STATUS=0
BEGIN
SET NOCOUNT ON
SET #qtypurch = (SELECT SUM(Purchasing_D_Qty)
FROM tblPurchasing_D
WHERE purchasing_M_Id = #pjid
AND Purchasing_D_Inv=#acode
GROUP BY Purchasing_D_Inv)
SET #qtyorder = (SELECT SUM(purchorder_D_qty)
FROM tblpurchorder_D
WHERE PurchOrder_M_ID = #pjpo
AND PurchOrder_D_Inv=#acode
group by PurchOrder_D_Inv)
IF #qtypurch <= #qtyorder
BEGIN
INSERT INTO tblRepTransactions (Depo_M_ID,
RepTransactions_Code,
RepTransactions_Acc,
RepTransactions_AmountD,
RepTransactions_AmountK,
RepTransactions_Desc,
SetCategory_ID)
VALUES (#depoid, #pjcode, #pjaccinv, #btot, '0',
'Detil Pembelian: '+#pjcode+' - '+#acode ,
#cateid)
INSERT INTO tblRepInvHistory (Depo_M_ID,
RepInvHistory_Trans,
RepInvHistory_Inv,
RepInvHistory_Hist,
RepInvHistory_Date,
RepInvHistory_Loc,
SetCategory_ID)
VALUES (#depoid, #pjcode, #acode, #dqty,
#pjdate, #cloc, #cateid)
IF ##ERROR<>0
BEGIN
SET NOCOUNT ON
ROLLBACK TRANSACTION
RETURN
END
FETCH NEXT FROM approvepj
INTO #acode, #btot, #cloc, #dqty
END
ELSE
BEGIN
SET NOCOUNT ON
ROLLBACK TRANSACTION
END
END
CLOSE approvepj
DEALLOCATE approvepj
END
ELSE
BEGIN
SET NOCOUNT ON
ROLLBACK TRANSACTION
END
COMMIT TRANSACTION