If..begin..begin tran..Rollback - sql

I've following code but it gives an error, please guide me how to nested if..begin..begin__tran__Rollback or commit
declare #acname as varchar(max),
#gstcount as int
set #acname='Party1'
use TESTDB
set #gstcount = (select count(GSTIN) from M_STATEWISEGSTINACTAG where GSTIN<>'UNREGISTERED' AND ACCD=
(select accd from m_ledger where descr LIKE #acname))
if #gstcount = 1
begin
--multiple select statments
select .......
--to display require info.
end
--Then Update Values
begin tran;
update M_tbl ........where....
--Show result with updated value
select * from M_tbl........where.........
--Undo Changes
ROLLBACK;
else
begin
SELECT ........reuqir info
end;
In this script I got an error 'Incorrect syntax near the keyword 'else'.

Try this:
DECLARE #acname AS VARCHAR(MAX), #gstcount AS INT;
SET #acname = 'Party1';
USE TESTDB;
SET #gstcount =
(
SELECT COUNT(GSTIN)
FROM M_STATEWISEGSTINACTAG
WHERE GSTIN <> 'UNREGISTERED' AND ACCD =(SELECT accd FROM m_ledger WHERE descr LIKE #acname)
);
IF #gstcount = 1
BEGIN
BEGIN
--multiple select statments
SELECT .......
--to display require info.
END;
--Then Update Values
BEGIN TRAN;
update M_tbl ........where....
--Show result with updated value
select * from M_tbl........where.........
--Undo Changes
ROLLBACK TRAN;
END;
ELSE
BEGIN
SELECT........reuqir info;
END;

Related

Trigger that prevents update of column based on result of the user defined function

We have DVD Rental company. In this particular scenario we consider only Member, Rental and Membership tables.
The task is to write a trigger that prevents a customer from being shipped a DVD
if they have reached their monthly limit for DVD rentals as per their membership contract using the function.
My trigger leads to infinite loop. It works without While loop, but then it does not work properly, if I consider multiple updates to the Rental table. Where I am wrong?
-- do not run, infinite loop
CREATE OR ALTER TRIGGER trg_Rental_StopDvdShip
ON RENTAL
FOR UPDATE
AS
BEGIN
DECLARE #MemberId INT
DECLARE #RentalId INT
SELECT * INTO #TempTable FROM inserted
WHILE (EXISTS (SELECT RentalId FROM #TempTable))
BEGIN
IF UPDATE(RentalShippedDate)
BEGIN
IF (SELECT TotalDvdLeft FROM dvd_numb_left(#MemberId)) <= 0
BEGIN
ROLLBACK
RAISERROR ('YOU HAVE REACHED MONTHLY LIMIT FOR DVD RENTALS', 16, 1)
END;
END;
DELETE FROM #TempTable WHERE RentalID = #RentalId
END;
END;
My function looks as follows:
CREATE OR ALTER FUNCTION dvd_numb_left(#member_id INT)
RETURNS #tab_dvd_numb_left TABLE(MemberId INT, Name VARCHAR(50), TotalDvdLeft INT, AtTimeDvdLeft INT)
AS
BEGIN
DECLARE #name VARCHAR(50)
DECLARE #dvd_total_left INT
DECLARE #dvd_at_time_left INT
DECLARE #dvd_limit INT
DECLARE #dvd_rented INT
DECLARE #dvd_at_time INT
DECLARE #dvd_on_rent INT
SET #dvd_limit = (SELECT Membership.MembershipLimitPerMonth FROM Membership
WHERE Membership.MembershipId = (SELECT Member.MembershipId FROM Member WHERE Member.MemberId = #member_id))
SET #dvd_rented = (SELECT COUNT(Rental.MemberId) FROM Rental
WHERE CONCAT(month(Rental.RentalShippedDate), '.', year(Rental.RentalShippedDate)) = CONCAT(month(GETDATE()), '.', year(GETDATE())) AND Rental.MemberId = #member_id)
SET #dvd_at_time = (SELECT Membership.DVDAtTime FROM Membership
WHERE Membership.MembershipId = (SELECT Member.MembershipId FROM Member WHERE Member.MemberId = #member_id))
SET #dvd_on_rent = (SELECT COUNT(Rental.MemberId) FROM Rental
WHERE Rental.MemberId = #member_id AND Rental.RentalReturnedDate IS NULL)
SET #name = (SELECT CONCAT(Member.MemberFirstName, ' ', Member.MemberLastName) FROM Member WHERE Member.MemberId = #member_id)
SET #dvd_total_left = #dvd_limit - #dvd_rented
SET #dvd_at_time_left = #dvd_at_time - #dvd_on_rent
IF #dvd_total_left < 0
BEGIN
SET #dvd_total_left = 0
SET #dvd_at_time_left = 0
INSERT INTO #tab_dvd_numb_left(MemberId, Name, TotalDvdLeft, AtTimeDvdLeft)
VALUES(#member_id, #name, #dvd_total_left, #dvd_at_time_left)
RETURN;
END
INSERT INTO #tab_dvd_numb_left(MemberId, Name, TotalDvdLeft, AtTimeDvdLeft)
VALUES(#member_id, #name, #dvd_total_left, #dvd_at_time_left)
RETURN;
END;
Will be glad for any advice.
Your main issue is that even though you populate #TempTable you never pull any values from it.
CREATE OR ALTER TRIGGER trg_Rental_StopDvdShip
ON RENTAL
FOR UPDATE
AS
BEGIN
DECLARE #MemberId INT, #RentalId INT;
-- Move test for column update to the first test as it applies to the entire update, not per row.
IF UPDATE(RentalShippedDate)
BEGIN
SELECT * INTO #TempTable FROM inserted;
WHILE (EXISTS (SELECT RentalId FROM #TempTable))
BEGIN
-- Actually pull some information from #TempTable - this wasn't happening before
SELECT TOP 1 #RentalID = RentalId, #MemberId = MemberId FROM #TempTable;
-- Select our values to its working
-- SELECT #RentalID, #MemberId;
IF (SELECT TotalDvdLeft FROM dvd_numb_left(#MemberId)) <= 0
BEGIN
ROLLBACK
RAISERROR ('YOU HAVE REACHED MONTHLY LIMIT FOR DVD RENTALS', 16, 1)
END;
-- Delete the current handled row
DELETE FROM #TempTable WHERE RentalID = #RentalId
END;
-- For neatness I always drop temp tables, makes testing easier also
DROP TABLE #TempTable;
END;
END;
An easy way to debug simply triggers like this is to copy the T-SQL out and then create an #Inserted table variable e.g.
DECLARE #Inserted table (RentalId INT, MemberId INT);
INSERT INTO #Inserted (RentalId, MemberId)
VALUES (1, 1), (2, 2);
DECLARE #MemberId INT, #RentalId INT;
-- Move test for column update to the first test as it applies to the entire update, not per row.
-- IF UPDATE(RentalShippedDate)
BEGIN
SELECT * INTO #TempTable FROM #inserted;
WHILE (EXISTS (SELECT RentalId FROM #TempTable))
BEGIN
-- Actually pull some information from #TempTable - this wasn't happening before
SELECT TOP 1 #RentalID = RentalId, #MemberId = MemberId FROM #TempTable;
-- Select our values to its working
SELECT #RentalID, #MemberId;
-- IF (SELECT TotalDvdLeft FROM dvd_numb_left(#MemberId)) <= 0
-- BEGIN
-- ROLLBACK
-- RAISERROR ('YOU HAVE REACHED MONTHLY LIMIT FOR DVD RENTALS', 16, 1)
-- END;
-- Delete the current handled row
DELETE FROM #TempTable WHERE RentalID = #RentalId
END;
-- For neatness I always drop temp tables, makes testing easier also
DROP TABLE #TempTable;
END;
Note: throw is the recommended way to throw an error instead of raiserror.
Another thing to consider is that you must try to transform your UDF into an inline TVF because of some side effects.
Like this one:
CREATE OR ALTER FUNCTION dvd_numb_left(#member_id INT)
RETURNS TABLE
AS
RETURN
(
WITH
TM AS
(SELECT Membership.MembershipLimitPerMonth AS dvd_limit,
Membership.DVDAtTime AS dvd_at_time,
CONCAT(Member.MemberFirstName, ' ', Member.MemberLastName) AS [name]
FROM Membership AS MS
JOIN Member AS M
ON MS.MembershipId = M.MembershipId
WHERE M.MemberId = #member_id
),
TR AS
(SELECT COUNT(Rental.MemberId) AS dvd_rented
FROM Rental
WHERE YEAR(Rental.RentalShippedDate ) = YEAR(GETDATE)
AND MONTH(Rental.RentalShippedDate ) = MONTH(GETDATE)
AND Rental.MemberId = #member_id
)
SELECT MemberId, [Name],
CASE WHEN dvd_limit - dvd_rented < 0 THEN 0 ELSE dvd_limit - dvd_rented END AS TotalDvdLeft,
CASE WHEN dvd_limit - dvd_rented < 0 THEN 0 ELSE dvd_at_time - dvd_on_rent END AS AtTimeDvdLeft
FROM TM CROSS JOIN TR
);
GO
Which will be much more efficient.
The absolute rule to have performances is: TRY TO STAY IN A "SET BASED" CODE instead of iterative code.
The above function can be optimized by the optimzer whilet yours cannot and will needs 4 access to the same tables.

How to union multiple select statements while they are not near together?

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.

Having an issue with a stored procedure SQL-Server

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.

If else expression compare not working

i want to perform a compare if #accid2 not equal #accid then roll back action, else perform insert.
My result of this trigger is even that is not match but it still insert into my table.
here is my code:
ALTER TRIGGER [dbo].[TG_checkacctypehtl]
ON [dbo].[Accommodation_Hotel] INSTEAD OF INSERT
AS
DECLARE #accid NVARCHAR(50), #accid2 NVARCHAR(50),#hid NVARCHAR(50),#fsp NVARCHAR(50), #fc NVARCHAR(50), #sr NVARCHAR(50);
SELECT #hid = i.hotel_id FROM INSERTED i;
SELECT #fsp = i.facillities_swimming_pool FROM INSERTED i;
SELECT #fc = i.facillities_catering FROM INSERTED i;
SELECT #sr = i.star_rating FROM INSERTED i;
SELECT #accid2 = i.accommodation_id FROM INSERTED i;
SELECT #accid = accommodation_id FROM [dbo].[Accommodation] WHERE accommodation_type= 'hotel' AND accommodation_id=#accid2;
BEGIN
BEGIN TRAN
SET NOCOUNT ON
PRINT #accid2
PRINT #accid
IF(#accid2 != #accid)
BEGIN
RAISERROR('Record Not Inserted, Accommodation ID is not a Hotel Id',16,1); ROLLBACK; END
ElSE BEGIN
INSERT INTO [dbo].[accommodation_hotel] (hotel_id,facillities_swimming_pool,facillities_catering,star_rating,accommodation_id)
VALUES (#hid,#fsp,#fc,#sr,#accid2);COMMIT;
END
END
*print is for check the value i get.
is that my logic error or my syntax error?
I would rewrite the whole trigger something like this...
ALTER TRIGGER [dbo].[TG_checkacctypehtl]
ON [dbo].[Accommodation_Hotel]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [dbo].[accommodation_hotel] (hotel_id,facillities_swimming_pool,facillities_catering,star_rating,accommodation_id)
SELECT i.hotel_id
,i.facillities_swimming_pool
,i.facillities_catering
,i.star_rating
,i.accommodation_id
FROM inserted i
WHERE EXISTS ( SELECT 1
FROM [dbo].[Accommodation] a
WHERE a.accommodation_type= 'hotel'
AND a.accommodation_id = i.accommodation_id)
IF EXISTS (SELECT 1 FROM inserted i
WHERE NOT EXISTS ( SELECT 1
FROM [dbo].[Accommodation] a
WHERE a.accommodation_type= 'hotel'
AND a.accommodation_id = i.accommodation_id)
)
BEGIN
RAISERROR('Records with invalid Accommodation ID is not a Hotel Id not inserted',16,1);
END
END
Insert the rows with valid accommodation ids and raise an error if there are any rows with invalid Hotel Ids, Also no need for all those variables.
Also triggers are fired for each transaction, not for each row. Your code assume there will only be one rows inserted ever in the table at a time.
Should be IF(#accid2 <> #accid)

loop in sql stored procedure and removing cursors

I have to modify a STORED procedure that is written by someone else.Basically the stored prcoedure uses a cusrsor to fetch the data from the table and then insert that data in another table. While fetching the code form another table, it also gets some distinct columns from another table Below is my code:
Declare data_cursor cursor for
Select emp_no, emp_name, event_date, Test_no, Code, Test_result
From test_table1
ORDER by emp_no
declare
#empNo varchar(100),
#emp_name varchar(2000),
#eventDate varchar(20),
#TestNo varchar(100),
#Code varchar(100),
#TestReuslt varchar(100),
#ProcessName varchar(100),
#FileProcess varchar(200),
#TestProcess varchar(100),
#countA int,
#error_count int
SELECT #ProcessName = (select distinct userID from test_table1)
SELECT #FileProcess = 'EW' + #ProcessName
Select #TestProcess = (Select distinct userID from test_Table1) + 'TXT'
select #countA = 0
BEGIN tran
OPEN data_cursor
fetch data_cursor into
#empNo ,
#emp_name ,
#eventDate ,
#TestNo ,
#Code ,
#TestReuslt
while (##FETCH_STATUS=0)
begin
insert into TESTTable2
(
empNum, empName, eventDate,TestNum, Code, TestResult, Testprocess, ProcessName)
values (#empNo, #emp_name, #eventDate , #TestNo , #Code, #TestReuslt, #TestProcess, #ProcessName)
if # ERROR > 0
begin
select #error_count = #error_count + 1
end
else
set #record_id = ##Identity
if #code like 'D%'
Insert into TESTTable3
(testProcess, FileProcess, empNum)
values (#TestProcess, #FileProcess, #empNo )
if ##error > 0
begin
select #error_count = #error_count + 1
end
set #countA = #countA + 1
fetch data_cursor into
fetch data_cursor into
#empNo ,
#emp_name ,
#eventDate ,
#TestNo ,
#Code ,
#TestReuslt
if # ERROR > 0
BEGIN
select #error_count = #error_count + 1
end
end
if #error_count > 0
begin
rollback tran
end
else
begin /* ##error = 0 */
commit tran
close data_cursor
deallocate data_cursor
Insert into LOG_File
(Name, Count, Processname)
values ('Test1', #CountA,#ProcessName)
Select 'TotalCount' = #CountA
The reason, I have to modify the above STORED proc now is because of some APPLICATION changes, I am getting around 50 distinct userID's from test_table1 so the above subquery(SELECT #ProcessName = (select distinct userID from test_table1) doesn't work. How can I loop through the above stored proc so that each #ProcessName can get inserted in table TESTTable2 so in other words
I want to pass each userId one at a time and insert it in table test_table1 and other subsequent tables. I can declare another cursor to accomplish this, but I was wondering if there is any better way to rewrite this stored proc and not use the cursor at all.
because of my application changes all these three statements above are throwing the error:
SELECT #ProcessName = (select distinct userID from test_table1)
SELECT #FileProcess = 'EW' + #ProcessName
Select #TestProcess = (Select distinct userID from testTable1) + 'TXT'
I am using sql server 2005.
any help will be greatly appreciated.
declare #countA int=0
begin tran
begin try
insert into TESTTable2(empNum, empName, eventDate,TestNum, Code, TestResult, Testprocess, ProcessName)
Select emp_no, emp_name, event_date, Test_no, Code, Test_result,userID+ 'TXT',userID
From test_table1
ORDER by emp_no
SET #CountA=##ROWCOUNT
Insert into TESTTable3(testProcess, FileProcess, empNum)
Select userID+ 'TXT','EW' + userID,emp_no
From test_table1
Where code like 'D%'
ORDER by emp_no
commit tran
Insert into LOG_File(Name, Count, Processname) values ('Test1', #CountA,'#ProcessName')
end try
begin catch
rollback tran
SET #CountA =0
Insert into LOG_File(Name, Count, Processname) values ('Test1', #CountA,'#ProcessName')
SELECT ERROR_NUMBER() AS ErrorNumber,ERROR_MESSAGE() AS ErrorMessage
end catch
Select #CountA [TotalCount]