Transaction error in stored procedure that is not using BEGIN or END TRANSACTION - sql-server-2008

I have a stored procedure "let's call it MY_NEW_SP" in which I'm not using BEGIN TRY / BEGIN CATCH. but, when I'm excecuting this SP (MY_NEW_SP), I get the following error:
Msg 266, Level 16, State 2, Procedure <MY_NEW_SP>, Line 132
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
This new stored procedure makes a big select basically, no transactions are made "in the sense of make DML operations on tables (INSERT, DELETE, UPDATE)", but in temp tables "i.e. #tmp".
I'm thinking this transaction error is due I'm using SET XACT_ABORT ON; in other stored procedures, but, I'm not sure.
I follow what it is said here: C. Using TRY...CATCH with XACT_STATE
The basic structure of the stored procedure that uses SET XACT_ABORT ON; is as follows:
IF NOT EXISTS (SELECT * FROM sysobjects WHERE TYPE = 'P' AND NAME = 'PROCEP_NEW_SP' )
BEGIN
EXEC dbo.sp_executesql #statement = N'CREATE PROCEDURE PROCEP_NEW_SP AS'
END
GO
ALTER PROCEDURE PROCEP_NEW_SP
(
#ID_TABLE INT
)
AS
BEGIN
DECLARE #TBL_CONSECUTIVE TABLE ( LOG_CONSECUTIVE INT );
SET XACT_ABORT ON;
BEGIN TRANSACTION
BEGIN TRY
IF ISNULL(#ID_TABLE, -1) = -1
BEGIN
SET #ID_TABLE = 1;
DELETE FROM #TBL_CONSECUTIVE;
INSERT INTO T_BH_LOG_TABLE (ASO_NCODE, CHA_NCODE, TSO_NCODE,
MSO_DACTION_DATE, MSO_CRESULT, MSO_CCAUSE_FAILURE)
OUTPUT INSERTED.MSO_NCODE INTO #TBL_CONSECUTIVE
SELECT #ASO_NCODE, ISNULL(#CHA_NCODE, 1), ISNULL(#TSO_NCODE, 1),
GETDATE() AS MSO_DACTION_DATE, #CST_FAIL_OR_SUC, #CST_GENERIC_MSG;
IF (XACT_STATE()) = 1
BEGIN
COMMIT TRANSACTION;
END
SELECT NULL Id_table, 'Failed' Result_process, 'Parameter (ID_TABLE) is required.' Result_process_message;
RETURN;
END
-- Operation:
UPDATE MY_TABLE
SET NAME = 'SAMPLE'
WHERE ID_TABLE = #ID_TABLE;
IF (XACT_STATE()) = 1
BEGIN
COMMIT TRANSACTION;
END
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
INSERT INTO T_BH_LOG_TABLE (ASO_NCODE, CHA_NCODE, TSO_NCODE,
MSO_DACTION_DATE, MSO_CRESULT, MSO_CCAUSE_FAILURE)
OUTPUT INSERTED.MSO_NCODE INTO #TBL_CONSECUTIVE
SELECT 1 AS ASO_NCODE, 1, 1 As TSO_NCODE,
GETDATE() AS MSO_DACTION_DATE, #CST_FAIL_OR_SUC, #CST_GENERIC_MSG;
SELECT NULL Id_table, 'Failed' Result_process, 'Internal error. See log # (' + CAST(L.LOG_CONSECUTIVE AS NVARCHAR) + ') for more details.' Result_process_message;
FROM #TBL_CONSECUTIVE L;
RETURN;
END CATCH
END;
I really don't know if by using SET XACT_ABORT ON; is causing this kind of error.
Anyone can point me in the right direction for solve this issue?

Related

MySQL Stored Procedure Unexpected Behaviour in certain conditions

I am relatively new to MySQL stored Procedures. I have a stored procedure that works fine in certain conditions and not in other. I'm a bit confused that what causes the error. It is a entity processing SP, based on certain values and conditions it either creates or update entity from data in a staging table.
In conditions when it works fine:
When I only process a single entity.
Works for bulk entries when there is nothing in entity table.
Don't works when:
There are entries in entity table and bulk processing is done. - Error in checking up the already existing entity_id in entity and older entity_id is assigned instead a new one should be created.
(I actually need above scenario to work more frequently than others)
I have tried to keep the code to minimum in order to understand the flow of SP. Please consider all variables as declared. SP might not compile.
CREATE DEFINER=`admin`#`%` PROCEDURE `sp_ent`(test_id int)
BEGIN
-- Move code tables to temp tables
-- Declare all the required variables here
DECLARE counter, len INT;
DECLARE var2 INT;
DECLARE var3 TINYINT(1);
DECLARE var4 DECIMAL(18,4);
DECLARE var5 DATE;
DECLARE var6 VARCHAR(1000);
DECLARE var7 VARCHAR(5000);
DROP TEMPORARY TABLE IF EXISTS `temp_ent`;
IF test_id IS NOT NULL THEN
CREATE TEMPORARY TABLE IF NOT EXISTS `temp_ent` AS (SELECT * FROM `ent_st` WHERE processed = 0 and id=test_id);
ELSE
CREATE TEMPORARY TABLE IF NOT EXISTS `temp_ent` AS (SELECT * FROM `ent_st` WHERE processed = 0);
END IF;
ALTER TABLE `temp_ent` ADD PRIMARY KEY(id);
-- SELECT * FROM `temp_ent`;
DROP TEMPORARY TABLE IF EXISTS `temp_exc`; CREATE TEMPORARY TABLE IF NOT EXISTS `temp_exc` AS (SELECT * FROM `code_exc`);
-- A few more like above
SET counter=1, len=(SELECT COUNT(*) FROM `temp_ent`);
WHILE counter <= len DO
SELECT `id`,var1, var2, var3
INTO v_id,var1, var2, var3
FROM `temp_ent` LIMIT 1;-- WHERE `id` = v_id;
BEGIN
DECLARE insufficient_information CONDITION FOR SQLSTATE '45000';
DECLARE CONTINUE HANDLER FOR insufficient_information SET v_proccessed=1;
SET v_status = CASE
WHEN ... THEN ...
ELSE 'valid'
END;
IF v_status <> 'valid' THEN SIGNAL insufficient_information; END IF;
SELECT `entity_id`,`entity`
INTO v_underlying_entity_id,v_underlying_entity_symbol
FROM `entity` WHERE `entity_id` = v_underlying OR `entity` = v_underlying_entity;
SET v_underlying_entity_symbol = COALESCE(v_underlying_entity_symbol,v_underlying_entity);
SET v_entity = (
CASE
WHEN ... THEN ...
WHEN ... THEN ...
.
.
WHEN ... THEN ...
END
);
SELECT `entity_id`,`entity`
INTO v_entity_id_check,v_entity_check
FROM entity WHERE `entity`=v_entity and `exc`=v_exc;
-- SELECT v_entity_id_check,v_entity_check,v_entity,v_exc;
IF v_entity_check IS NULL THEN
-- SELECT 'Create New Entity and Add';
SET v_entity_id = COALESCE((SELECT MAX(`entity_id`) FROM entity),0) + 1;
SET new_entity = 1;
ELSE
-- SELECT 'Entity Already Present';
SET v_entity_id = v_entity_id_check;
SET new_entity = 0;
END IF;
-- SELECT v_entity_id, v_entity, new_entity;
SET v_name = UPPER(COALESCE(v_name,v_entity));
-- UPDATE entity and underlying/derivatives table
IF new_entity = 1 THEN
-- Insert in respective tables
IF ... THEN
-- SELECT 'Entity Added',v_entity; -- Underlying Entity
INSERT INTO `entity_underlying` (`entity_id`,`entity`,`name`,`exc`,`seg`,`ins`,`isin`,`fo_yn`,`tick`,`lot_size`,`active_yn`)
VALUES (v_entity_id,v_entity,v_name,v_exc,v_seg,v_ins,COALESCE(v_isin,''),COALESCE(v_fo_yn,0),COALESCE(v_tick,0.05),COALESCE(v_lot_size,1),1);
ELSEIF ... THEN
-- SELECT 'Entity Added',v_entity; -- Derivative Entity
INSERT INTO `entity_derivatives` (`entity_id`,`entity`,`name`,`underlying`,`exc`,`seg`,`ins`,`ser`,`isin`,`strike`,`tick`,`lot_size`,`expiry`,`ex_ty`,`active_yn`)
VALUES (v_entity_id,v_entity,v_name,COALESCE(v_underlying_entity_id,-1),v_exc,v_seg,v_ins,COALESCE(v_ser,''),v_isin,COALESCE(v_strike),COALESCE(v_tick,0.05),COALESCE(v_lot_size,-1),v_expiry,v_ex_ty,1);
END IF;
-- Insert in final table
INSERT INTO entity(`entity_id`,`entity`,`exc`,`active_yn`)
VALUES (v_entity_id,v_entity,v_exc,1);
SET v_status='Entity Added';
ELSE
SET v_status='Not a New Entity';
END IF;
END;
UPDATE `ent_st` SET `entity_id`=v_entity_id,`processed`=1,`status`=v_status WHERE id=v_id;
DELETE FROM `temp_ent` WHERE id=v_id;
SET counter = counter +1;
COMMIT;
END WHILE;
END
Any help is highly appreciated. Thanks in advance.

error in stored procedure ...into keyword

Two tables Borrower(rollno,name,bookissue_date) and Fine(rollno,name,amount)
delimiter //
create procedure student( in roll_no int,in Nameofbook varchar(40))
begin
declare Dateofiss1 date;
Declare cur cursor for
select Dateofiss from Borrower where Roll_no = roll into Dateofiss1;
OPEN cur;
fetch cur into Dateofiss1
if(datediff(sysdate(),Dateofiss1)<15) then varchar(20))
update Borrower set status='R'where Roll_no=roll_no
elseif(datediff(sysdate(),Dateofiss1)>=15)and datediff (sysdate(),Dateofiss1<30)
SET FINEAMOUNT=5*(datediff(sysdate(),Dateofiss1)-15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
update.borrower set status='R' where Roll_no='rollno';
elseif (datediff(sysdate(),Dateofiss1)>30)
SET FINEAMOUNT=50*(datediff(sysdate(),Dateofiss1)-15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
update.borrower set status='R' where Roll_no='rollno';
close cur;
end if
select * from Borrower;
elect * from Fine;
end
You have a number of syntax errors.
You have an extraneous varchar(20)) in the first if statement.
You're missing THEN in the ELSEIF statements.
You wrote update.borrower instead of update borrower.
You have roll_no in quotes in some of your update statements.
The roll_no parameter is the same as a table column, since column names are case-insensitive. The condition where Roll_no = roll_no will match every row because of this. Give the parameter a different name.
In a SELECT, the INTO clause goes after FROM, not at the end.
There's no need to use a cursor if you're using SELECT INTO. Just execute the query and it will set the variable.
You can also simplify the code by putting the date difference in a variable, so you don't have to repeatedly calculate it. And in the ELSEIF you don't need to test >= 15, since you'll only get there if the < 15 test failed.
The UPDATE statement is the same in all conditions, so it doesn't need to be in the IF at all.
delimiter //
create procedure student( in p_roll_no int,in Nameofbook varchar(40))
begin
declare Dateofiss1 date;
declare diff INT;
select Dateofiss from Borrower into Dateofiss1 where Roll_no = p_roll_no;
OPEN cur;
SET diff = datediff(sysdate(),Dateofiss1)
IF diff BETWEEN 15 AND 29 THEN
SET FINEAMOUNT= 5 * (diff - 15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
else
SET FINEAMOUNT= 50 * (diff - 15)
insert into Fine(Roll_no,Date,amount)values(rollno,sysdate,fineamount);
end if
update Borrower set status='R'where Roll_no=p_roll_no
select * from Borrower;
select * from Fine;
end

declaring variable inside mysql stored procedure

we are trying to declare a variable inside mysql stored procedure that has transaction implemented in it. but it seems to be giving a syntax error :
following is the syntax of the stored procedure:
CREATE PROCEDURE `sp_MarkAppointmentRefferal`(
p_AppId bigint,
p_NewLocation bigint,
p_userId bigint,
p_ReferralReason varchar(500),
p_NewLocationName varchar(100)
)
begin
declare v_OldLocation int default 0;
set v_OldLocation = (select LocationId FROM appointments where iAppID = p_AppId limit 1 );
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
select -1;
END;
START TRANSACTION;
update table
set is_referred = 1,
referred_timestamp = now(),
referral_reason = p_ReferralReason
where iAppID = p_AppId
limit 1;
-- create a new appointment for the new referred location..
insert into appointments
(vAppName, vAppType, dAppDate, vCell, iPatID, iAppStatus, iuserid, iActive,
dInsertDate, iHSID, daily_ticket_no, LocationId, visit_id, encounter_id, ReferredFrom,ReferredOPDName, opd_name )
select vAppName, vAppType, now(), vCell, iPatID, iAppStatus, p_userId,
1, now(), iHSID, fn_GenerateNextAppointmentTicket(now(),p_NewLocation) , p_NewLocation, visit_id, encounter_id+1,
(select LocationId FROM appointments where iAppID = p_AppId limit 1),
(select OPD_Name FROM appointments where iAppID = p_AppId limit 1), p_NewLocationName
FROM appointments
where iAppID = p_AppId limit 1;
select LAST_INSERT_ID();
COMMIT;
end;
the syntax checker is saying that declare command is not valid here.
have also tried to place this inside the transaction clause and similar error shows up ..
any help is appreciated..
All declare statements should be at the top of the stored procedure body. Moving DECLARE EXIT HANDLER before the SET statement should fix the problem.

Stored Procedure Error Handling - Clean up but return original error

I'm writing a stored procedure that needs to clean up some data if an insert fails. I'd like it to perform the clean up, but return the original error if this insert fails (primarily for logging as I want to see exactly why the insert failed). Basically like a throw; in C#. Is there a simple way to do this?
BEGIN TRY
Insert into table (col1) values ('1")
END TRY
BEGIN CATCH
--do clean up here
--then throw original error
END TRY
Is this feasible/good practice? In the application code that calls the proc, I'm handling the error from an application standpoint, but the clean up statements seem to better fit inside the proc.
I usually do something like this:
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[procedure_name]') AND ObjectProperty(id, N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[procedure_name]
GO
CREATE PROCEDURE [dbo].[procedure_name]
(
#param1 VARCHAR(100)
,#param2 INT
)
AS
/*
*******************************************************************************
<Name>
[procedure_name]
</Name>
<Purpose>
[Purpose]
</Purpose>
<Notes>
</Notes>
<OutsideRef>
Called From: [Called From]
</OutsideRef>
<ChangeLog>
Change No: Date: Author: Description:
_________ ___________ __________ _____________________________________
001 [DATE] [YOU] Created.
</ChangeLog>
*******************************************************************************
*/
BEGIN
SET NOCOUNT ON
SET XACT_ABORT OFF -- Allow procedure to continue after error
-- *****************************************
-- Parameter string. Used for error handling
-- *****************************************
DECLARE #ErrorNumber INT
,#ErrorMessage VARCHAR(400)
,#ErrorSeverity INT
,#ErrorState INT
,#ErrorLine INT
,#ErrorProcedure VARCHAR(128)
,#ErrorMsg VARCHAR(2000)
,#NestedProc BIT = 1
,#Params VARCHAR(255); -- String representing parameters, make it an appropriate size given your parameters.
--Be Careful of the CONVERT here, GUIDs (if you use them) need 36 characters, ints need 10, etc.
SET #Params = ''
+ CHAR(13) + '#param1 = ' + COALESCE(CONVERT(VARCHAR(100), #param1), 'NULL')
+ CHAR(13) + '#param2 = ' + COALESCE(CONVERT(VARCHAR(10), #param2), 'NULL')
BEGIN TRY
--If you're using transactions, and want an 'all or nothing' approach, use this so that
--you only start a single transaction in the outermost calling procedure (or handle
--that through your application layer)
IF ##TRANCOUNT = 0
BEGIN
SET #NestedProc = 0
BEGIN TRANSACTION
END
INSERT INTO [TABLE]
(
COL1
,COL2
)
VALUES
(
#param1
,#param2
);
--Commit the transaction if this is the outtermost procedure and if there is a transaction to rollback.
IF ##TRANCOUNT > 0 AND #NestedProc = 0
BEGIN
COMMIT TRANSACTION
END
END TRY
BEGIN CATCH
--Roll back the transaction if this is the outtermost procedure and if there is a transaction to rollback.
IF ##TRANCOUNT > 0 AND #NestedProc = 0
BEGIN
ROLLBACK TRANSACTION
END
-- Execute the error retrieval routine.
SELECT
#ErrorNumber = ERROR_NUMBER(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorProcedure = ERROR_PROCEDURE(),
#ErrorState = ERROR_STATE(),
#ErrorLine = ERROR_LINE(),
#ErrorMessage = ERROR_MESSAGE();
SET #ErrorMsg = 'Error Number : ' + CAST(#ErrorNumber AS VARCHAR(5)) + CHAR(13)
+ 'Procedure Name : ' + #ErrorProcedure + CHAR(13)
+ 'Procedure Line : ' + CAST(#ErrorLine AS VARCHAR(5)) + CHAR(13)
+ 'Error Message : ' + #ErrorMessage + CHAR(13)
+ 'Parameters : ' + CHAR(13) + #Params + CHAR(13);
--Raise the exception.
RAISERROR (#ErrorMsg, #ErrorSeverity, #ErrorState);
END CATCH
END
GO
This type of procedure allows you to have nesting procs with transactions (so long as the desired effect is that if an error is thrown anywhere, you'll eventually throw back up to the outer procedure and then rollback). A pretty important scenario that I don't think this template handles is the case where an error that's severe enough to completely kill the procedure is thrown. Perhaps someone else could chime in on that front.
Assuming we are using a table MyTable defined as
CREATE TABLE [dbo].[MyTable](
[Col1] [int] NOT NULL
) ON [PRIMARY]
The I would use a procedure similar to the one below.
In the case of insert failure the code will enter the Catch block where a check for the error number/message can be perform and assigned.
Once assigned the transaction can be rolled back and the error number/message returned.
You may need to change the SQL Server Error number in the RAISERROR error line depending on what you are doing.
CREATE PROCEDURE [dbo].[zTestProc]
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#LocalError INT,
#ErrorMessage VARCHAR(4000)
BEGIN TRY
BEGIN TRANSACTION TestTransaction
Insert into MyTable(col1) values ('01/01/2002')
COMMIT TRANSACTION TestTransaction
END TRY
BEGIN CATCH
SELECT #LocalError = ERROR_NUMBER(),
#ErrorMessage = ERROR_MESSAGE()
IF( XACT_STATE()) <>0
BEGIN
ROLLBACK TRANSACTION TestTransaction
END
RAISERROR ('TestSP: %d: %s', 16, 1, #LocalError, #ErrorMessage) ;
RETURN(0)
END CATCH
END
Try the following snippet.
DECLARE #errNum int
DECLARE #rowCount int
BEGIN TRY
INSERT INTO [TABLE] (COL1) VALUES ('1")
END TRY
BEGIN CATCH
SET #errNum = ##ERROR
SET #rowCount = ##ROWCOUNT
RAISEERROR(#errNum)
END CATCH

Stored Procedure (mysql) fails with "can't return a result set in the given context"

I'm trying to get this SP to return (leave) if some conditions fails and so forth.
This code validates and it saves the procedure, but when I call the procedure with:
CALL ACH_Deposit(30027616,3300012003,200.00,"USD", "127.0.0.1")
It fails with error: "Procedure can't return a result set in the given context"
Does anyone have any idea on what the error is?
Procedure code:
CREATE DEFINER=`redpass_web_urs`#`%` PROCEDURE `ACH_Deposit`(
IN __Account_ID BIGINT,
IN __To_Bank_Account BIGINT,
IN __Amount DECIMAL(10,2),
IN __Currency CHAR(3),
IN __IP_Address VARCHAR(50)
)
COMMENT 'Makes a ACH deposit'
BEGIN
-- Declare Account Parameters
DECLARE _Account_Status INT;
DECLARE __Transaction_ID INT;
DECLARE _Account_Type INT DEFAULT 0;
DECLARE _Fee INT;
SELECT
Account_Status AS _Account_Status,
Account_Type AS _Account_Type
FROM Account_Users
WHERE Account_ID = __Account_ID;
main: BEGIN
-- Step 1, is account active ?
IF _Account_Status <> 1 THEN
-- Account must be active (not restricted, closed etc)
SELECT Response_Code, Description FROM ResponseCodes WHERE Response_Code = 106;
LEAVE main; -- Here we die..
END IF;
-- Step 2, Calculate the FEE (Loading Funds with ACH)
IF _Account_Type = 1 THEN
-- Personal Account
SET _Fee = (SELECT Fee_Template_Personal_1 FROM Fees WHERE Fee_Type_ID = __Fee_Type_ID);
ELSE
-- Business Account
SET _Fee = (SELECT Fee_Template_Business_1 FROM Fees WHERE Fee_Type_ID = __Fee_Type_ID);
END IF;
-- Step 3, Check that Fee is not bigger then the actual amount
IF _Fee > __Amount THEN
SELECT Response_Code, Description FROM ResponseCodes WHERE Response_Code = 108;
LEAVE main; -- Here we die..
END IF;
-- If we come here, we can make the transactions
INSERT INTO Bank_Transaction
(Bank_Account_ID
,Transaction_Type
,Amount
,IP_Address
,Pending)
VALUES
(__To_Bank_Account
,11
,__Amount
,__IP_Address
,1); -- Reserverade pengar
-- Transaction ID
SET __Transaction_ID = (SELECT LAST_INSERT_ID());
-- Deduct the Fee
INSERT INTO Bank_Transaction
(Bank_Account_ID
,Transaction_Type
,Amount
,Fee_Type_ID
,Fee_Transaction_ID)
VALUES
(__To_Bank_Account
,4
,-__Fee
,21
,__Transaction_ID);
END main;
SELECT Response_Code, Description, __Transaction_ID AS Transaction_ID
FROM ResponseCodes
WHERE Response_Code = 1;
END
To retrieve multiple resultsets from the stored procs, you should use a client which supports multiple queries.
If you use PHP, use MySQLi extension and call the procedure using mysqli_multi_query.
MySQL extension is only able to retrieve the first recordset returned by the proc. To be able to use ti, you should set CLIENT_MULTI_RESULTS (decimal 131072) in the parameter $client_flags to mysql_connect