MySQL local variable value changes unexpectedly - mysql

I have a table with following prototype:
CREATE TABLE `T2` (
`ID` int(11) DEFAULT NULL,
`NAME` varchar(100) DEFAULT NULL
)
I want to create procedure to insert random strings with a specific length and I wrote the following:
DELIMITER ;;
CREATE PROCEDURE STRGEN ( IN ITER INT) BEGIN
DECLARE COUNTER INT DEFAULT 0;
DECLARE _STR VARCHAR(1000) DEFAULT NULL;
WHILE COUNTER < #ITER DO
SET _STR = CONCAT(_STR , CHAR(RAND()*100+65));
SET COUNTER = COUNTER + 1;
END WHILE;
INSERT INTO T2 VALUES(RAND()*100 , _STR);
END;;
And called it this way:
CALL STRGEN(100);
The problem is that it inserts NULL as the NAME field.
My suspicion is that changes made to _STR goes out of scope when the while loop terminates.
I've also tried using a global variable like the following:
CREATE PROCEDURE STRGEN ( IN ITER INT) BEGIN
DECLARE COUNTER INT DEFAULT 0;
DECLARE _STR VARCHAR(1000) DEFAULT NULL;
SET #S = NULL;
WHILE (COUNTER < #ITER) DO
SET _STR = CONCAT(_STR , CHAR(RAND()*100+65));
SET COUNTER = COUNTER + 1;
SET #S = _STR;
END WHILE;
INSERT INTO T2 VALUES(RAND()*100 , #S);
END;;
And also declaring #S outside of the procedure , but nothing special happened.
Any help is appreciated.
Best regards.

When I tried running your procedure, I got the following error:
Error Code: 1366. Incorrect string value: '\x87' for column '_STR' at
row 1 0.000 sec
It seems that you are not generating valid chars... I tried a different char generator logic and it works as expected.
DELIMITER ;;
CREATE PROCEDURE STRGEN ( IN ITER INT) BEGIN
DECLARE COUNTER INT DEFAULT 0;
DECLARE _STR VARCHAR(1000) DEFAULT ''; -- change the initial value to ''
WHILE COUNTER < #ITER DO
-- SET _STR = CONCAT(_STR , CHAR(RAND()*100+65));
-- if I change the logic for generating random char, then I get expected result
SET _STR = CONCAT(_STR , LEFT(MD5(RAND()), 1));
SET COUNTER = COUNTER + 1;
END WHILE;
INSERT INTO T2 VALUES(RAND()*100 , _STR);
END;;

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 said: #1338 - Cursor declaration after handler declaration

I'm on developing web project and i get some problem with migration from oracle database to mysql database. I want to create function with this code :
DROP FUNCTION IF EXISTS F_MANIFEST_GABUNG_SMR;
DELIMITER //
CREATE FUNCTION F_MANIFEST_GABUNG_SMR (input_val varchar(4000))
RETURNS VARCHAR(4000)
BEGIN
DECLARE return_text VARCHAR(10000) DEFAULT NULL;
DECLARE not_found INT DEFAULT 0;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET not_found = 1;
DECLARE x CURSOR FOR SELECT DISTINCT IFNULL(SMR,'-') SMR FROM MANIFEST_EDI_SMR WHERE BL_NBR = input_val; OPEN x;
FETCH x INTO;
WHILE NOT_FOUND=0
DO
SET return_text = concat(ifnull(return_text, '') , ' ' , IFNULL(x.SMR, '')) ;
FETCH INTO;
END WHILE;
CLOSE ;
IF char_length(return_text) > 85 THEN
SET return_text = concat(ifnull(substr(return_text,1,85), '') , ' detail asp BL');
END IF;
RETURN return_text;
END;
//
DELIMITER ;
I am using phpmyadmin to store function with routine. Thanks for your help :)
The error message is self explanatory:you have declare the cursor after handler,need to change the order:
DROP FUNCTION IF EXISTS F_MANIFEST_GABUNG_SMR;
DELIMITER //
CREATE FUNCTION F_MANIFEST_GABUNG_SMR (input_val varchar(4000))
RETURNS VARCHAR(4000)
BEGIN
DECLARE return_text VARCHAR(10000) DEFAULT NULL;
DECLARE not_found INT DEFAULT 0;
-- Declare cursor before handler
DECLARE x CURSOR FOR SELECT DISTINCT IFNULL(SMR,'-') SMR
FROM MANIFEST_EDI_SMR WHERE BL_NBR = input_val; OPEN x;
-- handler need to be after cursor
DECLARE CONTINUE HANDLER FOR NOT FOUND SET not_found = 1;
FETCH x INTO;
WHILE NOT_FOUND=0
DO
SET return_text = concat(ifnull(return_text, '') , ' ' , IFNULL(x.SMR, '')) ;
FETCH INTO;
END WHILE;
CLOSE ;
IF char_length(return_text) > 85 THEN
SET return_text = concat(ifnull(substr(return_text,1,85), '') , ' detail asp BL');
END IF;
RETURN return_text;
END;
//
DELIMITER ;
More details can be found at:https://dev.mysql.com/doc/refman/8.0/en/cursors.html

MySQL Error by creating Stored Procedure

I want to make a Stored Procedure that handles an order from our site. The problem is that when I run the script there is a MySQL syntax error. I'm very new with MySQL Stored Procedures. Can someone look at my code?
USE postbrood;
DELIMITER //
CREATE PROCEDURE MaakBestelling(IN KlantIDParam INT, IN ProductIDArray VARCHAR(255), IN AantalArray VARCHAR(255))
BEGIN
DECLARE BestelID INT DEFAULT 0;
DECLARE ArrayLenght INT DEFAULT 0;
DECLARE Counter INT DEFAULT 0;
SET ArrayLenght = LENGTH(ProductIDArray) - LENGTH(REPLACE(ProductIDArray, ',', '')) + 1;
INSERT INTO bestelling(klantID) VALUES (KlantIDParam);
SET BestelID = LAST_INSERT_ID();
WHILE Counter < ArrayLenght DO
INSERT INTO bestelregel VALUES (SUBSTRING_INDEX(ProductIDArray,',',Counter),BestelID,SUBSTRING_INDEX(AantalArray,',',Counter));
SET Counter = Counter + 1;
END WHILE;
END//
DELIMITER ;
Thanks in advance!
Nailed it! :)
USE postbrood;
DELIMITER //
CREATE PROCEDURE MaakBestelling(IN KlantIDParam INT, IN ProductIDArray VARCHAR(255), IN AantalArray VARCHAR(255))
BEGIN
DECLARE BestelID INT DEFAULT 0;
DECLARE ArrayLenght INT DEFAULT 0;
DECLARE Counter INT DEFAULT 0;
SET ArrayLenght = LENGTH(ProductIDArray) - LENGTH(REPLACE(ProductIDArray, ',', '')) + 1;
INSERT INTO bestelling(klantID) VALUES (KlantIDParam);
SET BestelID = LAST_INSERT_ID();
WHILE Counter < ArrayLenght DO
INSERT INTO bestelregel VALUES (SUBSTRING_INDEX(ProductIDArray,',',Counter),BestelID,SUBSTRING_INDEX(AantalArray,',',Counter),2.50);
SET Counter = Counter + 1;
END WHILE;
END//
DELIMITER ;
I think the comma is not being recognized at
SET ArrayLenght = LENGTH(ProductIDArray) - LENGTH(REPLACE(ProductIDArray, ',', '')) + 1;
You should try replacing the comma with the ascii entity number: ,

