Transaction not working in SQL Server 2017 - sql

I have a transaction to check if an entry in my table has the same date and subHistoryID. Regardless if the data matches or not the transaction always rolls back. Any help on this matter would be great.
BEGIN TRANSACTION [Tran1]
INSERT INTO History(Name, subHistoryID, GarageID, StaffID, Date, Notes)
VALUES (#name, #subhistoryid, #garageid, #staffid, #date, #notes);
IF EXISTS (SELECT * FROM History
WHERE History.Date = #date AND History.subHistoryID = #subhistoryid)
BEGIN
ROLLBACK TRANSACTION [Tran1]
PRINT 'ERROR: Rolling back transaction.'
END
ELSE
BEGIN
PRINT 'PASS: Committing transaction.'
COMMIT TRANSACTION [Tran1]
END
GO
My data before attempting the transaction.
DECLARE #name varchar(20) = NULL;
DECLARE #subhistoryid int = 1;
DECLARE #garageid int = 1;
DECLARE #staffid int = 1;
DECLARE #date Date = '2018-10-21';
DECLARE #notes varchar(100) = 'test';
This data should roll back, which is does.
DECLARE #name varchar(20) = NULL;
DECLARE #subhistoryid int = 1;
DECLARE #garageid int = 1;
DECLARE #staffid int = 1;
DECLARE #date Date = '2016-1-1';
DECLARE #notes varchar(100) = 'test';
This data should commit, but it rolls back

Related

Entity Framework can't revert a transaction, how to declare transactions inside a Procedure?

Having a table like this:
CREATE TABLE tmpDelete
(
ID INT IDENTITY(1, 1),
Value DATE
)
GO
Then I declare my procedure like this:
CREATE PROCEDURE [TestSP]
#prmDate DATE
AS
BEGIN
--PRINT '##TRANCOUNT : ' + CAST(##TRANCOUNT AS NVARCHAR(10))
BEGIN TRANSACTION TestTransaction
BEGIN TRY
--PRINT '##TRANCOUNT : ' + CAST(##TRANCOUNT AS NVARCHAR(10))
INSERT INTO dbo.tmpDelete
( Value )
VALUES ( 'abc' -- Value - date
);
COMMIT TRANSACTION TestTransaction
--PRINT '##TRANCOUNT : ' + CAST(##TRANCOUNT AS NVARCHAR(10))
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION TestTransaction
DECLARE #NVERRO_MESS NVARCHAR(4000);
DECLARE #INERR_SEVE INT, #INER_STAT INT;
SELECT #NVERRO_MESS = ERROR_MESSAGE(), #INERR_SEVE = ERROR_SEVERITY(), #INER_STAT = ERROR_STATE();
RAISERROR (#NVERRO_MESS, #INERR_SEVE, #INER_STAT)
--PRINT '##TRANCOUNT : ' + CAST(##TRANCOUNT AS NVARCHAR(10))
RETURN
END CATCH
END
GO
And when I call it from Entity Framework it returns the following error message:
Cannot roll back TestTransaction. No transaction or savepoint of that
name was found. Transaction count after EXECUTE indicates that a
COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count =
%1, current count = %2.
How should I declare my transactions?

How to avoid concurrent read access issue in SQL Server?

I am getting a date value from a tblinfo table. If the date is null, then I will insert into table, else I will update the row in the table.
I am facing the issue that 2 sessions accessing the table simultaneously with same information, both are getting Null, so both are trying to insert records into same table with same primary key value. We are getting the primary key constraints issue. How to stop this?
begin tran
set #date=(select date from tblinfo where id=#id)
if(#date is null)
--insert
else
--update
commit tran
Using Locking
declare #Date DateTime
, #RowId bigint = 10
Begin Tran
select #Date = SomeDate
from tblInfo
with (RowLock) --this is the important point; during the transaction, this row will be locked, so other statements won't be able to read it
where Id = #RowId
if (#Date is Null)
begin
insert SomeTable (TblInfoId, Date)
values (#RowId, GetUtcDate())
end
else
begin
update SomeTable
set Date = #Date
where tblInfoId = #RowId
end
if ##TranCount > 0 Commit Tran
Technique Avoiding Locking
If preventing reads from occurring is an issue for some reason, the below is a lockless approach; but requires an extra field in your database.
declare #Date DateTime
, #RowId bigint = 10
, #DataAvailable bit
Begin Tran
update tblInfo
set session_lock = ##SPID
where Id = #RowId
and session_lock is null
select #Date = SomeDate
, #DataAvailable = 1
from tblInfo
where Id = #RowId
and session_lock = ##SPID
if (#DataAvailable = 1)
begin
if (#Date is Null)
begin
insert SomeTable (TblInfoId, Date)
values (#RowId, GetUtcDate())
end
else
begin
update SomeTable
set Date = #Date
where tblInfoId = #RowId
end
update tblInfo
set session_lock = null
where Id = #RowId
and session_lock = ##SPID
end
--optionally do something if the row was already "locked"
--else
--begin
--...
--end
if ##TranCount > 0 Commit Tran
Right after "begin tran", update your table like below script
begin tran
-- this update will lock the table so no other process can read data
update tblInfo with (tablock)
set date = date
---- do what ever you need to do here
set #date=(select * from tblinfo)
if(#date is null)
--insert
else
--update
commit tran
this will cause SQL Server lock the table and the second transaction will wait reading the data until the first process finish.
Relying on lock hints to get the desired outcome will perhaps prevent you from attempting to insert the value twice, but it won't prevent errors from happening. You'll just get an 'Unable to acquire lock' or deadlock error instead.
You should put a mutex in your code, in the minimally sized critical section. Using sp_getApplock, you can get a lock on a section of code with a wait period that you specify (subsequent threads will wait for the lock to clear and then continue). Here's some sample code:
declare #LockResource nvarchar(100) = 'VALUE_INSERT'
begin transaction
-- Fetch the lock:
EXEC #res = sp_getapplock
#Resource = #LockResource,
#LockMode = 'Exclusive',
#LockOwner = 'Transaction',
#LockTimeout = 10000,
#DbPrincipal = 'public'
if #res not in (0, 1)
begin
set #msg = 'Unable to acquire Lock for ' + #LockResource
raiserror (#msg , 16, 1)
rollback transaction
return
end
else
begin
begin try
---------------------------------------------
-- Fetch value if it exists, insert if not.
-- Both need to happen here.
---------------------------------------------
end try
begin catch
select #msg = 'An error occurred: ' + ERROR_MESSAGE()
-- Release the lock.
EXEC #res = sp_releaseapplock
#Resource = #LockResource,
#DbPrincipal = 'public',
#LockOwner = 'Transaction'
rollback transaction
raiserror(#msg, 11, 1)
goto cleanup
end catch
-- Release the lock.
EXEC #res = sp_releaseapplock
#Resource = #LockResource,
#DbPrincipal = 'public',
#LockOwner = 'Transaction'
end
commit transaction

RAISERROR Dosn't Work Inside CATCH With ROLLBACK TRANSACTION

I created a Stored Procedure to Insert Into 2 Table With Transaction to make sure That Both Inserts Done and I Used TRY and CATCH to Handle The Errors .. The Problem Is In The Catch Statement I Put ROLLBACK TRANS and RAISERROR The RoLLBACK Works But The Procedure Dose not RAISERROR
Here is The Code
ALTER PROC SP_InsertPlot
#PlotName nvarchar(50),
#GrossArea int,
#SectorName Nvarchar(50),
#PlotYear int,
#OwnerName Nvarchar(50),
#Remarks text,
#NumberOfPlants INT,
#NetArea INT,
#Category Nvarchar(50),
#Type Nvarchar(50),
#Variety Nvarchar(50),
#RootStock Nvarchar(50),
#PlantDistance Decimal(18,2)
AS
BEGIN
DECLARE #PlotID INT
SET #PlotID = (SELECT ISNULL(MAX(PlotID),0) FROM Plots) + 1
DECLARE #SectorID INT
SET #SectorID = (SELECT SectorID FROM Sectors WHERE SectorName = #SectorName)
DECLARE #OwnerID INT
SET #OwnerID = ( SELECT OwnerID FROM Owners WHERE OwnerName = #OwnerName)
DECLARE #CategoryID INT
SET #CategoryID = (SELECT CategoryID FROM Categories WHERE CategoryName = #Category)
DECLARE #TypeID INT
SET #TypeID = (SELECT TypeID FROM Types WHERE TypeName = #Type)
DECLARE #VarietyID INT
SET #VarietyID = (SELECT VarietyID FROM Varieties WHERE VarietyName = #Variety)
DECLARE #RootStockID INT
SET #RootStockID = (SELECT RootStockID FROM RootStocks WHERE RootStockName = #RootStock)
DECLARE #PlotDescID INT
SET #PlotDescID = (SELECT ISNULL(MAX(PlotDescID),0) FROM PlotDescriptionByYear) + 1
BEGIN TRY
SET XACT_ABORT ON
SET NOCOUNT ON
IF(SELECT Count(*) FROM Plots WHERE PlotName = #PlotName) = 0
BEGIN
BEGIN TRANSACTION
INSERT INTO Plots (PlotID,PlotName,GrossArea,SectorID,PlantYear,OnwerID,Remarks)
VALUES(#PlotID,#PlotName,#GrossArea,#SectorID,#PlotYear,#OwnerID,#Remarks)
INSERT INTO PlotDescriptionByYear (PlotDescID, PlantYear, NumberOfPlants,PlotID,NetArea,CategoryID,TypeID,VarietyID,RootStockID,PlantDistance)
VALUES(#PlotDescID,YEAR(GETDATE()),#NumberOfPlants,#PlotID -1,#NetArea,#CategoryID,#TypeID,#VarietyID,#RootStockID,#PlantDistance)
COMMIT TRANSACTION
END
END TRY
BEGIN CATCH
IF(XACT_STATE())= -1
BEGIN
ROLLBACK TRANSACTION
RAISERROR('This Plot Is Already Exists !!',11,1)
END
END CATCH
END
By the way i tried to change the Severity and I tried ##TRANCOUNT instead of XACT_STATE and the Same Problem Happens Which is When I Exec Proc and Pass an Existing Data To The Parameters The Transaction Roll back and did not rise the error
Change IF(XACT_STATE())= -1 to IF(XACT_STATE()) <> 0 and your problem will be done.

i want to decrease the quantity field and decrease its number

CREATE PROCEDURE dbo.IssueBook
(
#bookid nvarchar(50),
#ano nvarchar(50),
#mid int,
#librarian varchar(10),
#quantity int
)
AS
declare #cnt int
declare #msg varchar(100)
if not exists( select * from books where bookid = #bookid and quantity = #quantity)
begin
raiserror('Book is not available',16,1);
return;
end;
select #cnt = count(bookid) from issues where mid = #mid;
if ( #cnt >= 2 )
begin
raiserror('Maximum Limit Has Been Reached For Member!',16,1);
return;
end;
begin tran
begin try
update books set quantity =#quantity-1 where bookid= #bookid;
insert into issues values (#bookid, #mid, getdate(), #librarian, #ano);
commit tran
end try
begin catch
rollback tran
/* select #msg = error_message() */
raiserror( 'Unknown Error', 16,1);
end catch
i want to change value of quantity field in sql table how can i do that please help me i tried many things but they are not working i will be very thankful to you...
I think the problem is in this part: set quantity =#quantity-1. If I understand you correctly, it should either be
set quantity = quantity-1 -- Decreease the book quantity by 1
or
set quantity = quantity - #quantity -- Decreease the book quantity by #quantity

Yet another primary key exception

Last year I ask this question: What could be causing the primary key exception?
But I still have another error in this stored procedure: randomly I get
INSERT EXCEPTION WITH FOREIGN KEY "FK_Sessions" WITH TABLE
"Sessions", column "id".
CREATE PROCEDURE [dbo].[MyProcedure]
#sessionId varchar(512),
#variable varchar(350),
#value image
AS
BEGIN
BEGIN TRAN
DECLARE #result int = 0;
DECLARE #locked bit;
IF (SELECT COUNT(*) FROM Sessions WITH(ROWLOCK,HOLDLOCK) WHERE id = #sessionId) = 0
BEGIN
SET #result = -1;
END
ELSE BEGIN
DELETE Variables WITH(ROWLOCK,HOLDLOCK) WHERE sessionId = #sessionId AND variable = #variable
IF #value IS NOT NULL
BEGIN
INSERT Variables VALUES(#sessionId, #variable, #value, 0)
END
END
COMMIT TRAN
RETURN #result
END
Any ideas? Thanks again