I have a stored procedure that has 2 flaws:
If there is an error in the stored procedure, it keeps executing. I want the stored procedure to stop immediately once there is any error found.
There is an error log table for this stored procedure, I have customized the error and if the error do happen, it repeats the same error message multiple times because of the while loop used in the stored procedure. I want the error to be logged only once per occurrence.
This is the general outline of the stored procedure code:
ALTER PROCEDURE [sp_abc]
AS
BEGIN
create table #abc1 (id int, name char(20), done bit default 0)
values (1,'ben'), (2,'charles')
WHILE (SELECT count(*) from #abc1 where done = 0) > 0
BEGIN
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO abc123 (col1, col2)
VALUES (1,2), (3,4)
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DECLARE #error_number INT
, #error_message VARCHAR(MAX)
SET #error_number = ERROR_NUMBER()
SET #error_message = ERROR_MESSAGE() + ' [Trying to insert data into abc123]'
ROLLBACK TRANSACTION
-- Insert error into an error table
INSERT INTO [dbo].[error_log] ( sp_name,[error_message] )
SELECT DISTINCT ' sp123 ', #error_message
END CATCH
END
Related
I'm trying to call a stored procedure from another stored procedure and store the value in a variable. The inner stored procedure basically checks if something exists and uses a select statement to return a zero or one. I keep getting an error. In this situation, MySQL is saying "=" is not valid at this position, expecting ";"
CREATE PROCEDURE `CardNames_Add` (searchedCard VARCHAR(50))
BEGIN
DECLARE exist TINYINT;
EXECUTE exist = CardNames_CheckExist searchedCard
IF (exist = 0)
INSERT INTO card_names (name)
VALUE(searchedCard)
END
You have to rewrite you other stored procedure, that you don't need btw, to give back a result
CREATE PROCEDURE CardNames_CheckExist (IN searchedCard VARCHAR(50), OUT result TINYINT )
BEGIN
--do some stuzff
result = 1
END
CREATE PROCEDURE `CardNames_Add` (searchedCard VARCHAR(50))
BEGIN
CALL CardNames_CheckExist(searchedCard,#result);
IF (#result = 0) THEN
INSERT INTO card_names (name)
VALUES (searchedCard);
END IF;
END
I Have sample procedure .
CREATE PROCEDURE `sample_procedure `
(
IN IDIn bigint(20),
IN MainIDIn bigint(20),
IN NameIn varchar(20)
)
READS SQL DATA
DETERMINISTIC
BEGIN
INSERT INTO tbl_DEPT
(
ID,
Name)
Select 1,'Mohan';
IF NOT EXISTS (SELECT ID FROM tbl_emp te WHERE te.ID = IDIn) THEN
INSERT INTO tbl_emp
(
MainID,
Name)
VALUES (MainIDIn,
NameIn);
ELSE
IF EXISTS (SELECT ID FROM tbl_emp te WHERE te.ID = IDIn) THEN
UPDATE tbl_emp
set
MainID =MainIDIn,
name = NameIn
WHERE te.ID= IDIn;
END IF;
END IF;
END
Call sample_procedure(1,2,Sampl123)
I'm just sending some irrelevant Data into the Procedure so that procedure gets failed . But how we need to implement roll back means it should come to the starting state with out Inserting Records into the tbl_DEPT also.
IN T-SQL we will have
BEGIN
SET NOCOUNT ON
BEGIN TRANSACTION
BEGIN TRY
SET #OUT = "success";
COMMIT TRANSACTION
END TRY
BEGIN CATCH
set #out = 'not success';
ROLLBACK TRANSACTION
END CATCH
END
this kind of TRY CATCH blocks and to capture Error
"ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity"
In the same way in MYSQL I'm looking for TRY CATCH and ROLL BACK Mechanism .
IF Procedure fails it should ROLL BACK and Not to load in any of the table .
Can any one Suggest me in MYSQL.
Mysql does not use try/catch. If uses handlers, e.g. the EXIT handler that will terminate the execution of the SP:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
-- other logic
END;
There are other type of handlers as well.
I have created one stored procedure which inserts a record into table and gets auto incremented ID of that record. Here I am getting an syntax error while setting LAST_INSERT_ID() into a variable.
ERROR 1064 (42000): You have an error in your SQL syntax; check the
manual that corresponds to your MySQL server version for the right
syntax to use near ');
SET _orderId = SELECT LAST_INSERT_ID(); END' at line 5
Please help me to solve this issue. Thanks in Advance.
My code is like below:
DELIMITER //
CREATE PROCEDURE placeOrder(IN _cartId INT, IN _createdBy INT)
BEGIN
DECLARE _orderId INT;
-- insert into order
INSERT INTO `TBL_ORDER`(`DealerId`, `OrderNo`, `CreatedBy`)
VALUES ((SELECT DealerId FROM TBL_SHOPPING_CART WHERE Id = _cartId), UNIX_TIMESTAMP(), _createdBy));
SET _orderId = SELECT LAST_INSERT_ID();
END//
DELIMITER ;
Try this.
delimiter //
CREATE PROCEDURE placeOrder(IN _cartId INT,IN _createdBy INT)
BEGIN
SET #orderId = '';
-- insert into order
INSERT INTO `TBL_ORDER`(`DealerId`, `OrderNo`, `CreatedBy`) VALUES ((SELECT DealerId FROM TBL_SHOPPING_CART WHERE Id = _cartId),UNIX_TIMESTAMP(),_createdBy));
SELECT LAST_INSERT_ID() INTO #orderId;
END//
delimiter ;
OR
delimiter //
CREATE PROCEDURE placeOrder(IN _cartId INT,IN _createdBy INT)
BEGIN
-- insert into order
INSERT INTO `TBL_ORDER`(`DealerId`, `OrderNo`, `CreatedBy`) VALUES ((SELECT DealerId FROM TBL_SHOPPING_CART WHERE Id = _cartId),UNIX_TIMESTAMP(),_createdBy);
SELECT LAST_INSERT_ID() AS '_orderId ';
END//
delimiter ;
You should make sure that your application is not having a global connection or shared connection. As last_insert_it() will return the last generated AI value it can be from any table. Especially if your host application is using async TASKs
Consider following scenario
Your application saves gps location every second => generating new AI value
You're trying above SP to insert a value => Generating new AI value
Between your insert and read last_insert_id, your application logs gps location again and created new AI value.
Now guess what happens? you get the last inserted id from the gps table not from your SP.
usually insert's are fast, but assume your SP had to wait for a table lock and got delayed. In such case, you will receive wrong ID.
Safest way can be to escapsulate your SP work within a transaction and get the max value of your AI column (assuming it's an unsigned AI column).
Try:
...
-- SET _orderId = SELECT LAST_INSERT_ID();
SET _orderId = LAST_INSERT_ID();
SELECT _orderId;
...
or
...
-- SET _orderId = SELECT LAST_INSERT_ID();
SET _orderId = (SELECT LAST_INSERT_ID());
SELECT _orderId;
...
I have an SQL script that I need to convert to a parameterized stored procedure. I've only written simple functions in the past, never a complex transactional query with parameters.
Any help is greatly appreciated - simplified queries below. This script could really be anything containing a transaction and some user inputs.
-- transaction ensures i can clean up a mess, if one happens
begin;
-- parameters for the script; currently set manually before execution
set #parent_id := 123;
set #identifier := 'someid';
-- insert some row with user-specified values
insert into users (field1, field2) values (#parent_id, #identifier);
-- get the new id
set #user_id := last_insert_id();
-- do another insert
insert into usersmeta (user_id, field1, field2) values (#user_id, 1, 2);
-- if no errors happened yet, commit transaction
commit;
-- "return value"; some select query (could be 1 or many rows)
select users.id userid, usersmeta metaid
from users
join usersmeta on usersmeta.user_id = users.id;
I started with but then I pretty much got stuck. I'm especially concerned with ensuring errors, in the event they occur, are somehow made visible to the calling code
delimiter ;;
CREATE PROCEDURE mytask(IN parent_id INT(11), IN identifier VARCHAR(200))
BEGIN
SET #query = ???
PREPARE q FROM #query;
EXECUTE q;
DEALLOCATE PREPARE q;
END;;
delimiter ;
It took a good amount of research, trial, and error, but I think I arrived at a pretty good solution.
DELIMITER //
CREATE PROCEDURE my_procedure (IN parent_id int, IN identifier varchar(255), OUT out_user_id int)
BEGIN
-- local variable, set later after user is created
DECLARE user_id int;
-- rollback transaction and bubble up errors if something bad happens
DECLARE exit handler FOR SQLEXCEPTION, SQLWARNING
BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
-- insert some row with user-specified values
INSERT INTO users (field1, field2) values (parent_id, identifier);
-- get the new id
SET user_id = last_insert_id();
-- do another insert
INSERT INTO usersmeta (user_id, field1, field2) values (user_id, 1, 2);
-- if no errors happened yet, commit transaction
COMMIT;
-- return
SELECT user_id INTO out_user_id;
END //
DELIMITER ;
I can use it like this
-- run the procedure
CALL my_procedure(123, 'some_id', #user_id);
-- get the "return" value
SELECT #user_id as user_id;
This is definitely the most complex stored procedure I've written. If anyone sees an area for improvement, I'd be happy to learn how I can make it better.
I get an error
Custom error message. Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0. Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.
My SQL Code is following
Procedure 1
CREATE PROCEDURE [dbo].[spProcedure1]
#Id int
WITH EXECUTE AS OWNER
AS
BEGIN
BEGIN TRY
BEGIN TRANSACTION
EXEC spProcedure2 #Id
-- Code ommited, Next procedures execution
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
-- Code ommited, saving error to Log table
RAISERROR(#errorMessage, #errorSeverity, #errorState);
END CATCH
END
Procedure 2
CREATE PROCEDURE [dbo].[spProcedure2]
#Id int
WITH EXECUTE AS OWNER
BEGIN
BEGIN TRY
-- Check if record exists
IF NOT EXISTS(SELECT * FROM dbo.Table t WHERE t.Id = #Id)
BEGIN
-- stop procedure due to user error
RAISERROR('Custom error message.',16,1)
RETURN
END
-- Code ommited, DML operation
END TRY
BEGIN CATCH
-- Code ommited, saving error to Log table
RAISERROR(#errorMessage, #errorSeverity, #errorState);
END CATCH
END
I am not sure how to handle transaction in the Procedure 2 to obey this error ?