Error in SET command in a MySQL Procedure

I wish to retrieve all distinct values of a column (Task in this case) from a table (dataset in this case). The task values will go as column name for the table that I intend to create using the procedure (albeit not defined currently but only printed). But it gives an error at the line
SET qry = CONCAT("CREATE TABLE ",tblname," ( TEAM varchar(20) PRIMARY KEY ," );
saying end was missing. When I put end it says semicolon is missing and vice-versa.
DELIMITER //
CREATE PROCEDURE `createTable` (IN tblName varchar(20))
BEGIN
DECLARE task varchar(20) DEFAULT "";
DECLARE exit_loop BOOLEAN;
SET qry = CONCAT("CREATE TABLE ",tblname," ( TEAM varchar(20) PRIMARY KEY ," );
DECLARE task_curs CURSOR FOR SELECT DISTINCT Task FROM dataset;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
OPEN task_curs;
my_loop: loop
FETCH task_curs into task;
qry=CONCAT(qry,task, ' INT DEFAULT 0, ');
IF exit_loop THEN
CLOSE task_curs;
LEAVE my_loop;
END IF;
END LOOP my_loop;
qry=TRIM(TRAILING ',' FROM qry);
qry=CONCAT(qry, ');');
SELECT qry;
END; //
DELIMITER ;
What might be possibly wrong with the syntax ? Thanks!
Try:
DELIMITER //
CREATE PROCEDURE `createTable` (IN tblName varchar(20))
BEGIN
DECLARE task varchar(20) DEFAULT "";
DECLARE exit_loop BOOLEAN;
DECLARE qry VARCHAR(500);
-- SET qry = CONCAT("CREATE TABLE ",tblname," ( TEAM varchar(20) PRIMARY KEY ," );
DECLARE task_curs CURSOR FOR SELECT DISTINCT Task FROM dataset;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
SET qry = CONCAT("CREATE TABLE ",tblname," ( TEAM varchar(20) PRIMARY KEY ," );
OPEN task_curs;
my_loop: loop
FETCH task_curs into task;
-- qry=CONCAT(qry,task, ' INT DEFAULT 0, ');
-- SET qry=CONCAT(qry,task, ' INT DEFAULT 0, ');
IF exit_loop THEN
CLOSE task_curs;
LEAVE my_loop;
END IF;
SET qry=CONCAT(qry,task, ' INT DEFAULT 0, ');
END LOOP my_loop;
-- qry=TRIM(TRAILING ',' FROM qry);
SET qry=TRIM(TRAILING ',' FROM qry);
-- qry=CONCAT(qry, ');');
SET qry=CONCAT(qry, ');');
SELECT qry;
END; //
DELIMITER ;

