Can someone please advise why the first transaction fails and the second succeeds, I've been stuck on this for a while now
GO
DECLARE #errnum AS int;
BEGIN TRAN;
SET IDENTITY_INSERT Production.Products ON;
INSERT INTO Production.Products(productid, productname, supplierid,
categoryid, unitprice, discontinued)
VALUES(1, N'Test1: Ok categoryid', 1, 1, 18.00, 0);
SET #errnum = ##ERROR;
IF #errnum <> 0
BEGIN
IF ##TRANCOUNT > 0 ROLLBACK TRAN;
PRINT 'Insert #1 into Production.Products failed with error ' +
CAST(#errnum AS VARCHAR);
END;
INSERT INTO Production.Products(productid, productname, supplierid,
categoryid,unitprice, discontinued)
VALUES(101, N'Test2: Bad categoryid', 1, 1, 18.00, 0);
SET #errnum = ##ERROR;
IF #errnum <> 0
BEGIN
IF ##TRANCOUNT > 0 ROLLBACK TRAN;
PRINT 'Insert #2 into Production.Products failed with error ' +
CAST(#errnum AS VARCHAR);
END;
SET IDENTITY_INSERT Production.Products OFF;
Related
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
I want to know if both stored procedures below could perform well to do the same thing or if there is any major difference? I have tested them and both are working, but I don't know which one I should use.
I want to select just one of the procedures, if both would perform just fine, that's alright, I want to understand what major different could arise if they are indeed different.
I also want to ensure that the transaction will be rolled back if there is an error in the procedure.
Thanks!
CREATE PROCEDURE spDeleteAnInactiveEmployee
#TrainerID int,
#EActive char (1)
AS
BEGIN
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN
IF (SELECT COUNT(*) FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID) = 0
RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)
IF EXISTS (SELECT * FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID AND EActive = 'Y')
RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)
DELETE FROM [EmployeeDetails]
WHERE TrainerID = #TrainerID AND EActive = 'N'
COMMIT TRAN
PRINT 'Employee ID' + CAST (#TrainerID AS VARCHAR) + ' was successfully deleted.'
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH;
END
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC spDeleteAnInactiveEmployee
#TrainerID int,
#EActive char (1)
AS
BEGIN TRY
BEGIN TRAN
IF (SELECT COUNT(*) FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID) = 0
RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)
IF EXISTS (SELECT * FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID AND EActive = 'Y')
RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)
DELETE FROM [EmployeeDetails]
WHERE TrainerID = #TrainerID AND EActive = 'N'
COMMIT TRAN
BEGIN
PRINT 'Employee ID' + CAST (#TrainerID AS VARCHAR) + ' was successfully deleted.'
END
END TRY
BEGIN CATCH
ROLLBACK
PRINT 'An error has occurred and the transaction was not committed'
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_STATE() AS ErrorState,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO
I just want to check if the place where I put the ##Error and Begin/Commit tran is correct?
I am unsure if I should you the Begin Tran over the DELETE statement instead? And does the ##ERROR make any sense at all?
Thanks!
CREATE PROCEDURE spDeleteAnInactiveEmployee
#TrainerID int,
#EActive char (1)
AS
BEGIN TRY
BEGIN TRAN
IF (SELECT COUNT(*) FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID) = 0
RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)
IF EXISTS (SELECT * FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID AND EActive = 'Y')
RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)
DELETE FROM [EmployeeDetails]
WHERE TrainerID = #TrainerID AND EActive = 'N'
IF ##ERROR = 0
COMMIT TRAN
BEGIN
PRINT 'Employee ID' + CAST (#TrainerID AS VARCHAR) + ' was successfully deleted.'
END
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_STATE() AS ErrorState,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage;
IF (XACT_STATE()) = -1
BEGIN
PRINT 'Transaction was not committed'
ROLLBACK TRANSACTION;
END;
IF (XACT_STATE()) = 1
BEGIN
PRINT 'Transaction was committed'
COMMIT TRANSACTION;
END;
END CATCH;
GO
##ERROR is unnecessary when you use TRY/CATCH. Before TRY/CATCH you had to check ##ERROR after each statement that might fail and use GOTO to force control flow to an error label.
So this should be something like:
CREATE PROCEDURE spDeleteAnInactiveEmployee
#TrainerID int,
#EActive char (1)
AS
BEGIN
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRAN
IF (SELECT COUNT(*) FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID) = 0
RAISERROR ('Trainer details were not deleted. Trainer ID does not exist.', 16, 1)
IF EXISTS (SELECT * FROM EmployeeDetails ed
WHERE TrainerID = #TrainerID AND EActive = 'Y')
RAISERROR ('Trainer details were not deleted. Trainer is still active.', 16, 1)
DELETE FROM [EmployeeDetails]
WHERE TrainerID = #TrainerID AND EActive = 'N'
COMMIT TRAN
PRINT 'Employee ID' + CAST (#TrainerID AS VARCHAR) + ' was successfully deleted.'
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH;
END
I wrote a SQL procedure that inserts data into two tables. I started with begin transaction but some data violate constraint of second table and in the end data are added into first table, but not in second. What is wrong with my code?
create procedure [dbo].[procAddConference]
#Conf_name varchar(50),
#Begin_date date,
#End_date date,
#Price money,
#Student_disc numeric(3,2),
#Limit int,
#Disc numeric(3,2),
#Until int
as
set nocount on
begin transaction;
begin try
IF (#Begin_date > #End_date or #Limit < 0 or #Disc <0 or #Disc >1 or #Student_disc < 0 or #Student_disc > 1)
BEGIN
; throw 52000, 'Wrong data', 1
END
insert into Conferences (ConferenceName, BeginDate, EndDate, Price, StudentDiscount)
values (#Conf_name, #Begin_date, #End_date, #Price, #Student_disc)
IF ##ERROR <> 0
begin
RAISERROR('Error, transaction not completed!',16,-1)
rollback transaction;
end
declare #confID INT
set #confID = ##IDENTITY
declare #duration int
declare #iterator int
set #duration = DATEDIFF(dd, #Begin_date, #End_date)
set #iterator = 0
while #iterator <= #duration
begin
insert into ConferenceDay (ConferenceID, Date, Limit)
values (#confID, cast(DATEADD(dd, #iterator, #Begin_date) as date), #Limit)
IF ##ERROR <> 0 or ##ROWCOUNT = 0
begin
rollback transaction;
RAISERROR('Error, transaction not completed!',16,-1)
end
set #iterator = #iterator + 1
end
commit transaction;
END TRY
BEGIN CATCH
DECLARE #errorMsg nvarchar (2048) = 'Cannot add conference . Error message : ' + ERROR_MESSAGE () ;
; THROW 52000 , #errorMsg ,1
rollback transaction;
END CATCH
END
How can you improve this without using a cursor? I was thinking using table variable would help, but am I going right direction doing so? I have omitted cursor and try to do it with table variable. Please help, here is the code.
IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'[dbo].[BatchProcessBridge_CustomerEvent]') AND [xtype] IN (N'P'))
BEGIN
DROP PROCEDURE [dbo].[BatchProcessBridge_CustomerEvent]
END
GO
CREATE PROCEDURE [dbo].[BatchProcessBridge_CustomerEvent]
(
#BatchJobTable Bridge_CustomerEventBatchJobTable READONLY,
#Name VARCHAR(50)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Lsn BINARY(10),
DECLARE #SeqVal BINARY(10),
DECLARE #Action VARCHAR(300),
DECLARE #CustId VARCHAR(MAX)
--using tabel variable. Cursor gives bad performance.
DECLARE #TEMP_TABLE TABLE ( [Lsn] BINARY(10), [SeqVal] BINARY(10), [Action] VARCHAR(300), [CustId] VARCHAR(MAX))
INSERT INTO #TEMP_TABLE
SELECT Lsn, SeqVal, [Action], [CustId] FROM #BatchJobTable
--DECLARE GetBatchJobCursor CURSOR FAST_FORWARD
--FOR
--SELECT Lsn, SeqVal, [Action], [CustId] FROM #BatchJobTable
--OPEN GetBatchJobCursor
--FETCH NEXT FROM GetBatchJobCursor INTO #Lsn, #SeqVal, #Action, #CustId
--WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
BEGIN TRANSACTION
IF (#Action = 'create')
BEGIN
-- Create.
INSERT INTO [Bridge_Customer]
(
[CustId]
,[PersonId]
,[DisplayName]
,[CreatedDate]
,[ModifiedDate]
)
SELECT
[CustId]
,[PersonId]
,[DisplayName]
,[CreatedDate]
,[ModifiedDate]
FROM
#BatchJobTable
WHERE
(Lsn = #Lsn) AND (SeqVal = #SeqVal)
END
ELSE IF (#Action = 'update')
BEGIN
-- Update.
UPDATE [Target]
SET
[Target].[CustId] = [Ref].[CustId]
,[Target].[PersonId] = [Ref].[PersonId]
,[Target].[DisplayName] = [Ref].[DisplayName]
,[Target].[CreatedDate] = [Ref].[CreatedDate]
,[Target].[ModifiedDate] = [Ref].[ModifiedDate]
FROM
[dbo].[Bridge_Customer] AS [Target]
INNER JOIN
(SELECT * FROM #BatchJobTable WHERE (Lsn = #Lsn) AND (SeqVal = #SeqVal)) AS [Ref]
ON
([Target].[CustId] = [Ref].[CustId])
END
ELSE IF (#Action = 'delete')
BEGIN
DELETE FROM [dbo].[Bridge_Customer] WHERE [CustId] = #CustId
END
-- Update last processed event.
EXEC [dbo].[UpdateLastProcessedEvent]
#Name = #Name,
#Lsn = #Lsn,
#SeqVal = #SeqVal
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
-- Use RAISERROR inside the CATCH block to return error
-- information about the original error that caused
-- execution to jump to the CATCH block.
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
ROLLBACK TRANSACTION
END CATCH
--FETCH NEXT FROM GetBatchJobCursor INTO #Lsn, #SeqVal, #Action, #CustId
END
--CLOSE GetBatchJobCursor
--DEALLOCATE GetBatchJobCursor
END
GO
A cursor is not necessary here; this is just basic SQL. Forgive any mistakes with my code as you haven't provided any DLL but I'm pretty sure you can just do this:
IF (#Action = 'create')
INSERT INTO Bridge_Customer
(
CustId
,PersonId
,DisplayName
,CreatedDate
,ModifiedDate
)
SELECT
CustId
,PersonId
,DisplayName
,CreatedDate
,ModifiedDate
FROM #BatchJobTable
ELSE IF (#Action = 'update')
UPDATE tgt
SET tgt.CustId = Ref.CustId
,tgt.PersonId = Ref.PersonId
,tgt.DisplayName = Ref.DisplayName
,tgt.CreatedDate = Ref.CreatedDate
,tgt.ModifiedDate = Ref.ModifiedDate
FROM dbo.Bridge_Customer AS tgt
INNER JOIN #BatchJobTable AS ref
ON (tgt.CustId = Ref.CustId)
ELSE IF (#Action = 'delete')
DELETE FROM dbo.Bridge_Customer
WHERE CustId IN (SELECT CustId FROM #BatchJobTable);
Personally, I would split this into three stored and call whichever one from the application layer. What you are doing is known as a "Catch All query" which is fine but, if you must go that route, read this: Catch All Queries (Gail Shaw)