Pass variable as column name in stored procedure - mysql

SET myvar = MONTHNAME(NOW());
SET #q = CONCAT('Insert into sales', myvar,'customer values (1000, ABC);','Insert into sales', myvar,'customer values (2000, XYZ);');
PREPARE QUERY from #q;
EXECUTE QUERY;
END //
How can we pass variable value as column name into query and concat multiple queries ?

You must have a valid INSERT INTO query with placeholders, and run the code as many times as you need.
This can be used in a loop with a cursor and much more:
DELIMITER $$
CREATE PROCEDURE mytest()
BEGIN
SET myvar = MONTHNAME(NOW());
SET #q = CONCAT('Insert into sales (', myvar,'customer) values (?, ?);');
PREPARE QUERY from #q;
SET #a = 1000;
SET #b = "ABC";
EXECUTE QUERY USING #a, #b;
SET #a = 2000;
SET #b = "XYZ";
EXECUTE QUERY USING #a, #b;
DEALLOCATE PREPARE QUERY;
END$$
DELIMITER ;

Related

Set ROUND() function for entire mysql query

Is there a way to apply ROUND function for entire select statement in MYSQL instead of using it for every column?
I want to avoid repeating ROUND
SELECT
ROUND(col_1,2),
ROUND(col_2, 2),
ROUND(col_3,2)
FROM
TABLE_NAME
Could not find anything related in documentation.
If you mean to avoid repetition in code, here is a trick for 100 columns:
SET #q = 'SELECT ';
SET #i = 0;
WHILE #i < 100 DO
SET #q = CONCAT(#q, 'ROUND(col_',#i,',2), ');
SET #i = #i + 1;
END WHILE;
SET #q = CONCAT(SubStr(#q, 1, LENGTH(#q) - 2),' FROM TABLE_NAME');
PREPARE stmt FROM #q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
You need to put it on a procedure and then call it.

MySQL said: #1336 - Dynamic SQL is not allowed in stored function or trigger

This is main question please help me for this
This is my code :
BEGIN
DECLARE TMPCOL VARCHAR(100);
SET TMPCOL= 'ID';
SET #s = CONCAT('INSERT INTO TMP(DATA1, DATA2) VALUES ("DATA",OLD.',TMPCOL,');');
PREPARE stmt1 FROM #s;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END
You don't need dynamic SQL. This is effectively the same:
BEGIN
INSERT INTO TMP (DATA1, DATA2)
VALUES ('DATA', OLD.ID);
END;
This should work in a trigger. Otherwise, old is not defined.

How to convert mysql/mariadb row in triggers (NEW/OLD) into json