Compare String function in mysql by words

I am trying to create a search function in mysql. In order to make the search result more reliability i need to compare two string by words. Inputs are 2 strings and output is number word two strings match. In MySql i did as below.
CREATE DEFINER=`root`#`localhost` FUNCTION `CompareStrings`(str1 VARCHAR(255),str2 VARCHAR(255)) RETURNS double
BEGIN
DECLARE cur_position INT DEFAULT 1 ;
DECLARE remainder TEXT;
DECLARE cur_string VARCHAR(50);
DECLARE delimiter_length TINYINT UNSIGNED;
DECLARE numberMatch INT;
DECLARE total INT;
DECLARE result DOUBLE DEFAULT 0;
DECLARE delim VARCHAR(10);
DECLARE string2 VARCHAR(255);
SET delim = ' ';
DROP TEMPORARY TABLE IF EXISTS SplitString1;
CREATE TEMPORARY TABLE SplitString1 (
SplitString1ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT ,
val VARCHAR(50) NOT NULL
) ENGINE=MyISAM;
DROP TEMPORARY TABLE IF EXISTS SplitString2;
CREATE TEMPORARY TABLE SplitString2 (
SplitString1ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT ,
val VARCHAR(50) NOT NULL
) ENGINE=MyISAM;
SET remainder = str1;
SET delimiter_length = CHAR_LENGTH(delim);
WHILE CHAR_LENGTH(remainder) > 0 AND cur_position > 0 DO
SET cur_position = INSTR(remainder, delim);
IF cur_position = 0 THEN
SET cur_string = remainder;
ELSE
SET cur_string = LEFT(remainder, cur_position - 1);
END IF;
IF TRIM(cur_string) != '' THEN
INSERT INTO SplitString1(val) VALUES (cur_string);
END IF;
SET remainder = SUBSTRING(remainder, cur_position + delimiter_length);
END WHILE;
SET remainder = str2;
SET cur_position = 1;
WHILE CHAR_LENGTH(remainder) > 0 AND cur_position > 0 DO
SET cur_position = INSTR(remainder, delim);
IF cur_position = 0 THEN
SET cur_string = remainder;
ELSE
SET cur_string = LEFT(remainder, cur_position - 1);
END IF;
IF TRIM(cur_string) != '' THEN
INSERT INTO SplitString2(val) VALUES (cur_string);
END IF;
SET remainder = SUBSTRING(remainder, cur_position + delimiter_length);
END WHILE;
SELECT count(*) INTO numberMatch
FROM SplitString1 s1 JOIN SplitString2 s2 ON s1.val = s2.val;
RETURN result;
END
The idea is create two temporary table store each word and then compare these 2 tables. The result is good but the performace is awful. Anybody has better idea, please give me an advice.
Many thanks!
I don't think this will work as stated.
The logic is sound but you have not assigned any value to your result variable. Hence this function will always return 0. Replace:
RETURN result;
with
RETURN numberMatch;
Also replace:
CREATE DEFINER=`root`#`localhost` FUNCTION `CompareStrings`(str1 VARCHAR(255),str2 VARCHAR(255)) RETURNS double
with
CREATE DEFINER=`root`#`localhost` FUNCTION `CompareStrings`(str1 VARCHAR(255),str2 VARCHAR(255)) RETURNS double READS SQL DATA
As far as efficiency goes it looks pretty efficient. When you say 'performance is awful' - what constitutes 'awful'? Have you got any benchmark figures e.g. x calls took y millis?