SQL transaction/ErrorHandling/TryandCatch - sql-server-2012

Taking a SQL programming(2012 version) class and totally stuck, program wont work no matter how many times I try. The requirements (Questions) as well as what I have so far are below. Below the dashed line is another proc I wrote for error handling. Please help me finish this...please!
/*Create a Stored Procedure that accepts StockName, NewOpenPrice, NewClosePrice.
a. If the Stock Name does not EXIST a new record should be added into the dbo.Stocks table
b. If the Stock Name does EXIST, the OpenPrice an ClosePrice will be updated with the newly inserted Prices.
c. Insert and Update statements should be built using a transaction (Repeatable Read Isolation Level)
d. A Try Catch Statement should be used for the Update and Insert statements. If there is an error, the dbo.error_handler Stored Procedure should be called.
*/
CREATE PROCEDURE spc_Stocks
#Name varchar(25), #NewOpenPrice money, #NewClosePrice money
as
BEGIN
CREATE TABLE dbo.Stocks (
StockID int IDENTITY(1,1),
StockName varchar(50),
OpenPrice money,
ClosePrice money )
INSERT INTO dbo.Stocks
SELECT 'Walmart',21.58,22.98 UNION
SELECT 'Target',17.32,15.23 UNION
SELECT 'Taco Bell',4.58,12.98 UNION
SELECT 'Microsoft',7.15,8.15 UNION
SELECT 'Apple',10.79,9.89
Select StockName from stocks
where StockName = #Name
------Name does NOT exist
if (#Name = NULL)
Begin
Insert into dbo.Stocks (StockName)
Values (#Name)
END
----If name DOES exist
ELSE
BEGIN
Begin TRY
Begin SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
UPDATE dbo.Stocks SET OpenPrice =#NewOpenPrice, ClosePrice= #NewClosePrice where StockName = #Name
Commit transaction
END TRY
BEGIN CATCH
?!?!?!?!?!?
*This is my ErrorHandler stored proc query
ALTER PROCEDURE dbo.error_handler
as
BEGIN
DECLARE #errnum INT,
#severity INT,
#errstate INT,
#proc NVARCHAR(126),
#line INT,
#message NVARCHAR(4000)
-- capture the error information that caused the CATCH block to be invoked
SELECT #errnum = ERROR_NUMBER(),
#severity = ERROR_SEVERITY(),
#errstate = ERROR_STATE(),
#proc = ERROR_PROCEDURE(),
#line = ERROR_LINE(),
#message = ERROR_MESSAGE()
end

Create PROCEDURE [dbo].[USP_Stocks]
(#Name varchar(25), #OpenPrice MONEY, #ClosePrice MONEY)
AS
BEGIN
-----Name does NOT exist
IF NOT EXISTS (SELECT StockName FROM [dbo].[Stocks]
WHERE StockName = #Name)
BEGIN
BEGIN TRY
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRAN
INSERT INTO dbo.Stocks (StockName, OpenPrice, ClosePrice)
VALUES (#Name ,#OpenPrice, #ClosePrice)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
EXEC dbo.error_handler
ROLLBACK
END CATCH
END
----If name DOES exist
ELSE
BEGIN
BEGIN TRY
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRAN
UPDATE dbo.Stocks SET OpenPrice =#OpenPrice, ClosePrice= #ClosePrice
where StockName = #Name
Commit transaction
END TRY
BEGIN CATCH
ROLLBACK TRUANSACTION
EXEC dbo.error_handler
END CATCH
END
END

Related

Trigger to block insert into SQL Server

I have a requirement to prevent insert into table using after insert based on certain condition. When I am calling insert statement directly, it is executing perfectly without any problem. Whereas when am using procedure for insert statement with transaction scope, I am getting this error
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
My code:
create table test
(
id int ,
name varchar(10)
)
create table test1
(
id int ,
name varchar(10)
)
ALTER PROCEDURE test_insert
#id INT, #name NVARCHAR(10)
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO test1 (id, name)
VALUES (#id, #name)
INSERT INTO test (id, name)
VALUES (#id, #name)
COMMIT
END TRY
BEGIN CATCH
ROLLBACK;
DECLARE #errormsg NVARCHAR(MAX)
SELECT #errormsg = ERROR_MESSAGE();
THROW 500001, #errormsg, 1;
END CATCH
end
ALTER TRIGGER TRG_test
ON dbo.test
AFTER INSERT AS
BEGIN
DECLARE #idNum INT
SELECT #idNum = id FROM inserted
IF #idNum = 1
BEGIN
RAISERROR('error', 1,1);
ROLLBACK TRANSACTION
RETURN
END
END
Please let me know if am missing anything
Remove the ROLLBACK TRANSACTION inside the Trigger, remember that DML statements within the trigger will use the transaction context of the statement that fired the trigger, in this case, it would be covered by:
Your SP ==>
BEGIN TRANSACTION
--Fired the trigger and is involved by the same Transaction from the SP
insert into test1 (id,name) values(#id,#name)
insert into test (id,name) values(#id,#name)
COMMIT
Remove the ROLLBACK from the trigger and raise and error with severity 11 or higher so that the stored procedure CATCH block is entered. The code below also uses the simplified version of THROW to reraise the trigger error instead of throwing a new one and uses EXISTS to handle multi-row inserts.
alter proc test_insert #id int, #name nvarchar(10)
as
begin
BEGIN TRY
BEGIN TRANSACTION
insert into test1 (id,name) values(#id,#name)
insert into test (id,name) values(#id,#name)
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH
end
GO
alter TRIGGER TRG_test
ON dbo.test
AFTER INSERT AS
BEGIN
if EXISTS(select 1 from inserted WHERE id = 1)
begin
RAISERROR('error', 16,1);
RETURN
end
END
GO
From my understanding,
alter TRIGGER TRG_test
ON dbo.test
instead of insert as --- Trigger type is changed. Trigged before insert
BEGIN
declare #idNum int
select #idNum = id from inserted
if #idNum <> 1 ------ Condition is changed
begin
/* Do what you want.*/
RETURN
end
END
update 1:
create table test(
id int ,
name varchar(10)
)
create table test11(
id int ,
name varchar(10)
)
alter proc test_insert #id int, #name nvarchar(10)
as
begin
BEGIN TRY
BEGIN TRANSACTION
insert into test11(id,name) values(#id,#name)
insert into test (id,name) values(#id,#name)
COMMIT
END TRY
BEGIN CATCH
ROLLBACK;
declare #errormsg nvarchar(max)
select #errormsg=ERROR_MESSAGE();
THROW 500001, #errormsg, 1;
END CATCH
end
alter TRIGGER TRG_test
ON dbo.test
instead of insert as --- Trigger type is changed. Trigged before insert
BEGIN
declare #idNum int, #name int
select #idNum = id, #name=name from inserted
if #idNum <> 1 ------ Condition is changed
begin
insert into test (id,name) values(#idNum,#name)
end
else begin
print 'You insert invalid value 1'
end
END
test_insert 1, 2 -- error will be arised.
select * from test
select * from test11
test_insert 2, 2 -- insertion is occurs.
select * from test
select * from test11

How to make trigger react on specific SP only

create procedure UpdateIte(#iID int,#qtt int)
as begin
insert into updateItem
values(#iID,#qtt,getdate())
end
and
create procedure releseItem(#eid int,#qty int,#itemId int)
AS BEGIN
declare #no int,#qt int
select #no=qty
from Stock
where itemID=#itemID
if(#no>#qty)
BEGIN
update Stock
set qty=qty-#qty
where itemID=#itemID
select #qt=qty
from Stock
where itemID=#itemID
insert into release
values(#eid,#itemId,#qty,getdate())
if(#qt<=10)
BEGIN
print'Item needs to replace'
END
else
print'Success'
END
else
BEGIN
print'Not Enough items in stock'
END
END
i want to create trigger, when release items that update the release table with employee id. when i create trigger it trigger with both procedures. But i want it to trigger with releaseItem. How do i do that?
create trigger CheckQty
ON Stock
for Update
as begin
declare #qty int, #emp int, #q int,#ItemId int
select #qty= qty, #emp=eid, #ItemId=ItemId
from inserted
select #q=qty
from Stock
where ItemId=#ItemId
if(#qty>0)
begin
insert into releseItem
values (#eid,#ItemId,#qty)
end
else
rollback transaction
end
You can just exececute store procedure in trigger as below.
create trigger CheckQty ON Stock for Update as begin declare #qty int, #emp int, #q int,#ItemId int
select #qty= qty, #emp=eid, #ItemId=ItemId
from inserted
select #q=qty
from Stock
where ItemId=#ItemId
if(#qty>0)
begin
exec releseItem #eid, #ItemId, #qty
end
else
rollback transaction
end

Cannot rollback in if block transaction sql

When I executed this stored procedure, I cannot rollback in IF block even the IF statement was right. If the IF statement was right, script still running in the end and there is no rollback at all.
The message: The account id has already used successful register
Msg 3902, Level 16, State 1, Procedure PROC_DANGKY, Line 23
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.
Code:
CREATE PROCEDURE PROC_REGISTER
#name nvarchar(30),
#birth datetime,
#passport nvarchar(9),
#address nvarchar(50),
#phone nvarchar(11),
#email nvarchar(20),
#account nvarchar(30),
#password nvarchar(20)
AS
BEGIN TRAN
BEGIN TRY
IF (EXISTS(SELECT * FROM CUSTOMER WHERE ACCOUNT = #account))
BEGIN
PRINT N'The account id has already been used'
ROLLBACK TRAN
END
INSERT INTO KHACHHANG
VALUES (#name, #birth, #passport, #address, #phone, #email, #account, #password)
PRINT N'Successfully registered'
END TRY
BEGIN CATCH
DECLARE #ErrorMsg VARCHAR(2000)
SELECT #ErrorMsg = N'Error: ' + ERROR_MESSAGE()
RAISERROR(#ErrorMsg, 16,1)
ROLLBACK TRAN
RETURN
END CATCH
COMMIT TRAN
REFORMATTED your query
Removed rollback after exists
CREATE PROC PROC_REGISTER
#name nvarchar(30),
#birth datetime,
#passport nvarchar(9),
#address nvarchar(50),
#phone nvarchar(11),
#email nvarchar(20),
#account nvarchar(30),
#password nvarchar(20)
AS
BEGIN TRAN
BEGIN TRY
IF (EXISTS(SELECT * FROM CUSTOMER WHERE ACCOUNT = #account))
PRINT N'The account id has already used'
ELSE
BEGIN
INSERT INTO KHACHHANG VALUES (#name, #birth, #passport, #address, #phone, #email, #account, #password)
PRINT N'Successul register'
END
END TRY
BEGIN CATCH
DECLARE #ErrorMsg VARCHAR(2000)
SELECT #ErrorMsg = N'Error: ' + ERROR_MESSAGE()
RAISERROR(#ErrorMsg, 16,1)
IF ##TRANCOUNT > 0
ROLLBACK TRAN
END CATCH
IF ##TRANCOUNT > 0
COMMIT TRAN
GO
RETURN
You can check ##TRANCOUNT before rollback to ckeck if transaction exists:
CREATE PROC PROC_REGISTER
#name nvarchar(30),
#birth datetime,
#passport nvarchar(9),
#address nvarchar(50),
#phone nvarchar(11),
#email nvarchar(20),
#account nvarchar(30),
#password nvarchar(20)
AS
BEGIN TRAN
BEGIN TRY
IF (EXISTS(SELECT * FROM CUSTOMER WHERE ACCOUNT = #account))
BEGIN
PRINT N'The account id has already used'
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRAN
END
END
INSERT INTO KHACHHANG VALUES (#name, #birth, #passport, #address, #phone, #email, #account, #password)
PRINT N'Successul register'
END TRY
BEGIN CATCH
DECLARE #ErrorMsg VARCHAR(2000)
SELECT #ErrorMsg = N'Error: ' + ERROR_MESSAGE()
RAISERROR(#ErrorMsg, 16,1)
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRAN
END
RETURN
END CATCH
COMMIT TRAN

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

SQL Stored Procedure not handling error from nested stored procedure

I have two stored procedures, one nested inside the other. When the nested stored procedure is called, at the moment, it should error with a foreign key constraint violation and then rollback the earlier call to insert into the ProductLicense table. The nested procedure does not perform any action on the database because of the foreign key violation but the calling stored procedure isn't catching the error and rolling back. If I execute the nested stored procedure by itself it does return error 547 Foreign key violation.
How can I get the two stored procedures to work together?
Outer procedure:
ALTER PROCEDURE [dbo].[AddNewLicense2_i]
-- Add the parameters for the stored procedure here
#customerId nvarchar(10),
#licenseModeId int,
#licenseModeProgramId int,
#createdBy int,
#updateBy int,
#systemId nvarchar(50),
#productId int
AS
BEGIN TRY
BEGIN TRANSACTION
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--SET XACT_ABORT ON; --used for automatic rollback when an error occurs
DECLARE #tempDays INT
DECLARE #programCornerAmt INT
DECLARE #tempEndDate DATETIME
DECLARE #tempExpDate DATETIME
DECLARE #err INT
SET #err = 0
/*SET #tempDays = (SELECT lmp.TimeoutDays
FROM LicenseModeProgram lmp
WHERE lmp.LicenseModeProgramId = #licenseModeProgramId)*/
SELECT #tempDays = TimeoutDays, #programCornerAmt = MonthlyCornersAmount
FROM LicenseModeProgram
WHERE LicenseModeProgramId = #licenseModeProgramId
--Build Expiration and End Dates.
IF #tempDays = NULL --then this is NOT a time rental or metered system
BEGIN
SET #tempEndDate = NULL
SET #tempExpDate = NULL
END
ELSE
BEGIN
SET #tempEndDate = DATEADD("d", #tempDays, GETDATE())
SET #tempExpDate = DATEADD("d", #tempDays, GETDATE())
END
-- Create new product license record
INSERT INTO ProductLicense (CustomerId, LicenseModeId, LicenseModeProgramId, CreatedBy, UpdatedBy, SystemId, ProductId, ExpirationDate, LicenseEndDate)
VALUES (#customerId, #licenseModeId, #licenseModeProgramId, #createdBy, #updateBy, #systemId, #productId, #tempExpDate, #tempEndDate)
IF #licenseModeId = 4 AND #systemId NULL AND #programCornerAmt NULL
--call stored procedure to add corners to the customer account
EXECUTE #err = AddMeteredTx_i #systemId, 1, 1, #programCornerAmt , 'Initial License Creation'
PRINT #err
COMMIT TRANSACTION
END TRY
BEGIN CATCH
RAISERROR('Failed to Create License', 11, 2)
ROLLBACK TRANSACTION
RETURN 1
END CATCH
--COMMIT TRANSACTION
RETURN 0
GO
Inner procedure:
ALTER PROCEDURE [dbo].[AddMeteredTx_i]
-- Add the parameters for the stored procedure here
#systemId nvarchar(50),
#activityEventId int,
#createdBy int,
#amount int,
#notes text
AS
BEGIN TRY
BEGIN TRANSACTION
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--SET XACT_ABORT ON; --used for automatic rollback when an error occurs
INSERT INTO CustomerAccountActivity (SystemId, ActivityEventId, CreatedBy, Amount, Notes)
VALUES (#systemId, #activityEventId, #createdBy, #amount, #notes)
UPDATE CustomerAccount
SET MeteredBalance = (SELECT MeteredBalance FROM CustomerAccount WHERE SystemId = #systemId) + #amount
WHERE SystemId = #systemId
COMMIT TRANSACTION
END TRY
BEGIN CATCH
RAISERROR('Error Update to Customer Account Record ', 11, 2)
ROLLBACK TRANSACTION
RETURN 1
--COMMIT TRANSACTION
END CATCH
RETURN 0
GO
Catching errors with a call stack like this using ##Error can be problematic. It's a lot more reliable to use TRY/CATCH
The basic format is:
BEGIN TRY
<BEGIN TRAN>
... do stuff ...
<COMMIT TRAN>
END TRY
BEGIN CATCH
<ROLLBACK TRAN>
... do error stuff like re-raise the error to outer scope ...
END CATCH
Any error encountered in the try will automatically take you to the CATCH block without additional checking.