Incorrect syntax into IF... ELSE clause - tsql

I have a really simple query like:
BEGIN TRY
BEGIN TRAN;
DECLARE #CurrentPassword VARCHAR(255) = (SELECT TOP 1 [Password]
FROM Employee
WHERE #EmpGuid = EmpGuid)
IF (#Password = #CurrentPassword)
UPDATE [Employee]
SET [Password] = #NewPassword
WHERE #EmpGuid = EmpGuid
SELECT 1;
ELSE
SELECT 2;
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
END CATCH
But I really don't know why in my else clause I get
Incorrect syntax near 'ELSE'
What am I doing wrong? Regards

You you want to have more than one statement after the IF, you must use a BEGIN .... END block - like this:
BEGIN TRY
BEGIN TRAN;
DECLARE #CurrentPassword VARCHAR(255) = (SELECT TOP 1 [Password]
FROM Employee
WHERE #EmpGuid = EmpGuid)
IF (#Password = #CurrentPassword)
BEGIN --- you need a *BEGIN* here!!!
UPDATE [Employee]
SET [Password] = #NewPassword
WHERE #EmpGuid = EmpGuid
SELECT 1;
END --- and the *END* for your new *BEGIN*
ELSE
SELECT 2;
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
END CATCH

Use explicitly begin . . end
BEGIN TRY
BEGIN TRAN
DECLARE #CurrentPassword VARCHAR(255) = (SELECT TOP 1 [Password]
FROM Employee
WHERE #EmpGuid = EmpGuid)
IF (#Password = #CurrentPassword)
BEGIN
UPDATE [Employee]
SET [Password] = #NewPassword
WHERE #EmpGuid = EmpGuid
SELECT 1
END
ELSE
BEGIN
SELECT 2
END
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
END CATCH

Related

Procedure for deleting records referenced from other tables in postgresql

I am trying to create a Stored Procedure in Postgresql that allow me to delete foreign keys from other tables without using the DELETE CASCADE when creating tables but gives me an error that one id is still referenced in other table. This is the code i have tried:
CREATE OR REPLACE PROCEDURE sp_delete_restaurante_msg
( _id_restaurante integer,
_id_local integer,
_nome_restaurante varchar(100),
INOUT _msg varchar(100),
INOUT _msg1 varchar(100) )
LANGUAGE plpgsql
AS $$
DECLARE
loc_cursor CURSOR FOR SELECT local.id_local FROM local WHERE local.id_local = _id_local FOR UPDATE;
ementa_cursor CURSOR FOR SELECT ementa.id_restaurante FROM ementa WHERE ementa.id_restaurante = _id_restaurante FOR UPDATE;
func_cursor CURSOR FOR SELECT funcionario.id_restaurante FROM funcionario WHERE funcionario.id_restaurante = _id_restaurante FOR UPDATE;
apont_loc RECORD;
apont_em RECORD;
apont_func RECORD;
BEGIN
IF EXISTS(SELECT l.id_local, rest.id_restaurante FROM local l JOIN restaurante rest ON l.id_local = rest.id_local WHERE rest.id_restaurante = _id_restaurante AND
l.id_local = _id_local) THEN
DELETE FROM restaurante WHERE restaurante.id_restaurante = _id_restaurante;
COMMIT;
ELSE
IF (SELECT COUNT(*) FROM local WHERE local.id_local = _id_local) > 0 THEN
OPEN loc_cursor;
LOOP
FETCH NEXT FROM loc_cursor INTO apont_loc;
EXIT WHEN NOT FOUND;
IF apont_loc.id_local = _id_local THEN
DELETE FROM local WHERE CURRENT OF loc_cursor;
COMMIT;
END IF;
END LOOP;
CLOSE loc_cursor;
COMMIT;
ELSIF (SELECT COUNT(*) FROM ementa WHERE ementa.id_restaurante = _id_restaurante) > 0 THEN
OPEN ementa_cursor;
LOOP
FETCH NEXT FROM ementa_cursor INTO apont_em;
EXIT WHEN NOT FOUND;
IF apont_em.id_restaurante = _id_restaurante THEN
DELETE FROM ementa WHERE CURRENT OF ementa_cursor;
COMMIT;
END IF;
END LOOP;
CLOSE ementa_cursor;
COMMIT;
ELSIF (SELECT COUNT(*) FROM funcionario WHERE funcionario.id_restaurante = _id_restaurante) > 0 THEN
OPEN func_cursor;
LOOP
FETCH NEXT FROM func_cursor INTO apont_func;
EXIT WHEN NOT FOUND;
IF apont_func.id_restaurante = _id_restaurante THEN
DELETE FROM funcionario WHERE CURRENT OF func_cursor;
COMMIT;
END IF;
END LOOP;
CLOSE func_cursor;
ELSE
RAISE NOTICE 'Nao existe mais tabelas ligadas';
END IF;
END IF;
IF(SELECT COUNT(*) FROM restaurante WHERE restaurante.id_restaurante = _id_restaurante) = 0 THEN
RAISE EXCEPTION '%', _msg;
ROLLBACK;
END IF;
IF _nome_restaurante IS NULL OR _nome_restaurante = '' THEN
RAISE EXCEPTION '%', _msg1;
ROLLBACK;
END IF;
END;
$$

Using Cursors to return a specified number of results

I have a cursor in my SQL 2008R2 database. This cursor takes a list of data, parses each row the data, and then runs the parsed data row through a stored procedure.
DECLARE ExecsDataCursor CURSOR FAST_FORWARD FOR
SELECT TOP (#GuessListSize)
ExecutiveId,
CompanyExecutiveId,
Email,
CompanyId,
#EmailPatternID EmailPatternID,
ExecNameForSorting
FROM
CompanyExecutive
WHERE
CurrentlyWithCompany = 1
AND
Email IS NULL
AND
CompanyExecutiveId NOT IN
(
SELECT CompanyExecutiveId
FROM ExecsData_ExecutiveCandidates
WHERE EmailPatternID = #EmailPatternID
)
ORDER BY
CompanyExecutiveId
OPEN ExecsDataCursor
DECLARE
#ExecutiveId INT,
#CompanyExecutiveId INT,
#Email NVARCHAR(255),
#CompanyId INT,
#EmailPatternID_ForCursor TINYINT,
#ExecName NVARCHAR(255)
FETCH NEXT FROM ExecsDataCursor
INTO
#ExecutiveId ,
#CompanyExecutiveId ,
#Email ,
#CompanyId ,
#EmailPatternID_ForCursor,
#ExecName
DECLARE
#FirstName NVARCHAR(50) = '',
#MiddleName NVARCHAR(50) = '',
#LastName NVARCHAR(50) = '',
#ExampleEmail NVARCHAR(255),
#Domain NVARCHAR(50) = ''
WHILE ##FETCH_STATUS = 0
BEGIN
IF (SELECT COUNT(*) FROM dbo.splitString(#ExecName,' ')) = 1
BEGIN
SELECT #FirstName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 1
END
IF (SELECT COUNT(*) FROM dbo.splitString(#ExecName,' ')) = 2
BEGIN
SELECT #FirstName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 1
SELECT #LastName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 2
END
IF (SELECT COUNT(*) FROM dbo.splitString(#ExecName,' ')) >= 3
BEGIN
SELECT #FirstName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 1
SELECT #MiddleName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 2
SELECT #LastName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = (SELECT MAX(id) FROM dbo.splitString(#ExecName,' '))
END
SELECT #ExampleEmail = MAX(Email) FROM CompanyExecutive WHERE Email IS NOT NULL AND CompanyId = #CompanyId
SELECT #Domain = SUBSTRING(#ExampleEmail, CHARINDEX('#', #ExampleEmail), LEN(#ExampleEmail))
IF #EmailPatternID = 1 BEGIN BEGIN TRY EXEC ExecsData_Guess_fnamelname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 2 BEGIN BEGIN TRY EXEC ExecsData_Guess_fnamedotlname#domain_DataMe #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 3 BEGIN BEGIN TRY EXEC ExecsData_Guess_finitiallname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 4 BEGIN BEGIN TRY EXEC ExecsData_Guess_finitialdotlname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 5 BEGIN BEGIN TRY EXEC ExecsData_Guess_finitial_lname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 6 BEGIN BEGIN TRY EXEC ExecsData_Guess_fname_lname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 7 BEGIN BEGIN TRY EXEC ExecsData_Guess_fnamelinitial#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 8 BEGIN BEGIN TRY EXEC ExecsData_Guess_lnamefinitial#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 9 BEGIN BEGIN TRY EXEC ExecsData_Guess_fname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 10 BEGIN BEGIN TRY EXEC ExecsData_Guess_lname_fname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 11 BEGIN BEGIN TRY EXEC ExecsData_Guess_lname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 12 BEGIN BEGIN TRY EXEC ExecsData_Guess_finitiallinitial#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
IF #EmailPatternID = 13 BEGIN BEGIN TRY EXEC ExecsData_Guess_lnamedotfname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID END TRY BEGIN CATCH END CATCH END
FETCH NEXT FROM ExecsDataCursor
INTO
#ExecutiveId ,
#CompanyExecutiveId ,
#Email ,
#CompanyId ,
#EmailPatternID_ForCursor,
#ExecName
END
CLOSE ExecsDataCursor
DEALLOCATE ExecsDataCursor
This works very well, at least by my expectations. The cursor processes 8000 rows in about 19 seconds. The 8000 rows are specified by user input, which feeds to the #GuessListSize parameter. However, the parsed data is not always processed correctly, which is to be expected. Hence, the try-catch code. We don't really need the cursor to do anything with the failed stored procedure executions. We started tracking them in a separate table so we can figure out how to better process these data points in the future.
The issue with this is that the cursor now will not return a full list. We are hoping for a list of a size specified by the user. So the cursor pulls the list of the specified size, as it should, but some of those entries may not be properly processed by the stored procedures, returning a list less than the specified size.
So the next thing I tried was a WHILE loop. Now, the WHILE loop worked fine. It returned all of the rows the user requested. However, it took nearly 30 minutes to run through the same size of data set. This is obviously unacceptable.
DECLARE
#ExecutiveId INT,
#CompanyExecutiveId INT,
#Email NVARCHAR(255),
#CompanyId INT,
#EmailPatternID_ForCursor TINYINT,
#ExecName NVARCHAR(255)
DECLARE
#FirstName NVARCHAR(50) = '',
#MiddleName NVARCHAR(50) = '',
#LastName NVARCHAR(50) = '',
#ExampleEmail NVARCHAR(255),
#Domain NVARCHAR(50) = '',
#Counter SMALLINT = 0
--WHILE ##FETCH_STATUS = 0
WHILE #Counter < #GuessListSize
BEGIN
SELECT #CompanyExecutiveId =
MIN(CompanyExecutiveID)
FROM CompanyExecutive
WHERE CurrentlyWithCompany = 1 AND Email IS NULL
AND
CompanyExecutiveId NOT IN
(SELECT CompanyExecutiveId FROM ExecsData_ExecutiveCandidates WHERE EmailPatternID = #EmailPatternID)
AND
CompanyExecutiveID NOT IN
(SELECT CompanyExecutiveId FROM ExecsData_Errors)
SELECT
#ExecutiveId = ExecutiveId,
#Email = Email,
#CompanyId = CompanyId,
#EmailPatternID_ForCursor = #EmailPatternID,
#ExecName = ExecNameForSorting
FROM
CompanyExecutive
WHERE
CompanyExecutiveId = #CompanyExecutiveId
IF (SELECT COUNT(*) FROM dbo.splitString(#ExecName,' ')) = 1
BEGIN
SELECT #FirstName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 1
END
IF (SELECT COUNT(*) FROM dbo.splitString(#ExecName,' ')) = 2
BEGIN
SELECT #FirstName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 1
SELECT #LastName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 2
END
IF (SELECT COUNT(*) FROM dbo.splitString(#ExecName,' ')) >= 3
BEGIN
SELECT #FirstName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 1
SELECT #MiddleName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = 2
SELECT #LastName = Data FROM dbo.splitString(#ExecName,' ') WHERE id = (SELECT MAX(id) FROM dbo.splitString(#ExecName,' '))
END
SELECT #ExampleEmail = MAX(Email) FROM CompanyExecutive WHERE Email IS NOT NULL AND CompanyId = #CompanyId
SELECT #Domain = SUBSTRING(#ExampleEmail, CHARINDEX('#', #ExampleEmail), LEN(#ExampleEmail))
IF #EmailPatternID = 1 BEGIN BEGIN TRY EXEC ExecsData_Guess_fnamelname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 2 BEGIN BEGIN TRY EXEC ExecsData_Guess_fnamedotlname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 3 BEGIN BEGIN TRY EXEC ExecsData_Guess_finitiallname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 4 BEGIN BEGIN TRY EXEC ExecsData_Guess_finitialdotlname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 5 BEGIN BEGIN TRY EXEC ExecsData_Guess_finitial_lname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 6 BEGIN BEGIN TRY EXEC ExecsData_Guess_fname_lname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 7 BEGIN BEGIN TRY EXEC ExecsData_Guess_fnamelinitial#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 8 BEGIN BEGIN TRY EXEC ExecsData_Guess_lnamefinitial#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 9 BEGIN BEGIN TRY EXEC ExecsData_Guess_fname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 10 BEGIN BEGIN TRY EXEC ExecsData_Guess_lname_fname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 11 BEGIN BEGIN TRY EXEC ExecsData_Guess_lname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 12 BEGIN BEGIN TRY EXEC ExecsData_Guess_finitiallinitial#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
IF #EmailPatternID = 13 BEGIN BEGIN TRY EXEC ExecsData_Guess_lnamedotfname#domain_DataME #ExecutiveID ,#CompanyExecutiveID ,#FirstName,#MiddleName ,#LastName ,#Domain ,#CompanyID; SET #Counter = #Counter + 1; END TRY BEGIN CATCH INSERT INTO ExecsData_Errors (CompanyExecutiveID,EmailPatternID) VALUES (#CompanyExecutiveId,#EmailPatternID) END CATCH END
END
Because the Cursor is designed to chew through a pre-defined list of data, I'm not sure how to make said cursor "dynamic" and return the user-specified number of results regardless of errors. The WHILE loop has the "counter" only increase if the SP executes without hitting the CATCH block, but I don't know how to integrate that into the Cursor, or if I even can.
Is there something obvious I'm missing here?
(As requested, the INSERT SQL SPs)
ALTER PROCEDURE [dbo].[ExecsData_Guess_fname#domain_DataMe]
(
#ExecutiveID int,
#CompanyExecutiveID int,
#FirstName nvarchar(50),
#MiddleName nvarchar(50),
#LastName nvarchar(50),
#DomainName nvarchar(255),
#CompanyID int
)
AS
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
DECLARE #GUESS nvarchar(255)
DECLARE #FirstInitial nvarchar(1)
DECLARE #MiddleInitial nvarchar(1)
DECLARE #LastInitial nvarchar(1)
set #FirstInitial = SUBSTRING(#FirstName, 1, 1)
set #MiddleInitial = SUBSTRING(#MiddleName, 1, 1)
set #LastInitial = SUBSTRING(#LastName, 1, 1)
--*****Example******
--FirstName = Andy,
--Middle Name = Xanadu,
--Last Name = Farag,
--Domain = #umphreys.com
--******************
--ex. andy#umphreys.com
set #GUESS = LTRIM(#FirstName)+ #DomainName
EXEC ExecsData_InsertEmailGuessByExec_DataMe
#ExecutiveID,
#CompanyExecutiveID,
#GUESS,
#CompanyID,
9
RETURN (##ERROR)
ALTER PROCEDURE [dbo].[ExecsData_InsertEmailGuessByExec_DataMe]
(
#ExecutiveID int,
#CompanyExecutiveID int,
#EmailAddress nvarchar(50),
#CompanyID int,
#EmailPatternID tinyint
)
AS
BEGIN
INSERT ExecsData_ExecutiveCandidates
(
ExecutiveID,
CompanyExecutiveID,
EmailAddress,
CompanyID,
EmailPatternID,
GuessTimestamp
)
VALUES
(
#ExecutiveID,
#CompanyExecutiveID,
#EmailAddress,
#CompanyID,
#EmailPatternID,
CURRENT_TIMESTAMP
)
END
Per RBarryYoung's suggestion, I opted to look at the actual SP inserting process a bit. One of the problems I found was that many of our Execs were not pulling a domain to concatenate into an email address. Expanding the areas where the script could look for domain information has improved the process to near-user-specified completion. While it's still not perfect, it's a step in the right direction.
As for finding ways to dynamically force a cursor to pull a specific number of rows, I'm thinking I might nest this particular SP in a second SP that uses the WHILE loop. So basically, while the list size is less than the user-specified list size, it will re-execute the insertion SP. That might work. If it works, I'll re-edit and post this as the solution.
As suggested in the last paragraph of the post, I ended up using nested SPs. The outermost SP runs a WHILE loop that keeps track of however many execs were requested. It then runs the generation SP with the specified number of execs. If the returned number is less than the requested number, it remains in the WHILE loop.
The errors in the generation SP are being logged to be reviewed by our data team.

T-SQL: control flow in case of errors

Sup guys, is it possible for INSERT or UPDATE to throw an exception that would stop the procedure? I'm in a little bit of a pickle, because i've hanging transactions in what seems to be a bullet proof code.
BEGIN TRANSACTION;
SET #sSystemLogDataId = CONVERT(NCHAR(36), NEWID());
INSERT INTO crddata.crd_systemlogdata (systemdataid,systemlogid,userid,
actiondatetime,actionstate)
VALUES(#sSystemLogDataId,#inSystemLogId,#sUserId,GETDATE(),#nActionState);
SET #nError = ##ERROR;
IF (1 = #nChangeMassprintTaskStatus) AND (0 = #nError)
BEGIN
UPDATE crddata.crd_massprinttasks SET massprinttaskstatus=#nMassprintTaskStatus
WHERE massprinttaskid = #inMassprintTaskId;
SET #nError = ##ERROR;
END
IF (#MassprintTaskType <> 1) AND (27 = #nActionState) AND (0 = #nError)
BEGIN
UPDATE crddata.crd_massprinttasks SET massprinttasktype=1
WHERE massprinttaskid = #inMassprintTaskId;
SET #nError = ##ERROR;
END
IF 0 = #nError
BEGIN
COMMIT TRANSACTION;
END
ELSE
BEGIN
ROLLBACK TRANSACTION;
END
Halp, anyone?
Without TRY/CATCH, this is not bullet proof.
Errors can be batch aborting (eg datatype conversions or errors thrown from triggers) which means ROLLBACK does not run.
You have to use TRY/CATCH and I always use SET XACT_ABORT ON too
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
SET #sSystemLogDataId = CONVERT(NCHAR(36), NEWID());
INSERT INTO crddata.crd_systemlogdata (systemdataid,systemlogid,userid,
actiondatetime,actionstate)
VALUES(#sSystemLogDataId,#inSystemLogId,#sUserId,GETDATE(),#nActionState);
IF (1 = #nChangeMassprintTaskStatus)
BEGIN
UPDATE crddata.crd_massprinttasks SET massprinttaskstatus=#nMassprintTaskStatus
WHERE massprinttaskid = #inMassprintTaskId;
END
IF (#MassprintTaskType <> 1) AND (27 = #nActionState)
BEGIN
UPDATE crddata.crd_massprinttasks SET massprinttasktype=1
WHERE massprinttaskid = #inMassprintTaskId;
END
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 --may already be rolled back by SET XACT_ABORT or a trigger
ROLLBACK TRANSACTION;
RAISERROR [rethrow caught error using ERROR_NUMBER(), ERROR_MESSAGE(), etc]
END CATCH
Mandatory background reading is Erland Sommarskog's "Error Handling in SQL 2005 and Later": we'll test you on it later...
Create a trigger that will raise an exception if insert/update is not correct
example:
create table t (id int)
go
create trigger tr on t
for insert
as
if exists(select 1 from inserted where id = 0)
raiserror('id is not valid', 16, 1)
go
insert t select 1
select ##error
insert t select 0
select ##error

first attempt at a sproc transaction

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.

Have I to count transactions before rollback one in catch block in T-SQL?

I have next block in the end of each my stored procedure for SQL Server 2008
BEGIN TRY
BEGIN TRAN
-- my code
COMMIT
END TRY
BEGIN CATCH
IF (##trancount > 0)
BEGIN
ROLLBACK
DECLARE #message NVARCHAR(MAX)
DECLARE #state INT
SELECT #message = ERROR_MESSAGE(), #state = ERROR_STATE()
RAISERROR (#message, 11, #state)
END
END CATCH
Is it possible to switch CATCH-block to
BEGIN CATCH
ROLLBACK
DECLARE #message NVARCHAR(MAX)
DECLARE #state INT
SELECT #message = ERROR_MESSAGE(), #state = ERROR_STATE()
RAISERROR (#message, 11, #state)
END CATCH
or just
BEGIN CATCH
ROLLBACK
END CATCH
?
Actually, I never start a new transaction if I'm already in one.
This deals with nested stored procs, distributed TXNs and TransactionScope
Remember, there is no such thing as a nested transaction in SQL Server anyway.
DECLARE #StartTranCount int
BEGIN TRY
SET #StartTranCount = ##TRANCOUNT
IF #StartTranCount = 0 BEGIN TRAN
-- my code
IF #StartTranCount = 0 COMMIT TRAN
END TRY
BEGIN CATCH
IF #StartTranCount = 0 AND ##trancount > 0
BEGIN
ROLLBACK TRAN
DECLARE #message NVARCHAR(MAX)
DECLARE #state INT
SELECT #message = ERROR_MESSAGE(), #state = ERROR_STATE()
RAISERROR (#message, 11, #state)
END
/*
or just
IF #StartTranCount = 0 AND ##trancount
ROLLBACK TRAN
*/
END CATCH
You need to check that there is a transaction in scope before trying to rollback.
You can use the following:
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
This will rollback the transaction, but no error will be reported back to your application.
Check MSDN for more info.