Check if a row exists inside a MySQL function - mysql

In my user table the IDs user_id are randomly generated by the following functions:
DELIMITER #
CREATE FUNCTION GenerateRandomUserID()
RETURNS CHAR(15)
BEGIN
DECLARE user_id CHAR(15) DEFAULT '';
WHILE LENGTH(user_id) < 15 DO
SET user_id = CONCAT(user_id, SUBSTRING('0123456789', RAND() * 10 + 1, 1));
END WHILE;
RETURN user_id;
END#
DELIMITER ;
DELIMITER #
CREATE FUNCTION GenerateUniqueUserID()
RETURNS CHAR(15)
BEGIN
DECLARE user_id CHAR(15) DEFAULT '';
REPEAT
SET user_id = GenerateRandomUserID();
UNTIL NOT UserIDExists(user_id) END REPEAT;
RETURN user_id;
END#
DELIMITER ;
I then insert a user generating its ID with this function. So far so good. The problem is when I insert another user, which generates an infinite loop. I managed to boil down the problem to yet another function, UserIDExists, which always returns TRUE (it shouldn't, obviously).
DELIMITER #
CREATE FUNCTION UserIDExists(user_id CHAR(15))
RETURNS BOOL
BEGIN
RETURN EXISTS(SELECT 1 FROM `user` WHERE `user_id` = user_id);
END#
DELIMITER ;
This last function does not do what it's supposed to, but I can't figure out why. If a single user exists with any password this will return TRUE, only if there are no users does it return FALSE. Can anyone figure out why?
Update:
I also tried this. Same result:
DELIMITER #
CREATE PROCEDURE UserIDExists(IN user_id CHAR(15), OUT user_id_exists BOOL)
BEGIN
SET user_id_exists = 0;
SELECT 1 INTO user_id_exists FROM `user` WHERE `user_id` = user_id;
END#
DELIMITER ;
Update:
Tried this as well:
DELIMITER #
CREATE PROCEDURE UserIDExists(IN user_id CHAR(15), OUT user_id_exists BOOL)
BEGIN
IF ((SELECT `user_id` FROM `user` WHERE `user_id` = user_id) != NULL) THEN
SET user_id_exists = TRUE;
ELSE
SET user_id_exists = FALSE;
END IF;
END#
DELIMITER ;
Update:
I tried a few variantes with the following:
DELIMITER #
CREATE PROCEDURE UserIDExists_2(IN user_id CHAR(15), OUT user_id_exists CHAR)
BEGIN
SELECT `user_id` FROM `user` WHERE `user_id` = user_id;
END#
DELIMITER ;
And came to the conclusion that this returns 1 when user_id is also 1 and returns nothing for any other value. This is pretty odd, because this is the only row I have on the table:
# user_id, first_name, last_name, pwd, is_confirmed
'252316605573186', 'André', 'Fratelli', NULL, '0'
This is getting on my nerves...

This works. I though that the quotes where enough to distinguish the procedure's arguments from the table's columns, but I guess I was wrong.
DELIMITER #
CREATE PROCEDURE UserIDExists(IN p_user_id CHAR(15), OUT p_user_id_exists BOOL)
BEGIN
SET p_user_id_exists = EXISTS(SELECT `user_id` FROM `user` WHERE `user_id` = p_user_id);
END#
DELIMITER ;

Related

Procedure Slow update records

How to improve the procedure to perform faster?
I have 3 procedures, the first searches the balance before the second updates the balance and the third I use to do the processing where I call the two previous procedures.
In short, when I insert a table record a timer in the service redo the balance processing by calling the procedure PROC_PROCESSAR_SALDO.
DROP PROCEDURE IF EXISTS `PROC_SALDO_ANTERIOR`;
DELIMITER $$
CREATE PROCEDURE `PROC_SALDO_ANTERIOR` (
IN codigo_Empresa_par INT,
IN codigo_Filial_par INT,
IN codigo_Conta_par INT,
IN sequencia_par BIGINT(20),
IN data_Hora_par DATETIME,
OUT sequencia_ret BIGINT(20),
OUT data_Hora_ret DATETIME,
OUT saldo_Atual_ret DECIMAL(20, 2)
)
BEGIN
SELECT SEQUENCIA, DATAHORA, SALDO_ATUAL INTO sequencia_ret, data_Hora_ret, saldo_Atual_ret
FROM CONTAS_CORRENTES
WHERE CODIGO_EMPRESA = codigo_Empresa_par AND
CODIGO_FILIAL = codigo_Filial_par AND
CODIGO_CONTA = codigo_Conta_par AND
((DATAHORA =data_Hora_par AND SEQUENCIA < sequencia_par) OR (DATAHORA < data_Hora_par))
ORDER BY DATAHORA DESC, SEQUENCIA DESC LIMIT 1;
if sequencia_ret IS NULL THEN
SET sequencia_ret := 0;
SET data_Hora_ret := '0001/01/01 12:00:00';
SET saldo_Atual_ret := 0;
END IF;
END;
DELIMITER ;
DROP PROCEDURE IF EXISTS `PROC_ATUALIZAR_SALDO`;
DELIMITER $$
CREATE PROCEDURE `PROC_ATUALIZAR_SALDO` (
IN codigo_Empresa_par INT,
IN codigo_Filial_par INT,
IN codigo_Conta_par INT,
IN sequencia_par BIGINT(20),
IN data_Hora_par DATETIME,
IN saldo_Atual_par DECIMAL(20,2)
)
BEGIN
DECLARE SEQUENCIA_NEW BIGINT(20) DEFAULT 0;
DECLARE DEBITO_NEW DECIMAL(20,2) DEFAULT 0;
DECLARE CREDITO_NEW DECIMAL(20,2) DEFAULT 0;
DECLARE SALDO_ANTERIOR DECIMAL(20,2) DEFAULT 0;
DECLARE done INT DEFAULT FALSE;
DECLARE cur CURSOR FOR SELECT A.SEQUENCIA, A.DEBITO, A.CREDITO
FROM CONTAS_CORRENTES A
WHERE A.CODIGO_EMPRESA = codigo_Empresa_par AND
A.CODIGO_FILIAL = codigo_Filial_par AND
A.CODIGO_CONTA = codigo_Conta_par AND
((A.DATAHORA = data_Hora_par AND
A.SEQUENCIA > sequencia_par) OR
(A.DATAHORA > data_Hora_par))
ORDER BY A.DATAHORA ASC,
A.SEQUENCIA ASC;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
START TRANSACTION;
SET SALDO_ANTERIOR := saldo_Atual_par;
OPEN cur;
ins_loop: LOOP
FETCH cur INTO SEQUENCIA_NEW, DEBITO_NEW, CREDITO_NEW;
IF done THEN
LEAVE ins_loop;
END IF;
SET SALDO_ANTERIOR := SALDO_ANTERIOR - DEBITO_NEW + CREDITO_NEW;
UPDATE CONTAS_CORRENTES SET SALDO_ATUAL = SALDO_ANTERIOR
WHERE CODIGO_EMPRESA = codigo_Empresa_par AND
CODIGO_FILIAL = codigo_Filial_par AND
CODIGO_CONTA = codigo_Conta_par AND
SEQUENCIA = SEQUENCIA_NEW;
END LOOP;
CLOSE cur;
COMMIT;
END $$
DELIMITER ;
DROP PROCEDURE IF EXISTS `PROC_PROCESSAR_SALDO`;
DELIMITER $$
CREATE PROCEDURE `PROC_PROCESSAR_SALDO` (
IN codigo_Empresa_new INT,
IN codigo_Filial_new INT,
IN codigo_Conta_new INT,
IN sequencia_new BIGINT(20),
IN data_Hora_new DATETIME
)
BEGIN
CALL PROC_SALDO_ANTERIOR(codigo_Empresa_new, codigo_Filial_new, codigo_Conta_new, sequencia_new,
data_Hora_new, #sequencia_ret, #data_Hora_ret, #saldo_Atual_ret);
CALL PROC_ATUALIZAR_SALDO(codigo_Empresa_new, codigo_Filial_new, codigo_Conta_new, #sequencia_ret,
#data_Hora_ret, #saldo_Atual_ret);
END $$
DELIMITER ;
CALL PROC_PROCESSAR_SALDO(#CODIGO_EMPRESA, #CODIGO_FILIAL, #CODIGO_CONTA, #SEQUENCIA, #DATAHORA);
You have to check what makes the queries slow. You basically have two things to check:
Do the individual queries use indexes?
Does the cursor loop produce so many rows that (mabye an slightly unoptimized) query in the loop adds up leading to the slow performance
Check the individual queries with EXPLAIN for index use. You can also add select now() after in each query and execute the procedure and you should see which part consumes the time.

MYSQL: IF statement to find a null column in n rows

I'm trying to use an IF statement to find out if any row where the primary key = copy_code has a column return = null and then I would like to do something if it is.
IF ((SELECT `return` FROM loan
WHERE `code` = copy_code AND `return` IS NULL) IS NULL THEN
do something
END IF;
I'm not getting any errors but when I insert data I don't get the correct results.
How can I achieve this?
set delimiter //
create procedure foo as
begin
declare #variable int
SELECT count(*) INTO #variable FROM loan
WHERE `code` = copy_code AND `return` IS NULL;
if (#variable)
begin
... do something
end
end//
set delimiter ;

Cannot set variable in if else statement in MySql

I created a stored procedure which requirement as below
When I execute the stored procedure with one username typed in which be compare if username are exists in database then (variable) result_userId will set at userId ELSE if username dose not exists in database then (variable) result_userId will be set the number 99
BUT I CANNOT DO THAT
Please help me !
::CODE BELOW::
DELIMITER $$
USE `shoppy` $$
CREATE PROCEDURE `testProc02`
(
IN _username CHAR(50),
OUT result_userId INT UNSIGNED
)
BEGIN
SELECT #uId := userId FROM user
WHERE userName = _username;
IF #uId = NULL THEN
SET result_userId = 99;
ELSE
SET result_userId = #uId;
END IF;
END $$
DELIMITER ;
When I CALL testProc02();
enter image description here
You can't use the equality operator = on null. You have to test the expression IS NULL or use the null-safe equality operator <=>.
Your code should be
DELIMITER $$
USE `shoppy` $$
CREATE PROCEDURE `testProc02`
(
IN _username CHAR(50),
OUT result_userId INT UNSIGNED
)
BEGIN
SELECT #uId := userId FROM user
WHERE userName = _username;
IF #uId IS NULL THEN
SET result_userId = 99;
ELSE
SET result_userId = #uId;
END IF;
END $$
DELIMITER ;
What happens is that #uID = NULL always evaluates to null, which the if interprets as false.

Stored procedure with recursive call using mysql

enter image description hereget legside from binary
I have tried using stored procedure with recursive call. i need to display relevant name based on emp_name based on leg1
but it showing error like #1414 - OUT or INOUT argument 2 for routine sample.getVolume is not a variable or NEW pseudo-variable in BEFORE trigger
Here is my code,
DELIMITER $$
CREATE PROCEDURE getVolume( IN param_name VARCHAR(255), OUT result VARCHAR(255 ))
BEGIN
SELECT val INTO result FROM employee WHERE emp_name = param_name ;
IF result IS NULL THEN
select result;
ELSE
CALL getVolume(result, '');
END IF;
END $$
DELIMITER ;
SET ##GLOBAL.max_sp_recursion_depth = 255;
SET ##session.max_sp_recursion_depth = 255;
call getVolume('new', #result);
select #result;
The answer of #Shadow is correct. Here a practical example that can help you:
DELIMITER //
DROP TABLE IF EXISTS `employee`//
DROP PROCEDURE IF EXISTS `getVolume`//
CREATE TABLE `employee` (
`emp_name` VARCHAR(255),
`val` VARCHAR(255)
)//
INSERT INTO `employee` (`emp_name`, `val`)
VALUES
('demo', 'new'),
('new', 'd.new'),
('d.new', 'view'),
('view', 'hello'),
('hello', NULL)
//
CREATE PROCEDURE `getVolume`(
IN `param_name` VARCHAR(255),
OUT `result` VARCHAR(255)
)
BEGIN
DECLARE `next_param_name` VARCHAR(255);
SELECT `val` INTO `next_param_name`
FROM `employee`
WHERE `emp_name` = `param_name`;
IF `next_param_name` IS NULL THEN
SET `result` := `param_name`;
ELSE
-- CALL `getVolume`(`result`, '');
CALL `getVolume`(`next_param_name`, `result`);
-- SET `result` := CONCAT(`param_name`, ',', IFNULL(`result`, ''));
END IF;
END//
DELIMITER ;
SQL Fiddle demo
The problem is that when you call getResult() within getresult, you pass an empty string constant as a parameter:
CALL getVolume(result, '');
Declare a variable instead and pass that as a parameter. After the getVolume call do not forget to concatenate what is returned by getVolume to the result variable with a separator character. Are you sure that 255 characters will be enough to hold all values?

MySql stored functions: Not works

I have the following function:
DROP FUNCTION IF EXISTS saveTableRow;
DELIMITER $$
CREATE FUNCTION saveTableRow(adapter_id int(10), view_id int(10),name varchar(255)) RETURNS TINYINT(1)
BEGIN
DECLARE retOK TINYINT DEFAULT 0;
IF (SELECT COUNT(*) FROM `tables` WHERE `adapter_id`=adapter_id AND `view_id`=view_id AND `name`=name ) = 0 THEN
INSERT INTO `tables` (`adapter_id`,`view_id`,`name`) VALUES (adapter_id, view_id, name);
SET retOK = 1;
END IF;
RETURN retOK;
END;
$$
DELIMITER ;
When i call the function to insert a new row with
SELECT saveTableRow(3,1,'Text');
I get the result '0' and there is no new row saved.
It might be a name collision problem. The name of the column is the same with the name of you parameter. You need to change the name of your parameter that is different from the name of your column. eg,
DROP FUNCTION IF EXISTS saveTableRow;
DELIMITER $$
CREATE FUNCTION saveTableRow(
_adapter_id int(10),
_view_id int(10),
_name varchar(255))
RETURNS TINYINT(1)
BEGIN
DECLARE retOK TINYINT DEFAULT 0;
IF (SELECT COUNT(*)
FROM `tables`
WHERE `adapter_id`=_adapter_id AND
`view_id`=_view_id AND
`name`=_name ) = 0 THEN
INSERT INTO `tables` (`adapter_id`,`view_id`,`name`)
VALUES (_adapter_id, _view_id, _name);
SET retOK = 1;
END IF;
RETURN retOK;
END;
$$
DELIMITER ;
change the if as follows as try please:
IF ((SELECT COUNT(*) FROM `tables` WHERE `adapter_id`=adapter_id AND `view_id`=view_id AND `name`=name ) < 1)