how i can convert a row inside a mysql/mariadb trigger into an json object with new JSON features?
BEGIN
CALL my_audit_insert(tableName, id, ... JSON_OBJECT(NEW) ...);
END
Is there any possibility to get programatically columns of NEW or OLD?
First Try - Create a Statement
Idea is to get colums from system tables and get each value from NEW/OLD programatically
BEGIN
SET #s = 'SELECT NEW.? INTO #result';
PREPARE stmt FROM #s;
SET #a = 'id';
EXECUTE stmt USING #a;
CALL audit_insert(NEW.id, 'pages', JSON_ARRAY(result));
END
(1336): Dynamic SQL is not allowed in stored function or trigger
Second Idea - Select the row via PrimaryKey as JSON_Object in after-triggers
procedure spGetJson from https://stackoverflow.com/a/35957518/7080961
DROP PROCEDURE IF EXISTS `spGetJson`;
DELIMITER //
CREATE DEFINER=`root`#`%` PROCEDURE `spGetJson`(pTableName varchar(45), pId int, out pJson JSON)
begin
select group_concat(concat("'", COLUMN_NAME, "', ", COLUMN_NAME) separator ',')
into #cols
from information_schema.columns
where TABLE_NAME = pTableName and TABLE_SCHEMA = database();
set #q = concat('select json_object(', #cols, ') INTO #a from ', pTableName);
if pId is not null then
set #q = concat(#q, ' where id = ', pId);
end if;
set #q = concat(#q, ';');
prepare statement from #q;
execute statement;
deallocate prepare statement;
SET pJson = #a;
end//
DELIMITER;
After Insert Trigger:
BEGIN
CALL spGetJson('pages', NEW.id, #a);
CALL audit_insert(NEW.id, 'pages', #a);
END
same: (1336): Dynamic SQL is not allowed in stored function or trigger
Conclusion:
have to wait for this feature: https://bugs.mysql.com/bug.php?id=89366
or switch to postresql

MySql, split a string and insert into table

I have two inputs for my stored procedure. One is the 'RoledID' and second one is the 'MenuIDs'. 'MenusIDs' is a list of comma separated menus ids that need to be inserted with RoledID. RoleId is just an INT and we need to put this RoledID against each MenuID. My table 'RolesMenus' contains two columns one for MenuID and one for RoleID.
Now I need to split MenuIDs and insert each MenuID with RoleID.
How can I write a stored procedure for it?
You can build one INSERT query (because statement allows to insert multiple records) and run it with prepared statements, e.g. -
SET #MenuIDs = '1,2,3';
SET #RoledID = 100;
SET #values = REPLACE(#MenuIDs, ',', CONCAT(', ', #RoledID, '),('));
SET #values = CONCAT('(', #values, ', ', #RoledID, ')'); -- This produces a string like this -> (1, 100),(2, 100),(3, 100)
SET #insert = CONCAT('INSERT INTO RolesMenus VALUES', #values); -- Build INSERT statement like this -> INSERT INTO RolesMenus VALUES(1, 100),(2, 100),(3, 100)
-- Execute INSERT statement
PREPARE stmt FROM #insert;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
As you see, it can be done without stored procedure.
Give this a go. It may need some tweaking if the MenuIDs string does not conform to 'menuId,menuId,menuId'.
Also I do not know what data type the menuId column is in your target table (INT?) so you may have to put some numeric checking in too (in case '1,2,3,banana,4,5' is passed in as the MenuIds input parameter).
DELIMITER $$
DROP PROCEDURE IF EXISTS `insert_role_menuids`$$
CREATE PROCEDURE `insert_role_menuids`(IN RoleID INT,IN MenuIDs varchar(500))
BEGIN
declare idx,prev_idx int;
declare v_id varchar(10);
set idx := locate(',',MenuIDs,1);
set prev_idx := 1;
WHILE idx > 0 DO
set v_id := substr(MenuIDs,prev_idx,idx-prev_idx);
insert into RolesMenus (RoleId,MenuId) values (RoleID,v_id);
set prev_idx := idx+1;
set idx := locate(',',MenuIDs,prev_idx);
END WHILE;
set v_id := substr(MenuIDs,prev_idx);
insert into RolesMenus (RoleId,MenuId) values (RoleID,v_id);
END$$
DELIMITER ;
for this solution, you must create a table with the name split_table, it can have a id(autoincrement) if you need it and must have a column where to store the value (I call it valor)
DELIMITER $$
USE `dbaname`$$
DROP PROCEDURE IF EXISTS `Split`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `Split`(
IN cadena VARCHAR(8000),
IN delimitador VARCHAR(10)
)
BEGIN
TRUNCATE split_table;
SET #posicion = 1;
SET #ldel = LENGTH(delimitador);
SET #valor = SUBSTRING_INDEX(cadena, delimitador, 1);
WHILE #valor <> '' AND #posicion > 0 DO
SET #valor = SUBSTRING_INDEX(cadena, delimitador, 1);
INSERT INTO split_table(valor) VALUES (#valor);
SET #posicion = POSITION(delimitador IN cadena);
SET #largo = LENGTH(cadena);
IF #largo >= #posicion THEN
SET cadena = SUBSTR(cadena, #posicion + #ldel, #largo - #posicion);
SET #valor = SUBSTRING_INDEX(cadena, delimitador, 1);
ELSE
SET #posicion = 0;
END IF;
END WHILE;
END$$
DELIMITER ;
First create procedure
CREATE DEFINER=`root`#`localhost` PROCEDURE `split_str_save_to_tmp_table`(
IN _str TEXT,
IN _table_name VARCHAR(80)
)
BEGIN
#DROP FIRST OLD TABLE
SET #q = CONCAT('DROP TEMPORARY TABLE IF EXISTS ', _table_name);
PREPARE st FROM #q;
EXECUTE st;
#CREATE TABLE
SET #q = CONCAT('CREATE TEMPORARY TABLE ', _table_name, '(id INT UNSIGNED NOT NULL PRIMARY KEY (id) )' );
PREPARE st FROM #q;
EXECUTE st;
SET #ids = REPLACE(_str, ',', '),(');
SET #ids = CONCAT('(', #ids, ')');
#INSERT INTO TABLE
SET #q = CONCAT('INSERT INTO ' , _table_name ,' VALUES');
SET #q = CONCAT(#q, #ids);
PREPARE st FROM #q;
EXECUTE st;
DEALLOCATE PREPARE st;
END
Then call
call split_str_save_to_tmp_table('1,2,3,4,5', 'tmp_split_product');
SELECT * FROM tmp_split_product
AFAIK MySQL does not have a function to split strings. Here is the MySQL manual for string related functions. In the comments section should be some information about workarounds for splitting string with substring-functions but not really usable:
MySQL manual

use a variable for table name in mysql sproc

I'm trying to pass a table name into my mysql stored procedure to use this sproc to select off of different tables but it's not working...
this is what I"m trying:
CREATE PROCEDURE `usp_SelectFromTables`(
IN TableName varchar(100)
)
BEGIN
SELECT * FROM #TableName;
END
I've also tried it w/o the # sign and that just tells me that TableName doesn't exist...which I know :)
SET #cname:='jello';
SET #vname:='dwb';
SET #sql_text = concat('select concept_id,concept_name,',#vname,' from enc2.concept a JOIN enc2.ratings b USING(concept_id) where concept_name like (''%',#cname,'%'') and 3 is not null order by 3 asc');
PREPARE stmt FROM #sql_text;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
An extra bit that caused me problems.
I wanted to set the table name and field dynamically in a query as #kyle asked, but I also wanted to store the result of that query into a variable #a within the query.
Instead of putting the variable #a into the concat literally, you need to include it as part of the string text.
delimiter //
CREATE PROCEDURE removeProcessed(table_name VARCHAR(255), keyField VARCHAR(255), maxId INT, num_rows INT)
BEGIN
SET #table_name = table_name;
SET #keyField = keyField;
SET #maxId = maxId;
SET #num_rows = num_rows;
SET #sql_text1 = concat('SELECT MIN(',#keyField,') INTO #a FROM ',#table_name);
PREPARE stmt1 FROM #sql_text1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
loop_label: LOOP
SET #sql_text2 = concat('SELECT ',#keyField,' INTO #z FROM ',#table_name,' WHERE ',#keyField,' >= ',#a,' ORDER BY ',#keyField,' LIMIT ',#num_rows,',1');
PREPARE stmt2 FROM #sql_text2;
EXECUTE stmt2;
DEALLOCATE PREPARE stmt2;
...Additional looping code...
END LOOP;
END
//
delimiter ;
So in #sql_text1 assign the result of the query to #a within the string using:
') INTO #a FROM '
Then in #sql_text2 use #a as an actual variable:
,' WHERE ',#keyField,' >= ',#a,' ORDER BY '
It depends on the DBMS, but the notation usually requires Dynamic SQL, and runs into the problem that the return values from the function depend on the inputs when it is executed. This gives the system conniptions. As a general rule (and therefore probably subject to exceptions), DBMS do not allow you to use placeholders (parameters) for structural elements of a query such as table names or column names; they only allow you to specify values such as column values.
Some DBMS do have stored procedure support that will allow you to build up an SQL string and then work with that, using 'prepare' or 'execute immediate' or similar operations. Note, however, that you are suddenly vulnerable to SQL injection attacks - someone who can execute your procedure is then able to control, in part, what SQL gets executed.