I am using MySql stored procedure to do operation with my database.
Here is sample store procedure.
CREATE DEFINER=`ntadmin`#`%` PROCEDURE `usp_user`(
IN cMode VARCHAR(20),
IN nUserID MEDIUMINT UNSIGNED,
IN cEmail VARCHAR(50),
IN cFirstName VARCHAR(20),
IN cLastName VARCHAR(20),
IN nIsMale TINYINT(1) UNSIGNED
) DETERMINISTIC
BEGIN
IF (cMode = "insert") THEN
IF NOT EXISTS(SELECT 1 FROM user WHERE email = cEmail) THEN
INSERT INTO user(email, firstname, lastname, ismale)
VALUES (cEmail, cFirstName, cLastName, nIsMale);
SET nUserID = LAST_INSERT_ID();
-- Here I would like to send all details of inserted user.
SET max_sp_recursion_depth = 1;
CALL usp_user("select", nUserID, null, null, null, null);
ELSE
SELECT -1 AS "UnSuccess", "Email already exists" AS "Error"; ;
END IF;
ELSEIF (cMode = "select") THEN
SELECT email, firstname, lastname, ismale
FROM user
WHERE userid = nUserID;
ELSEIF (cMode = "delete") THEN
//delete code
SELECT 1 AS "Success";
END IF;
END
As you look carefully my code, I am calling this stored procedure recursively in insert using max_sp_recursion_depth. Is there any better option to avoid recursive call?
Thanks.
Should I use Label or GOTO statements?
One option to avoid recursion is changing the structure as follows:
mysql> DELIMITER //
mysql> DROP FUNCTION IF EXISTS `existsEmail`//
Query OK, 0 rows affected (0.00 sec)
mysql> DROP PROCEDURE IF EXISTS `usp_user_delete`//
Query OK, 0 rows affected (0.00 sec)
mysql> DROP PROCEDURE IF EXISTS `usp_user_insert`//
Query OK, 0 rows affected (0.00 sec)
mysql> DROP PROCEDURE IF EXISTS `usp_user_select`//
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS `user`//
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS `user` (
-> `nuserid` MEDIUMINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
-> `firstname` VARCHAR(20) DEFAULT NULL,
-> `lastname` VARCHAR(20) DEFAULT NULL,
-> `email` VARCHAR(50) DEFAULT NULL,
-> `ismale` TINYINT(1) UNSIGNED DEFAULT NULL,
-> PRIMARY KEY (`nuserid`),
-> UNIQUE KEY `idx_email` (`email`)
-> )//
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE PROCEDURE `usp_user_insert`(
-> IN `cEmail` VARCHAR(50),
-> IN `cFirstName` VARCHAR(20),
-> IN `cLastName` VARCHAR(20),
-> IN `nIsMale` TINYINT(1) UNSIGNED
-> )
-> BEGIN
-> DECLARE `_existsEmail` CONDITION FOR SQLSTATE '45000';
-> IF (SELECT NOT `existsEmail`(`cEmail`)) THEN
-> INSERT INTO `user` (
-> `email`,
-> `firstname`,
-> `lastname`,
-> `ismale`
-> ) VALUES (
-> `cEmail`,
-> `cFirstName`,
-> `cLastName`,
-> `nIsMale`
-> );
-> CALL `usp_user_select`(LAST_INSERT_ID());
-> ELSE
-> -- SELECT -1 'UnSuccess', 'Email already exists' 'Error';
-> SIGNAL `_existsEmail`
-> SET MESSAGE_TEXT = 'Email already exists', MYSQL_ERRNO = 4000;
-> END IF;
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE PROCEDURE `usp_user_select`(
-> IN `cnuserid` MEDIUMINT UNSIGNED
-> )
-> BEGIN
-> SELECT
-> `email`,
-> `firstname`,
-> `lastname`,
-> `ismale`
-> FROM
-> `user`
-> WHERE
-> `nuserid`= `cnuserid`;
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE PROCEDURE `usp_user_delete`()
-> BEGIN
-> -- delete code
-> SELECT 1 'Success';
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE FUNCTION `existsEmail`(
-> `cemail` VARCHAR(50)
-> ) RETURNS TINYINT(1)
-> BEGIN
-> RETURN
-> EXISTS(
-> SELECT
-> NULL
-> FROM
-> `user`
-> WHERE
-> `email` = `cemail`
-> );
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> CALL `usp_user_insert`(
-> 'user#example.com',
-> 'firstname_user',
-> 'lastname_user',
-> 1
-> )//
+------------------+----------------+---------------+--------+
| email | firstname | lastname | ismale |
+------------------+----------------+---------------+--------+
| user#example.com | firstname_user | lastname_user | 1 |
+------------------+----------------+---------------+--------+
1 row in set (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
mysql> CALL `usp_user_insert`(
-> 'user#example.com',
-> 'firstname_user',
-> 'lastname_user',
-> 1
-> )//
ERROR 4000 (45000): Email already exists
Related
I want to perform transactional inserts , but i do not understand the problem because i couldnt see the error. I read carefully the mysql instructions for performing transactional procedure.
The problem is there is no return out header id, How do I mitigate this issue?
CREATE PROCEDURE `add_payment`(IN `transaction_no` VARCHAR(50),
IN `transaction_type_id` MEDIUMINT(8) UNSIGNED,
IN `distributor_details_id` MEDIUMINT(8) UNSIGNED,
IN `customer_id` INT(11) UNSIGNED,
IN `amount` DECIMAL(18,8),
IN `salesman_id` INT(11) UNSIGNED,
IN `created_datetime` DATETIME,
OUT `payment_header_id` INT(11) UNSIGNED)
BEGIN
DECLARE transaction_code_id INTEGER(11) UNSIGNED DEFAULT 0;
DECLARE transaction_x_payment_header_id INTEGER(11) UNSIGNED DEFAULT 0;
DECLARE payment_details_id INTEGER(11) UNSIGNED DEFAULT 0;
DECLARE user_id MEDIUMINT(8) UNSIGNED DEFAULT 0;
#
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SET payment_header_id = 0;
ROLLBACK ;
END;
START TRANSACTION;
# GET USER TO HAVE A REFERENCE WHOS DOING THIS
SET user_id = (SELECT user_id FROM salesman_x_user WHERE salesman_id = salesman_id);
# INSERT PAYMENT HEADER FIRST TO HAVE PAYMENT HEADER ID
INSERT INTO `payment_header` (`no` , `created_datetime`) VALUES(transaction_no , created_datetime);
SET payment_header_id = (SELECT LAST_INSERT_ID());
COMMIT;
END;
Avoid naming variables and parameters as columns of your tables.
See the following adapted example:
mysql> DROP TABLE IF EXISTS `payment_header`;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS `payment_header`(
-> `payment_header_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
-> `no` VARCHAR(50),
-> `created_datetime` DATETIME,
-> UNIQUE KEY (`no`)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER //
mysql> DROP PROCEDURE IF EXISTS `add_payment`//
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE PROCEDURE `add_payment` (
-> IN `_transaction_no` VARCHAR(50),
-> IN `_transaction_type_id` MEDIUMINT(8) UNSIGNED,
-> IN `_distributor_details_id` MEDIUMINT(8) UNSIGNED,
-> IN `_customer_id` INT(11) UNSIGNED,
-> IN `_amount` DECIMAL(18, 8),
-> IN `_salesman_id` INT(11) UNSIGNED,
-> IN `_created_datetime` DATETIME,
-> OUT `_payment_header_id` INT(11) UNSIGNED
-> )
-> BEGIN
-> DECLARE `transaction_code_id` INTEGER(11) UNSIGNED DEFAULT 0;
-> DECLARE `transaction_x_payment_header_id` INTEGER(11) UNSIGNED DEFAULT 0;
-> DECLARE `payment_details_id` INTEGER(11) UNSIGNED DEFAULT 0;
-> DECLARE `user_id` MEDIUMINT(8) UNSIGNED DEFAULT 0;
->
-> DECLARE EXIT HANDLER FOR SQLEXCEPTION
-> BEGIN
-> SET `_payment_header_id` = 0;
-> ROLLBACK;
-> END;
->
-> START TRANSACTION;
-> # GET USER TO HAVE A REFERENCE WHOS DOING THIS
-> /*SET `user_id` = (SELECT `user_id`
/*> FROM `salesman_x_user`
/*> WHERE `salesman_id` = `_salesman_id`);*/
->
-> # INSERT PAYMENT HEADER FIRST TO HAVE PAYMENT HEADER ID
-> INSERT INTO `payment_header` (`no` , `created_datetime`)
-> VALUES (`_transaction_no`, `_created_datetime`);
->
-> SET `_payment_header_id` = LAST_INSERT_ID();
-> COMMIT;
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;
mysql> CALL `add_payment`(
-> '00000000000000000000000000000000000000000000000001',
-> NULL,
-> NULL,
-> NULL,
-> NULL,
-> NULL,
-> NOW(),
-> #`payment_header_id`
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT #`payment_header_id`;
+----------------------+
| #`payment_header_id` |
+----------------------+
| 1 |
+----------------------+
1 row in set (0.00 sec)
mysql> SELECT
-> `payment_header_id`,
-> `no`,
-> `created_datetime`
-> FROM
-> `payment_header`;
+-------------------+----------------------------------------------------+---------------------+
| payment_header_id | no | created_datetime |
+-------------------+----------------------------------------------------+---------------------+
| 1 | 00000000000000000000000000000000000000000000000001 | 2000-01-01 00:00:01 |
+-------------------+----------------------------------------------------+---------------------+
1 row in set (0.00 sec)
mysql> CALL `add_payment`(
-> '00000000000000000000000000000000000000000000000001',
-> NULL,
-> NULL,
-> NULL,
-> NULL,
-> NULL,
-> NOW(),
-> #`payment_header_id`
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT #`payment_header_id`;
+----------------------+
| #`payment_header_id` |
+----------------------+
| 0 |
+----------------------+
1 row in set (0.00 sec)
mysql> SELECT
-> `payment_header_id`,
-> `no`,
-> `created_datetime`
-> FROM
-> `payment_header`;
+-------------------+----------------------------------------------------+---------------------+
| payment_header_id | no | created_datetime |
+-------------------+----------------------------------------------------+---------------------+
| 1 | 00000000000000000000000000000000000000000000000001 | 2000-01-01 00:00:01 |
+-------------------+----------------------------------------------------+---------------------+
1 row in set (0.00 sec)
See db-fiddle.
This is my code:
IF EXISTS(SELECT * FROM User WHERE Username = #Username) THEN
RETURN -1;
ELSEIF EXISTS(SELECT * FROM User WHERE Email = #Email)
THEN
RETURN -2;
ELSE
INSERT INTO User(Username, Password, Email)
VALUES ('Nicki',#Password,#Email);
RETURN LAST_INSERT_ID();
END IF
And an image
But the function doesn't insert a value — can you explain why not?
#Username is a 9.4 User-Defined Variables and Username is a table column.
Also, check: C.1 Restrictions on Stored Programs::Name Conflicts within Stored Routines.
Try:
mysql> SELECT VERSION();
+-----------+
| VERSION() |
+-----------+
| 5.7.18 |
+-----------+
1 row in set (0.00 sec)
mysql> DROP FUNCTION IF EXISTS `InsertUser`;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS `User`;
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE IF NOT EXISTS `User`(
-> `Id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-> `Username` VARCHAR(255) NOT NULL,
-> `Password` VARCHAR(255) NOT NULL,
-> `Email` VARCHAR(255) NOT NULL
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER //
mysql> CREATE FUNCTION `InsertUser`(`_Username` VARCHAR(255),
-> `_Email` VARCHAR(255),
-> `_Password` VARCHAR(255)
-> )
-> RETURNS INT
-> LANGUAGE SQL
-> DETERMINISTIC
-> MODIFIES SQL DATA
-> BEGIN
-> IF EXISTS(SELECT * FROM `User` WHERE `Username` = `_Username`) THEN
-> RETURN -1;
-> ELSEIF EXISTS(SELECT * FROM `User` WHERE `Email` = `_Email`) THEN
-> RETURN -2;
-> ELSE
-> INSERT INTO `User` (`Username`, `Password`, `Email`)
-> VALUES ('Nicki', `_Password`, `_Email`);
-> RETURN LAST_INSERT_ID();
-> END IF;
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;
mysql> SELECT
-> `Id`,
-> `Username`,
-> `Password`,
-> `Email`
-> FROM
-> `User`;
Empty set (0.00 sec)
mysql> SELECT `InsertUser`(NULL, 'pass', 'email#domain.ext');
+------------------------------------------------+
| `InsertUser`(NULL, 'pass', 'email#domain.ext') |
+------------------------------------------------+
| 1 |
+------------------------------------------------+
1 row in set (0.01 sec)
mysql> SELECT
-> `Id`,
-> `Username`,
-> `Password`,
-> `Email`
-> FROM
-> `User`;
+----+----------+------------------+-------+
| Id | Username | Password | Email |
+----+----------+------------------+-------+
| 1 | Nicki | email#domain.ext | pass |
+----+----------+------------------+-------+
1 row in set (0.00 sec)
mysql> SELECT `InsertUser`(NULL, 'pass', 'email#domain.ext');
+------------------------------------------------+
| `InsertUser`(NULL, 'pass', 'email#domain.ext') |
+------------------------------------------------+
| -2 |
+------------------------------------------------+
1 row in set (0.00 sec)
Example db-fiddle.
I have a transaction which calls a generated procedure, however if an error occurs after the procedure I notice that the statements during and before the procedure have been committed. Here is the procedure:
DELIMITER $$
CREATE PROCEDURE location_processor(in _city varchar(20), in _country_code varchar(2), out id int)
begin
select location_id into id from location where city = _city and country_code = _country_code limit 0,1;
if id is null then
select #id := max(location_id) from location;
if #id is null then
set #id = 0;
end if;
set #id = #id + 1;
insert into location (location_id, city, country_code)
values(#id, _city, _country_code);
set id = #id;
end if;
end; $$
DELIMITER ;
Note: there is no start/end transaction syntax used within this procedure; although I have reason to believe that begin and end of the procedure itself is causing commit as:
Note:
Within all stored programs (stored procedures and functions, triggers, and events), the parser treats BEGIN [WORK] as the beginning of a BEGIN ... END block. Begin a transaction in this context with START TRANSACTION instead.
(https://dev.mysql.com/doc/refman/5.7/en/commit.html)
I need this procedure for error checking purposes. Is there anyway to avoid committing within a transaction whilst using a procedure within it?
With a simple example, I can't reproduce the problem:
mysql> DROP PROCEDURE IF EXISTS `location_processor`;
Query OK, 0 rows affected (0.00 sec)
mysql> DROP TABLE IF EXISTS `location`;
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE IF NOT EXISTS `location` (
-> `location_id` INT,
-> `city` VARCHAR(255),
-> `country_code` VARCHAR(255)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER //
mysql> CREATE PROCEDURE `location_processor`()
-> BEGIN
-> INSERT INTO `location`
-> (`location_id`, `city`, `country_code`)
-> VALUES
-> (2, 'city', 'country_code');
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT `location_id`, `city`, `country_code`
-> FROM `location`;
Empty set (0.00 sec)
mysql> INSERT INTO `location`
-> (`location_id`, `city`, `country_code`)
-> VALUES
-> (1, 'city', 'country_code');
Query OK, 1 row affected (0.00 sec)
mysql> CALL `location_processor`;
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO `location`
-> (`location_id`, `city`, `country_code`)
-> VALUES
-> (3, 'city', 'country_code');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT `location_id`, `city`, `country_code`
-> FROM `location`;
+-------------+------+--------------+
| location_id | city | country_code |
+-------------+------+--------------+
| 1 | city | country_code |
| 2 | city | country_code |
| 3 | city | country_code |
+-------------+------+--------------+
3 rows in set (0.00 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT `location_id`, `city`, `country_code`
-> FROM `location`;
Empty set (0.00 sec)
I need a trigger between two database when a row insert to tbl1 in db1,this row insert into tbl2 in db2.
CREATE TRIGGER `update_users` AFTER INSERT ON `db1`.`tbl1`
FOR EACH ROW INSERT INTO `db2`.`tbl2` (id,email,password)
VALUES
(???)
what is write in VALUES??
See example:
mysql> CREATE DATABASE `db1`;
Query OK, 1 row affected (0.00 sec)
mysql> CREATE DATABASE `db2`;
Query OK, 1 row affected (0.00 sec)
mysql> USE `db1`;
Database changed
mysql> CREATE TABLE `tbl1` (
-> `id` INT UNSIGNED,
-> `email` VARCHAR(255),
-> `password` VARCHAR(255)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> DELIMITER //
mysql> CREATE TRIGGER `update_users` AFTER INSERT ON `tbl1`
-> FOR EACH ROW
-> BEGIN
-> INSERT INTO `db2`.`tbl2` (`id`, `email`, `password`)
-> VALUES
-> (NEW.`id`, NEW.`email`, NEW.`password`);
-> END//
Query OK, 0 rows affected (0.00 sec)
mysql> DELIMITER ;
mysql> USE `db2`;
Database changed
mysql> CREATE TABLE `tbl2` (
-> `id` INT UNSIGNED,
-> `email` VARCHAR(255),
-> `password` VARCHAR(255)
-> );
Query OK, 0 rows affected (0.00 sec)
mysql> USE `db1`;
Database changed
mysql> INSERT INTO `tbl1` VALUES (1, 'email#email.net', 'secret');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT `id`, `email`, `password` FROM `tbl1`;
+------+-----------------+----------+
| id | email | password |
+------+-----------------+----------+
| 1 | email#email.net | secret |
+------+-----------------+----------+
1 row in set (0.00 sec)
mysql> SELECT `id`, `email`, `password` FROM `db2`.`tbl2`;
+------+-----------------+----------+
| id | email | password |
+------+-----------------+----------+
| 1 | email#email.net | secret |
+------+-----------------+----------+
1 row in set (0.00 sec)
DB schema
CREATE TABLE Users(
UserId int auto_increment NOT NULL Primary key,
Username varchar(20) NOT NULL,
Password varchar(20) NOT NULL,
Email nvarchar(30) NOT NULL,
CreatedDate datetime NOT NULL,
LastLoginDate datetime NULL
);
and the stored procedure that I am trying to write is
CREATE procedure Insert_User(
in uname NVARCHAR(20),
in Pass NVARCHAR(20),
in Em NVARCHAR(30)),
out result int)
BEGIN
IF EXISTS(SELECT UserId FROM Users WHERE Username = uname)
BEGIN
set result=-1 -- Username exists.
END
elif EXISTS(SELECT UserId FROM Users WHERE Email = Em)
BEGIN
set result=-2 -- Email exists.
END
ELSE
BEGIN
INSERT INTO Users
(Username
,Password
,Email
,CreatedDate)
VALUES
(#Username
,#Password
,#Email
,GETDATE())
set result=SCOPE_IDENTITY() -- UserId
END
END
What am I am doing wrong?
You have plenty of issues in the procedure here is the one in mysql.
delimiter //
CREATE procedure Insert_User(
in uname VARCHAR(20),
in Pass VARCHAR(20),
in Em VARCHAR(30),
out result int
)
BEGIN
IF (select count(*) from Users WHERE Username = uname ) > 0 then
begin
set result=-1 ;
end ;
elseif (select count(*) from Users WHERE Email = Em ) > 0 then
begin
set result=-2 ;
end ;
else
begin
INSERT INTO Users
(Username
,Password
,Email
,CreatedDate)
VALUES
(uname
,Pass
,Em
,curdate()) ;
set result=LAST_INSERT_ID();
end ;
end if ;
END ;//
delimiter ;
Here is the test case in mysql cli
mysql> CREATE TABLE Users(
-> UserId int auto_increment NOT NULL Primary key,
-> Username varchar(20) NOT NULL,
-> Password varchar(20) NOT NULL,
-> Email nvarchar(30) NOT NULL,
-> CreatedDate datetime NOT NULL,
-> LastLoginDate datetime NULL
->
-> );
Query OK, 0 rows affected (0.16 sec)
mysql> delimiter //
mysql> CREATE procedure Insert_User(
-> in uname VARCHAR(20),
-> in Pass VARCHAR(20),
-> in Em VARCHAR(30),
-> out result int
-> )
-> BEGIN
-> IF (select count(*) from Users WHERE Username = uname ) > 0 then
-> begin
-> set result=-1 ;
-> end ;
-> elseif (select count(*) from Users WHERE Email = Em ) > 0 then
-> begin
-> set result=-2 ;
-> end ;
-> else
-> begin
-> INSERT INTO Users
-> (Username
-> ,Password
-> ,Email
-> ,CreatedDate)
-> VALUES
-> (uname
-> ,Pass
-> ,Em
-> ,curdate()) ;
->
-> set result=LAST_INSERT_ID();
-> end ;
-> end if ;
-> END ;//
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql> delimiter ;
mysql> call Insert_User('Abhik','abhik','abhik#aa.com',#res);
Query OK, 1 row affected (0.05 sec)
mysql> select #res;
+------+
| #res |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql> select * from Users;
+--------+----------+----------+--------------+---------------------+---------------+
| UserId | Username | Password | Email | CreatedDate | LastLoginDate |
+--------+----------+----------+--------------+---------------------+---------------+
| 1 | Abhik | abhik | abhik#aa.com | 2015-03-14 00:00:00 | NULL |
+--------+----------+----------+--------------+---------------------+---------------+
1 row in set (0.00 sec)
mysql> call Insert_User('Abhik','abhik','abhik#aa.com',#res);
Query OK, 0 rows affected (0.00 sec)
mysql> select #res;
+------+
| #res |
+------+
| -1 |
+------+
1 row in set (0.00 sec)