how to set mysql stored procedures for update with variables - mysql

I am looking to automate a procedure I am handling manually now. So, I am new to stored procedures and the code below is not working.
It stops when it outputs the first set of variables of the SELECT statement. It does not do the UPDATE and does not continue with the WHILE loop. No errors are reported.
DELIMITER //
CREATE PROCEDURE ins_die_1()
BEGIN
DECLARE v_yr INT;
DECLARE v_col CHAR(10);
DECLARE v_base INT;
DECLARE v_factor INT;
DECLARE v_cf INT;
DECLARE v_fuel CHAR(10);
SET v_yr :=1;
SET v_col := '';
SET v_base := 2860;
SET v_factor := 1;
SET v_cf := 5;
SET v_fuel := 'diesel';
WHILE v_yr <= 5 DO
SET v_col := CONCAT('ins_yr',v_yr);
SELECT v_col, v_base, v_factor, v_fuel, v_cf;
UPDATE ct_insurance
SET v_col = (v_base + 1254 + (v_factor * .0032 * price ))
WHERE fuel = v_fuel
AND CF=v_cf
AND price * v_factor < 162000
AND version_id = 1746;
SET v_yr := v_yr+1;
IF v_yr = 2 THEN SET v_factor := .8;
ELSE SET v_factor := v_factor - 0.1;
END IF;
END WHILE;
END//
DELIMITER ;
I know that there is the IN construct, whereby I should enter the variables manually when calling the procedures, but that seems like an awkward solution and certainly not very efficient.
In case you wonder I have many variables, it's because this is a test procedure. Once it works, hopefully, I will expand it.
Also, I am looking for a mySql solution only, ie, no php involved.

