first attempt at a sproc transaction - tsql

I am trying to write a sproc with a transaction. Can anybody tell me if there would be any issues with code below, or if it will work as intended?
ALTER procedure [dbo].[DeleteMetricMeter]
(
#SectionID int,
#MetricMeterID int,
#Result bit output
)
as
declare #MetricMeterCount int
declare #err int
declare #rowcount int
set xact_abort on
begin tran
select #MetricMeterCount = count(*) from luMetricMeters
where fkSectionID = #SectionID
select #err = ##error, #rowcount = ##rowcount
if (#err <> 0) or (#rowcount = 0)
begin
goto on_error
end
delete from luMetricMeterList
where pkMetricMeterID = #MetricMeterID
select #err = ##error, #rowcount = ##rowcount
if (#err <> 0) or (#rowcount = 0)
begin
goto on_error
end
delete from luMetricMeters
where pkMetricMeterID = #MetricMeterID
select #err = ##error, #rowcount = ##rowcount
if (#err <> 0) or (#rowcount = 0)
begin
goto on_error
end
if (#MetricMeterCount = 1)
begin
delete from luMetricSections
where pkSectionID = #SectionID
select #err = ##error, #rowcount = ##rowcount
if (#err <> 0) or (#rowcount = 0)
begin
goto on_error
end
end
commit tran
set #result = 1
return #result
on_error:
rollback tran
set #result = 0
return #result

If you are using Sql Server 2005+, I would recomend rather using TRY...CATCH (Transact-SQL) and have a look at section [B. Using TRY…CATCH in a transaction]
This will GREATLY simplify your procedure.

There are a few issues with the procedure:
You should not evaluate rowcount unless it really indicates an error. When there is no data deleted there is no need to rollback.
Your code is not thread safe. The MetricMeterCount query should be changed to this to prevent other threads from performing the delete in between your select & the execution of the delete:
select #MetricMeterCount = count(*)
from luMetricMeters with (xlock, serializable)
where fkSectionID = #SectionID
I would write the code like this:
ALTER procedure [dbo].[DeleteMetricMeter]
(
#SectionID int,
#MetricMeterID int,
#Result bit output
)
as
DECLARE #MetricMeterCount int
DECLARE #err int
DECLARE #rowcount int
SET xact_abort ON
BEGIN TRAN
DELETE FROM luMetricMeterList
WHERE pkMetricMeterID = #MetricMeterID
SELECT #err = ##error
IF (#err <> 0)
GOTO on_error
DELETE FROM luMetricMeters
WHERE pkMetricMeterID = #MetricMeterID
SELECT #err = ##error
IF (#err <> 0)
GOTO on_error
IF EXISTS (SELECT *
FROM luMetricMeters WITH (xlock, serializable)
WHERE fkSectionID = #SectionID)
BEGIN
DELETE FROM luMetricSections
WHERE pkSectionID = #SectionID
SELECT #err = ##error
IF (#err <> 0)
GOTO on_error
END
COMMIT TRAN
RETURN (0)
on_error:
ROLLBACK TRAN
RETURN (-1)
GO
Note: The return values should be 0 for success and a negative number for failure.

Related

Looping a Delete SP

I need to some help trying to create some handling for this SP I'm creating which currently looks like this...
alter PROCEDURE ArchiveCampaignData
#CampaignId INT,
#DeleteBatchOf INT
AS
BEGIN
BEGIN
DECLARE #Rowcount INT = 1
WHILE #Rowcount > 0
DELETE TOP (#DeleteBatchOf)FROM dbo.u_activity
OUTPUT DELETED.* INTO u_activity_archive
WHERE campaignId = #CampaignId
SET #Rowcount = ##ROWCOUNT
END
BEGIN
DECLARE #Rowcount2 INT = 1
WHILE #Rowcount2 > 0
DELETE TOP (#DeleteBatchOf) C
OUTPUT DELETED.* INTO dbo.u_contact_archive
FROM dbo.U_contact AS C
INNER JOIN dbo.U_Campaigncontact AS CON WITH(NOLOCK)ON C.id = CON.contactId
WHERE CON.campaignId = #CampaignId
SET #Rowcount2 = ##ROWCOUNT
END
BEGIN
DECLARE #Rowcount3 INT = 1
WHILE #Rowcount3 > 0
DELETE TOP (#DeleteBatchOf) FROM u_campaignContact
OUTPUT Deleted.* INTO u_campaignContact_archive
WHERE campaignId = #CampaignId
SET #Rowcount3 = ##ROWCOUNT
END
BEGIN
DECLARE #Rowcount4 INT = 1
WHILE #Rowcount4 > 0
DELETE TOP (#DeleteBatchOf) L
OUTPUT DELETED.* INTO dbo.dxi_CDRLog_Archive
FROM dbo.dxi_CDRLog AS L
INNER JOIN dbo.u_campaignHdr AS H WITH(NOLOCK)ON L.qid = H.diallerCampaignId
WHERE H.parentId = #CampaignId
SET #Rowcount4 = ##ROWCOUNT
END
BEGIN
DECLARE #Rowcount5 INT = 1
WHILE #Rowcount5 > 0
DELETE TOP(#DeleteBatchOf) T
OUTPUT Deleted.* INTO dxi_diallerStatsSmall_archive
FROM dbo.dxi_diallerStatsSmall AS T
INNER JOIN dbo.u_campaignHdr AS G WITH (NOLOCK) ON G.diallerCampaignId = T.campaign
WHERE G.id = #CampaignId
SET #Rowcount5 = ##ROWCOUNT
END
BEGIN
DECLARE #Rowcount6 INT = 1
WHILE #Rowcount6 > 0
DELETE TOP(#DeleteBatchOf) FROM dbo.u_campaignHdr
OUTPUT Deleted.* INTO u_campaignHdr_archive
WHERE Id = #CampaignId OR parentId = #CampaignId
SET #Rowcount6 = ##ROWCOUNT
END
END
the goal is for each block to loop in the SP to run at a time and once each block is completed that block will stop its transaction and it will move on to the next block as well as printing 'Completed' after each successful delete.
Any recommendations/Enhancements will be appreciated .

Error Incorrect syntax

I have created a procedure in Sybase
create procedure prcrms_crms_cust_id_verify_ins(#customer_code numeric(12,0),#id_type tinyint,#verification_status varchar(12),#verification_response varchar(255),#verification_date datetime,#add_user varchar(21)) as
begin
declare #check_id_exists int
select #check_id_exists = count(*) from crms_customer_id_verification where ( ('customer_code'=#customer_code) and ('id_type'=#id_type))
if(#check_id_exists > 0)
begin
update crms_customer_id_verification set verification_status=#verification_status,verification_response=#verification_response,verification_date=#verification_date where ( ('customer_code'=#customer_code) and ('id_type'=#id_type))
return ##rowcount
end
if((#check_id_exists <> null) or (#check_id_exists <> 0))
begin
insert into crms_customer_id_verification(customer_code,id_type,verification_status,verification_response,verification_date,add_user) values(#customer_code,#id_type,#verification_status,#verification_response,#verification_date,#add_user)
return ##rowcount
end
end
when i try to execute the procedure
exec prcrms_crms_cust_id_verify_ins(3344,0,"VERIFIED",'{test:test}','1998-09-09 12:12:12.000','Admin')
it shows Incorrect syntax near '3344'.
/*
create procedure prcrms_crms_cust_verify_ins(#customer_code varchar(255),#id_type varchar(255) ,#verification_status varchar(12),#verification_response varchar(255),#verification_date varchar(255),#add_user varchar(21))as
/*
** ------------------------------------
** Created By : Bibil Mathew Chacko
** Created On : Jul 16 2016
** Description : insert in crms_customer_id_verification if id_type and customer exist.Update table if customer and id_type exist.
** -------------------------------------
*/
begin
declare
#check_id_exists int,
#cc numeric,
#id_t tinyint,
#vd datetime
select #cc = convert(numeric(12,0),#customer_code)
select #id_t = convert(tinyint,#id_type)
select #vd = convert(datetime,#verification_date)
select #check_id_exists = count(*) from crms_customer_id_verification where ( (customer_code=#cc) and (id_type=#id_t))
if(#check_id_exists > 0)
begin
update crms_customer_id_verification set verification_status=#verification_status,verification_response=#verification_response,verification_date=#vd where (customer_code=#cc and id_type=#id_t)
return ##rowcount
end
if((#check_id_exists <> null) or (#check_id_exists <> 0))
begin
insert into crms_customer_id_verification(customer_code,id_type,verification_status,verification_response,verification_date,add_user) values(#cc,#id_t,#verification_status,#verification_response,#vd,#add_user)
return ##rowcount
end
end
*/
/*exec prcrms_crms_cust_verify_ins '3344','0',"VERIFIED",'{test:test}','1998-09-09 12:12:12.000','Admin'*/
Should use VARCHAR type as parameter and procedure should have support for converting to required type. Another mistake I have done is Sybase Procedures don't have parenthesis.

Database proc fails, saying it failed to convert a varchar(param) to int

I have the following procedure which is supposed to insert a cell in a many to many table. The problem it it fails, with an error message which I do not get where it is caused, since I do not intend to do any conversion.
ALTER PROCEDURE [dbo].[updateGrade]
#grade int,
#studentName nvarchar(50),
#courseName nvarchar(50)
AS
BEGIN TRANSACTION;
BEGIN TRY
IF (#grade < 0 OR #grade > 10 OR #grade=NULL )
RAISERROR('!! grade -- #grade < 0 OR #grade > 10 OR ISNULL(#grade, 0) = 0', 18, 0)
IF (#studentName = '' OR ISNULL(#studentName,0) = 0)
RAISERROR('!! studentName -- #studentName = '' OR ISNULL(#studentName)', 18, 0)
IF (#courseName = '' OR ISNULL(#courseName,0) = 0)
RAISERROR('!! courseName -- #courseName = '' OR ISNULL(#studentName)', 18, 0)
SET #studID = (SELECT id FROM students WHERE students.name = #studentName)
SET #courseID = (SELECT id FROM courses WHERE courses.name = #courseName)
INSERT INTO grades (grade, studentID, courseID)
VALUES (#grade, #studID, #courseID)
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF ##TRANCOUNT > 0
COMMIT TRANSACTION;
params:
params: 6 / Maier Bogdan / baze2
ret code:
DECLARE #return_value int
EXEC #return_value = [dbo].[updateGrade]
#grade = 6,
#studentName = N'Maier Bogdan',
#courseName = N'baze2'
SELECT 'Return Value' = #return_value
GO
Error:
Conversion failed when converting the nvarchar value 'Maier Bogdan' to data type int.
Error is here:
ISNULL(#studentName,0) = 0
Comparing #studentName (nvarchar(50)) to 0 (int)
Notice that when #studentName is Null the problem not appear because you compare 0 to 0, but, when #studentName is not null you are comparing #studentName to 0!

How does one use loops in TSQL?

In TSQL, I would like to change the following code from have to use hard coded dhomes to using a loop for optimization. My failed attempt at trying to add a loop is also included.
Declare #dhome Tinyint, #bp smallint, #lr smallint, #q smallint
// Set #dhome = 1
While(#dhome <= 3) // My attempt to add a loop
SELECT #lr = MAX(NQdDate), #q = NQd
FROM NQdHistory
WHERE dhomeId = #dhome
GROUP BY NQdDate, NQd
SELECT #bd = COUNT(*)
FROM bdhome
WHERE NQdDate= #lr AND dhomeID= #dhome
DELETE FROM ND1 WITH(XLOCK)
WHERE dhomeID= #dhome AND NQdDate= #lr
UPDATE NQdHistory
SET Nbd = #q - ##RowCount - #bp, NBd = #bp
WHERE NQdDate= #lr AND dhomeID= #dhome
Set #dhome = #dhome +1 //My attempt to end a loop
You're on the right track. You're missing your begin and end. Also, be sure to give #dhome a value. It looks like you started to and have it commented out on your third line:
Declare #dhome Tinyint, #bp smallint, #lr smallint, #q smallint
// Set #dhome = 1
While(#dhome <= 3) // My attempt to add a loop
begin
SELECT #lr = MAX(NQdDate), #q = NQd
FROM NQdHistory
WHERE dhomeId = #dhome
GROUP BY NQdDate, NQd
SELECT #bd = COUNT(*)
FROM bdhome
WHERE NQdDate= #lr AND dhomeID= #dhome
DELETE FROM ND1 WITH(XLOCK)
WHERE dhomeID= #dhome AND NQdDate= #lr
UPDATE NQdHistory
SET Nbd = #q - ##RowCount - #bp, NBd = #bp
WHERE NQdDate= #lr AND dhomeID= #dhome
Set #dhome = #dhome +1 //My attempt to end a loop
end
If you're familiar with C/C#/C++, think of T-SQL's Begin and End like curly braces { and }, if you're more familiar with VB Then and End If. Or more like pascals Begin and End. You get the idea :)
Missing a begin and end on your while.
WHILE (Transact-SQL)
Example 1
DECLARE #I INT,#COUNTVAR INT
SET #I = 1
DECLARE #Parent_Child TABLE(ID INT IDENTITY(1,1),ParentPositionID INT NULL,ChildPositionId Int)
INSERT INTO #Parent_Child(ParentPositionID,ChildPositionId)
SELECT DISTINCT PARENT_POSITION_ID,CHILD_POSITION_ID from tblPOSITION_HIERARCHY
--WHERE CHILD_POSITION_ID IN (--YOUR CONDITION IF ANY)
SELECT #COUNTVAR =COUNT(*) FROM #PTS_Parent_Child
DECLARE #int_SUPE_POSITION_ID INT, #int_CHILD_POSITION_ID INT
--loop through records here
WHILE #I <= #COUNTVAR
BEGIN
SELECT #int_SUPE_POSITION_ID=ParentPositionID,#int_CHILD_POSITION_ID=ChildPositionId FROM #Parent_Child WHERE ID=#I
--Whatever you want to do with records
SET #I=#I+1
END
Example 2
Just another approach if you are fine using temp tables.I have personally tested this and it will not cause any exception (even if temp table does not have any data.)
CREATE TABLE #TempTable
(
ROWID int identity(1,1) primary key,
HIERARCHY_ID_TO_UPDATE int,
)
--INSERT DATA INTO TEMP TABLE USING INSERT INTO CLAUSE OR FOR EAXMPLE BELOW
--INSERT INTO #TempTable VALUES(1)
--INSERT INTO #TempTable VALUES(2)
--INSERT INTO #TempTable VALUES(4)
--INSERT INTO #TempTable VALUES(6)
--INSERT INTO ##TempTable VALUES(8)
DECLARE #MAXID INT
SET #COUNTER =1
SELECT #MAXID=COUNT(*) FROM #TempTable
--PRINT #MAXID
WHILE (#MAXID > 0)
BEGIN
--DO THE PROCESSING HERE
SELECT #HIERARCHY_ID_TO_UPDATE =PT.HIERARCHY_ID_TO_UPDATE FROM #TempTable PT WHERE ROWID=#COUNTER
--PRINT '#MAXID VALUE '
--PRINT #MAXID
SET #MAXID=#MAXID-1
SET #COUNTER =#COUNTER + 1
End
If(OBJECT_ID('tempdb..#TempTable') IS NOT NULL)
BEGIN
DROP TABLE #TempTable
END

T-SQL loop over query results

I run a query select #id=table.id from table and I need to loop over the results so I can exec a store procedure for each row exec stored_proc #varName=#id,#otherVarName='test'
How can I do this in a T-SQL script?
You could use a CURSOR in this case:
DECLARE #id INT
DECLARE #name NVARCHAR(100)
DECLARE #getid CURSOR
SET #getid = CURSOR FOR
SELECT table.id,
table.name
FROM table
OPEN #getid
FETCH NEXT
FROM #getid INTO #id, #name
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC stored_proc #varName=#id, #otherVarName='test', #varForName=#name
FETCH NEXT
FROM #getid INTO #id, #name
END
CLOSE #getid
DEALLOCATE #getid
Modified to show multiple parameters from the table.
You could do something like this:
create procedure test
as
BEGIN
create table #ids
(
rn int,
id int
)
insert into #ids (rn, id)
select distinct row_number() over(order by id) as rn, id
from table
declare #id int
declare #totalrows int = (select count(*) from #ids)
declare #currentrow int = 0
while #currentrow < #totalrows
begin
set #id = (select id from #ids where rn = #currentrow + 1)
exec stored_proc #varName=#id, #otherVarName='test'
set #currentrow = #currentrow +1
end
END
My prefer solution is Microsoft KB 111401 http://support.microsoft.com/kb/111401.
The link refers to 3 examples:
This article describes various methods that you can use to simulate a cursor-like FETCH-NEXT logic in a stored procedure, trigger, or Transact-SQL batch.
/*********** example 1 ***********/
declare #au_id char( 11 )
set rowcount 0
select * into #mytemp from authors
set rowcount 1
select #au_id = au_id from #mytemp
while ##rowcount <> 0
begin
set rowcount 0
select * from #mytemp where au_id = #au_id
delete #mytemp where au_id = #au_id
set rowcount 1
select #au_id = au_id from #mytemp
end
set rowcount 0
/********** example 2 **********/
declare #au_id char( 11 )
select #au_id = min( au_id ) from authors
while #au_id is not null
begin
select * from authors where au_id = #au_id
select #au_id = min( au_id ) from authors where au_id > #au_id
end
/********** example 3 **********/
set rowcount 0
select NULL mykey, * into #mytemp from authors
set rowcount 1
update #mytemp set mykey = 1
while ##rowcount > 0
begin
set rowcount 0
select * from #mytemp where mykey = 1
delete #mytemp where mykey = 1
set rowcount 1
update #mytemp set mykey = 1
end
set rowcount 0
DECLARE #id INT
DECLARE #name NVARCHAR(100)
DECLARE #getid CURSOR
SET #getid = CURSOR FOR
SELECT table.id,
table.name
FROM table
WHILE 1=1
BEGIN
FETCH NEXT
FROM #getid INTO #id, #name
IF ##FETCH_STATUS < 0 BREAK
EXEC stored_proc #varName=#id, #otherVarName='test', #varForName=#name
END
CLOSE #getid
DEALLOCATE #getid
try this:
declare #i tinyint = 0,
#count tinyint,
#id int,
#name varchar(max)
select #count = count(*) from table
while (#i < #count)
begin
select #id = id, #name = name from table
order by nr asc offset #i rows fetch next 1 rows only
exec stored_proc #varName = #id, #otherVarName = 'test', #varForName = #name
set #i = #i + 1
end
DECLARE #id INT
DECLARE #filename NVARCHAR(100)
DECLARE #getid CURSOR
SET #getid = CURSOR FOR
SELECT top 3 id,
filename
FROM table
OPEN #getid
WHILE 1=1
BEGIN
FETCH NEXT
FROM #getid INTO #id, #filename
IF ##FETCH_STATUS < 0 BREAK
print #id
END
CLOSE #getid
DEALLOCATE #getid