Column names in the UPDATE statement are taken literally, variables don't substitute there. You can do it using dynamic SQL:
SET #sql = CONCAT('UPDATE ct_insurance
SET ', v_col, ' = (v_base + 1254 + (v_factor * .0032 * price ))
WHERE fuel = v_fuel
AND CF=v_cf
AND price * v_factor < 162000
AND version_id = 1746');
PREPARE stmt FROM #sql;
EXECUTE stmt;
However, you can't access procedure local variables from prepared statements (see here) so you need to use user-defined # variables.
DELIMITER //
CREATE PROCEDURE ins_die_1()
BEGIN
DECLARE v_yr INT;
DECLARE v_col CHAR(10);
SET v_yr :=1;
SET v_col := '';
SET #v_base := 2860;
SET #v_factor := 1;
SET #v_cf := 5;
SET #v_fuel := 'diesel';
SELECT v_col, #v_base, #v_factor, #v_fuel, #v_cf;
WHILE v_yr <= 5 DO
SET v_col := CONCAT('ins_yr',v_yr);
SET #sql = CONCAT('UPDATE ct_insurance
SET ', v_col, ' = (#v_base + 1254 + (#v_factor * .0032 * price ))
WHERE fuel = #v_fuel
AND CF = #v_cf
AND price * #v_factor < 162000
AND version_id = 1746');
PREPARE stmt FROM #sql;
EXECUTE stmt;
SET v_yr := v_yr+1;
IF v_yr = 2 THEN SET v_factor := .8;
ELSE SET v_factor := v_factor - 0.1;
END IF;
END WHILE;
END//

Related

How to combine for and bulkinsert in MySQL

I was creating the following SQL to insert 10,000,000 records into the users_bobbies table.
However, it is too slow to proceed.
How can I introduce a bulk insert into this WHILE statement?
I would appreciate it if you could tell me.
Version: MySQL5.7
drop procedure if exists insert_user_hobby_data;
DELIMITER $$
CREATE PROCEDURE insert_user_hobby_data()
BEGIN
DECLARE i INT DEFAULT 1;
SET #user_counter = 0;
WHILE #user_counter <= 9999999 DO
set #user_id = #user_counter + 1;
INSERT INTO users_bobbies (user_id, hobby_id)
VALUES (#user_id, 1);
SET #user_counter = #user_counter + 1;
END WHILE;
END$$
DELIMITER ;
CALL insert_user_hobby_data();
This code got it running.
DROP PROCEDURE IF EXISTS bulk_insert;
DELIMITER ;;
CREATE PROCEDURE bulk_insert(IN len INT, IN block INT)
BEGIN
SET #cnt = 0;
loop1: LOOP
SET #sql = 'INSERT INTO users_bobbies (user_id,hobby_id)VALUES';
SET #i = 0;
loop2: LOOP
IF #cnt >= len THEN LEAVE loop1; END IF;
SET #id = #cnt + 1;
SET #sql = CONCAT(#sql, ' (', #id, ',', #cnt+1, ')');
SET #cnt = #cnt + 1;
SET #i = #i + 1;
IF #i < block AND #cnt < len THEN SET #sql = CONCAT(#sql, ',');
ELSE LEAVE loop2; END IF;
END LOOP loop2;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP loop1;
END;;
DELIMITER ;
TRUNCATE users_bobbies; CALL bulk_insert(10000000, 1000);

return a value from a dynamic mysql and update a table with

I am working on an automatisation of queries in my database. I have 15 tables names the same way 'blablablaCOUNTRYblablabla' and i want to count the number of rows of each table and store them into an other table that actually contains just the list of the countries and and the table name.
I have decided to create a procedure
CREATE DEFINER=CURRENT USER PROCEDURE `PM`()
MODIFIES SQL DATA
BEGIN
DECLARE nom_table VARCHAR(1000);
DECLARE site_table_uniq VARCHAR(50);
DECLARE x INT;
DECLARE nb_ligne INT;
DECLARE site_coord VARCHAR(1000);
SET nom_table = "";
SET site_table_uniq = "";
SET x = 1;
SET nb_ligne = (SELECT COUNT(*) FROM SIG_PM_THERMO);
# SIG PM THERMO is the table i want to fill with the number of rows
WHILE x <= nb_ligne DO
SET site_table_uniq = (SELECT pmthermo.Site_table FROM SIG_PM_THERMO pmthermo WHERE pmthermo.Id = x);
SET #site_coord = (SELECT pmthermo.Site_coord FROM SIG_PM_THERMO pmthermo WHERE pmthermo.Id = 1) ;
SET #nom_table = (SELECT pm.Nom_table FROM SIG_PM_THERMO pm WHERE pm.Site_table = site_table_uniq );
SET #sql = CONCAT('SELECT SUM(nb_therm) FROM (SELECT COUNT(pmt.Temperature) AS nb_therm FROM ', #nom_table,' pmt WHERE pmt.Temperature !="-99" ) AS t ;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET x = x + 1;
END WHILE;
END
This display (each value in a tab with one line) all the value i need but i don't understand how to get them and store them in my table with a condition (that the countries are the same)
Would have any idea ?
Best regards (and happy new year)
You rewrite your query a little and add a varianble that stores the data
Every loop adds the session variable #sumthermo to the tolasum which is stored in an OUT variable
CREATE DEFINER=CURRENT_USER PROCEDURE `PM`(OUT _totalsum BIGINT)
MODIFIES SQL DATA
BEGIN
DECLARE nom_table VARCHAR(1000);
DECLARE site_table_uniq VARCHAR(50);
DECLARE x INT;
DECLARE nb_ligne INT;
DECLARE site_coord VARCHAR(1000);
SET _totalsum := 0;
SET nom_table = "";
SET site_table_uniq = "";
SET x = 1;
SET nb_ligne = (SELECT COUNT(*) FROM SIG_PM_THERMO);
# SIG PM THERMO is the table i want to fill with the number of rows
WHILE x <= nb_ligne DO
SET site_table_uniq = (SELECT pmthermo.Site_table FROM SIG_PM_THERMO pmthermo WHERE pmthermo.Id = x);
SET #site_coord = (SELECT pmthermo.Site_coord FROM SIG_PM_THERMO pmthermo WHERE pmthermo.Id = 1) ;
SET #nom_table = (SELECT pm.Nom_table FROM SIG_PM_THERMO pm WHERE pm.Site_table = site_table_uniq );
SET #sql = CONCAT('SELECT SUM(nb_therm) INTO #sumthermo FROM (SELECT COUNT(pmt.Temperature) AS nb_therm FROM ', #nom_table,' pmt WHERE pmt.Temperature !="-99" ) AS t ;');
SET _totalsum := _totalsum + #sumthermo ;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET x = x + 1;
END WHILE;
END
Now you can run the query and have the value stored in another sessionvariable
CALL PM(#totalsum);
What you also can do is save all the sums in another session varialble
CREATE DEFINER=CURRENT_USER PROCEDURE `PM`()
MODIFIES SQL DATA
BEGIN
DECLARE nom_table VARCHAR(1000);
DECLARE site_table_uniq VARCHAR(50);
DECLARE x INT;
DECLARE nb_ligne INT;
DECLARE site_coord VARCHAR(1000);
SET nom_table = "";
SET site_table_uniq = "";
SET x = 1;
SET nb_ligne = (SELECT COUNT(*) FROM SIG_PM_THERMO);
# SIG PM THERMO is the table i want to fill with the number of rows
WHILE x <= nb_ligne DO
SET site_table_uniq = (SELECT pmthermo.Site_table FROM SIG_PM_THERMO pmthermo WHERE pmthermo.Id = x);
SET #site_coord = (SELECT pmthermo.Site_coord FROM SIG_PM_THERMO pmthermo WHERE pmthermo.Id = 1) ;
SET #nom_table = (SELECT pm.Nom_table FROM SIG_PM_THERMO pm WHERE pm.Site_table = site_table_uniq );
SET #sql = CONCAT('SELECT SUM(nb_therm) INTO #sumthermo FROM (SELECT COUNT(pmt.Temperature) AS nb_therm FROM ', #nom_table,' pmt WHERE pmt.Temperature !="-99" ) AS t ;');
SET #totalsum := #totalsum + #sumthermo ;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET x = x + 1;
END WHILE;
END
Now you only execute the procudre with
call PM();
and have also the values in #totalsum
The OUT variable is preferable, when you use python or other languages, that can run stored procedures

MySQL - INSERT INTO SELECT using subqueries for the column names and values [duplicate]

I have create a trigger which is create a dynamic query.and execute it i had tried 'EXECU q' but it does not work. how can i run/execute that dynamic query.
BEGIN
DECLARE a INT Default 0 ;
DECLARE str VARCHAR(255);
DECLARE q VARCHAR(500);
SET q = 'insert into '+new.master_name+' values(';
simple_loop: LOOP
SET a=a+1;
SET str = SPLIT_STRING(new.remarks,"|",a);
SET q = CONCAT(q,str+',');
SET q = LEFT(q, LENGTH(q) - 1);
IF str='' THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
SET q = CONCATE(q,');');
EXEC q
END
This is Trigerr
this is Function which i made
RETURN REPLACE(
SUBSTRING(
SUBSTRING_INDEX(str , delim , pos) ,
CHAR_LENGTH(
SUBSTRING_INDEX(str , delim , pos - 1)
) + 1
) ,
delim ,
''
)
I've written a stored procedure to execute dynamically constructed sql statements.
Usage
SET #index := 7;
CALL eval(CONCAT('SELECT ', #index));
Implementation
DELIMITER $$
CREATE PROCEDURE eval(IN dynamic_statement TEXT)
BEGIN
SET #dynamic_statement := dynamic_statement;
PREPARE prepared_statement FROM #dynamic_statement;
EXECUTE prepared_statement;
DEALLOCATE PREPARE prepared_statement;
END$$
DELIMITER ;
From my understanding you must make a prepared statement from your string first in order to execute it. So the following partial code should work in replacement for just EXEC q:
PREPARE thequery FROM q;
EXECUTE thequery;
use prepare statement to execute your dynamic query
BEGIN
DECLARE a INT Default 0 ;
DECLARE str VARCHAR(255);
DECLARE q VARCHAR(500);
DECLARE q1 VARCHAR(500);
DECLARE q2 VARCHAR(500);
SET #q = 'insert into '+new.master_name+' values(';
simple_loop: LOOP
SET a=a+1;
SET str = SPLIT_STRING(new.remarks,"|",a);
SET q = CONCAT(#q,str+',');
SET q1 = LEFT(q, LENGTH(q) - 1);
IF str='' THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
SET q2 = CONCATE(q1,');');
PREPARE stmt FROM q2;
EXECecute stmt;
deallocate PREPARE stmt;
END

How to Execute Dynamic Sql String as a Query in mysql server?

I have create a trigger which is create a dynamic query.and execute it i had tried 'EXECU q' but it does not work. how can i run/execute that dynamic query.
BEGIN
DECLARE a INT Default 0 ;
DECLARE str VARCHAR(255);
DECLARE q VARCHAR(500);
SET q = 'insert into '+new.master_name+' values(';
simple_loop: LOOP
SET a=a+1;
SET str = SPLIT_STRING(new.remarks,"|",a);
SET q = CONCAT(q,str+',');
SET q = LEFT(q, LENGTH(q) - 1);
IF str='' THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
SET q = CONCATE(q,');');
EXEC q
END
This is Trigerr
this is Function which i made
RETURN REPLACE(
SUBSTRING(
SUBSTRING_INDEX(str , delim , pos) ,
CHAR_LENGTH(
SUBSTRING_INDEX(str , delim , pos - 1)
) + 1
) ,
delim ,
''
)
I've written a stored procedure to execute dynamically constructed sql statements.
Usage
SET #index := 7;
CALL eval(CONCAT('SELECT ', #index));
Implementation
DELIMITER $$
CREATE PROCEDURE eval(IN dynamic_statement TEXT)
BEGIN
SET #dynamic_statement := dynamic_statement;
PREPARE prepared_statement FROM #dynamic_statement;
EXECUTE prepared_statement;
DEALLOCATE PREPARE prepared_statement;
END$$
DELIMITER ;
From my understanding you must make a prepared statement from your string first in order to execute it. So the following partial code should work in replacement for just EXEC q:
PREPARE thequery FROM q;
EXECUTE thequery;
use prepare statement to execute your dynamic query
BEGIN
DECLARE a INT Default 0 ;
DECLARE str VARCHAR(255);
DECLARE q VARCHAR(500);
DECLARE q1 VARCHAR(500);
DECLARE q2 VARCHAR(500);
SET #q = 'insert into '+new.master_name+' values(';
simple_loop: LOOP
SET a=a+1;
SET str = SPLIT_STRING(new.remarks,"|",a);
SET q = CONCAT(#q,str+',');
SET q1 = LEFT(q, LENGTH(q) - 1);
IF str='' THEN
LEAVE simple_loop;
END IF;
END LOOP simple_loop;
SET q2 = CONCATE(q1,');');
PREPARE stmt FROM q2;
EXECecute stmt;
deallocate PREPARE stmt;
END

How to pass variable to UPDATE mysql query

I wanted to replace a value (1 > -1) in a table with the following commands
UPDATE table_name
SET column_name = replace(column_name, '1', '-1');
However,I decided to learn how to use stored programs in Mysql, since the number of columns are large with their regularly formatted namesn ('i01', 'i02',...).
Below is my trial:
DELIMITER $$
DROP PROCEDURE IF EXISTS example$$
CREATE PROCEDURE example()
BEGIN
DECLARE p INT;
DECLARE str VARCHAR(20);
SET p = 1;
WHILE p < 100 DO
IF p <= 9 THEN SET str = CONCAT('i0', p);
ELSE SET str = CONCAT('i', p);
END IF;
UPDATE target_table
SET `str` = replace(str, '1', '-1');
SET p = p + 1;
END WHILE;
END$$
When I source the script, which was OK and call the function, there says ERROR 1054 (42S22): Unknown column 'str' in 'field list.'
How can I pass the variable, in this case #str, inside UPDATE query?
I searched online and found PREPARE could be an answer but could not figure out how to use that in my case.
Yes, you were on the right track with PREPARE.. the only issue is you have to concate the query string with the variable name outside of the string to access its contents.
SET #A = (SELECT CONCAT("UPDATE target_table SET `", #str, "` = replace(str, '1', '-1');"));
PREPARE qry FROM #A;
EXECUTE qry;
DEALLOCATE PREPARE qry;
if the replace you have is related to your variable #str (because you dont have the # sign there I'm not sure if its an actual column or the variable) then you need to change #A a little bit
SET #A = (SELECT CONCAT("UPDATE target_table SET `", #str, "` = replace(", #str, ", '1', '-1');"));
DEMO
with your DECLAREs this should be your final query
DELIMITER $$
DROP PROCEDURE IF EXISTS example$$
CREATE PROCEDURE example()
BEGIN
DECLARE p INT;
DECLARE str VARCHAR(20);
DECLARE update_qry VARCHAR(200);
SET p = 1;
WHILE p < 100 DO
-- set up column name
IF p <= 9
THEN SET str = CONCAT('i0', p);
ELSE SET str = CONCAT('i', p);
END IF;
-- set up query to execute
SET update_qry = CONCAT("UPDATE target_table SET `", str, "` = replace(", str, ", '1', '-1');")
-- prepare execute and deallocate query
PREPARE qry FROM update_qry;
EXECUTE qry;
DEALLOCATE PREPARE qry;
-- increment counter for next column name
SET p = p + 1;
END WHILE;
END$$