use a variable for table name in mysql sproc - mysql

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.

Related

How to loop comma separated values into select statement in MySQL stored procedure

I have to send comma separated values into a select statement where it will update values through #sql statement.
I have common table in all Databases I need to update the table column by one update statement in the procedure.
For Example : Input Param will be ('DataBase1','Database2',....., 'Database10')
Below is the sample procedure :
DELIMITER &&
CREATE PROCEDURE update_stmt (IN DBName varchar(100))
BEGIN
Declare DBName = #DB;
**comma seperated values loop and placed into the #DB**
use #DB;
SELECT concat(update #DB.sample SET COL = 0 where ID = \'',ID,'\','; ) as stmt FROM
Test.Sample into #s;
SET #sql = #s
PREPARE stmt from #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END &&
DELIMITER ;
so that update statement will execute in each of the databases.
Here's another approach. I don't try to split the comma-separated string, I use it with FIND_IN_SET() to match schema names in INFORMATION_SCHEMA.TABLES. This filters to schemas in the list that actually exist, and tables that actually exist in that schema.
Then use a cursor to loop over the matching rows, so you don't have to split any strings, which is awkward to do in a stored procedure.
I supposed that you would want to specify the id of the row to update too, so I added that to the procedure parameters.
Also notice the use of quotes when I create #sql. You can concatenate strings, but those must be quote-delimited like any other string literal. Variables must not be inside the quoted string. There's no feature to expand variables inside string literals in MySQL.
DELIMITER &&
CREATE PROCEDURE update_stmt (IN schema_name_list VARCHAR(100), IN in_id INT)
BEGIN
DECLARE done INT DEFAULT false;
DECLARE schema_name VARCHAR(64);
DECLARE cur1 CURSOR FOR
SELECT TABLE_SCHEMA FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'sample' AND FIND_IN_SET(TABLE_SCHEMA, schema_name_list);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = true;
SET #id = in_id;
OPEN cur1;
schema_loop: LOOP
FETCH cur1 INTO schema_name;
IF done THEN
LEAVE schema_loop;
END IF;
SET #sql = CONCAT('UPDATE `', schema_name, '`.sample SET col = 0 WHERE id = ?');
PREPARE stmt FROM #sql;
EXECUTE stmt USING #id;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur1;
END &&
DELIMITER ;
Frankly, I hardly ever use stored procedures in MySQL. The procedure language is primitive, and the tasks I see people try to do in stored procedures could be done a lot more easily in virtually every other programming language.

OUT parameter of MySQL stored procedure is null after calling stored procedure

I've created a stored procedure to return a value for row count of any table I pass in as an "IN" parameter, and output that rowcount to an OUT parameter
PROCEDURE `GetCount`(in tblname varchar(255), out rowcount int)
BEGIN
SET #sql_text1 = concat('SELECT COUNT(*) FROM ',tblname);
SET #sql_text2 = concat(#sql_text1,' INTO ');
SET #sql_final = concat(#sql_text2, rowcount);
PREPARE stmt1 FROM #sql_text1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END
when I open a query window in MySQL workbench and do the following:
set #tablename = 'my_table_name;
set #cnt = -9998;
call GetCount(#tablename,#cnt);
SELECT #cnt;
the value of #cnt is NULL.
Is my method of testing the stored proc incorrect, or is the stored proc to blame?
Your test methodology is valid, but you have made three errors in the procedure.
Error #1 you are using the wrong variable for the prepared statement.
PREPARE stmt1 FROM #sql_text1;
This should have been...
PREPARE stmt1 FROM #sql_final;
Error #2 This doesn't do what you intend:
SET #sql_final = concat(#sql_text2, rowcount);
This concatenates the value of #sql_text2 with the value of rowcount. Since rowcount is an out parameter, it is null at this point. If any argument to CONCAT() is null, then the result is also null, so you are actually setting #sql_final to null. If not for error #1, either the PREPARE or subsequent EXECUTE would have thrown an error about ...the right syntax to use near NULL at line 1.
Here's what you actually intended, the literal string 'rowcount':
SET #sql_final = concat(#sql_text2, 'rowcount');
...but that would also fail, because rowcount is a program variable. Prepared statements run with session scope, not program scope, so program variables are out of scope in a prepared statement.
The fix requires you to use a user-defined variable, which has session scope, and then copy it into the program variable, as #nbk illustrated in their answer.
SET #sql_final = concat(#sql_text2, '#rowcount');
PREPARE stmt1 FROM #sql_text1;
EXECUTE stmt1;
SET rowcount = #rowcount;
Note that program variables like rowcount and user-defined variables like #rowcount are from completely different namespaces, so there's no need for the names to be the same and no need for the names to be different.
Error #3 is not strictly an error, in the sense that it isn't stopping your code from working, but here's a note about a potentially dangerous practice.
You are accepting a table name as input, which opens up a security vulnerability called SQL Injection, where malicious input can cause unexpected/unauthorized results. Even if the argument can be made that this input comes from a trusted source, that argument is disregarded as a matter of best practice, because future changes could invalidate that assumption. It is worth your time to learn to do this, and do it consistently so that it becomes second-nature to you.
You can safely escape a table name, column name, or other object identifier in MySQL by replacing any embedded backtick with a double backtick, then prepending and appending a single backtick on each end.
You can do this at the top of the procedure...
SET tblname = CONCAT('`',REPLACE(tblname,'`','``'),'`');
...or inline...
SET #sql_text1 = concat('SELECT COUNT(*) FROM ',CONCAT('`',REPLACE(tblname,'`','``'),'`'));
...but of course not both. In the second example, the nested CONCAT() isn't strictly necessary, so this would also work, but the intention is less obvious:
SET #sql_text1 = concat('SELECT COUNT(*) FROM ','`',REPLACE(tblname,'`','``'),'`');
Use this stored procdure:
DELIMITER //
DROP PROCEDURE IF EXISTS GetCount //
CREATE DEFINER=`root`#`localhost` PROCEDURE `GetCount`(IN tblname varchar(255), OUT rowcount int)
BEGIN
SET #sql_text1 = concat('SELECT COUNT(*) FROM ',tblname);
SET #sql_text1 = concat(#sql_text1,' INTO ');
SET #sql_text1 = concat(#sql_text1, ' #rowcount;' );
PREPARE stmt1 FROM #sql_text1;
EXECUTE stmt1;
Set rowcount = #rowcount;
DEALLOCATE PREPARE stmt1;
END
//
DELIMITER ;
The idea is that mysql stores the count into the sessionvariable #rowcount which will be created automatically. The rest is simple getting the result to the proper variable.

MySQL prepared statements with parameters not returning data

I have been trying to make parameterized prepared statements work. The first function only presents the header row with the column names and does not show any daya. The second
function provides all the data requested. The difference is that the first one uses parameters and the second only uses concatenated strings. The select statements in the first
function are used for debugging purposes, there are no NULL strings in any of the variables.
I would like to get the parameterized version working properly, I would appreciate any help.
I have already checked out these stackoverflow answers, Multiple Parameters, mysql Prepare Statement, Internals of prepared statement, unable to create prepared statements (might be the answer to my problem).
-- -----------------------------------------------------
-- procedure getAllBookDataWhere2
-- -----------------------------------------------------
USE `booklibinventory`;
DROP procedure IF EXISTS `booklibinventory`.`getAllBookDataWhere2`;
DELIMITER $$
USE `booklibinventory`$$
CREATE PROCEDURE `getAllBookDataWhere2`
(
IN whereStr VARCHAR(256)
)
BEGIN
DECLARE finalQuery VARCHAR(4096);
DECLARE selectedFields, leftJoinTables, joinOnFields VARCHAR(1024);
DECLARE whereClause, orderByClause VARCHAR(256);
SET #selectedFields = allBooksSelectFields();
SET #jointTables4Query = allBooksDataTables();
-- orderBy may become a parameter in the future.
SET #orderByClause = ' a.LastName, a.FirstName, s.SeriesName, v.VolumeNumber, t.TitleStr';
SET #whereclause = whereStr;
-- #selectedFields and #jointTables4Query are concatenated because they don't change,
-- #whereClause and #orderByClause can change and therefore are parameters.
SELECT #orderByClause;
SELECT #whereClause;
SET #finalQuery = CONCAT('SELECT ', #selectedFields);
SET #finalQuery = CONCAT(#finalQuery, ' FROM bookinfo AS BKI ');
SET #finalQuery = CONCAT(#finalQuery, #jointTables4Query);
SET #finalQuery = CONCAT(#finalQuery, ' WHERE ? ORDER BY ? ;');
SELECT #finalQuery;
PREPARE stmt FROM #finalQuery;
EXECUTE stmt USING #whereClause, #orderByClause;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
-- -----------------------------------------------------
-- procedure getAllBookDataWhere
-- -----------------------------------------------------
USE `booklibinventory`;
DROP procedure IF EXISTS `booklibinventory`.`getAllBookDataWhere`;
DELIMITER $$
USE `booklibinventory`$$
CREATE PROCEDURE `getAllBookDataWhere`
(
IN whereStr VARCHAR(256)
)
BEGIN
DECLARE finalQuery VARCHAR(4096);
DECLARE selectedFields, leftJoinTables, joinOnFields VARCHAR(1024);
DECLARE whereClause, orderByClause VARCHAR(256);
SET #selectedFields = allBooksSelectFields();
SET #jointTables4Query = allBooksDataTables();
-- orderBy may become a parameter in the future.
SET #orderByClause = ' ORDER BY a.LastName, a.FirstName, s.SeriesName, v.VolumeNumber, t.TitleStr;';
SET #whereclause = CONCAT(' WHERE ', whereStr);
-- #selectedFields and #jointTables4Query are concatenated because they don't change,
-- #whereClause and #orderByClause can change and therefore are parameters.
SET #finalQuery = CONCAT('SELECT ', #selectedFields);
SET #finalQuery = CONCAT(#finalQuery, ' FROM bookinfo AS BKI ');
SET #finalQuery = CONCAT(#finalQuery, #jointTables4Query);
SET #finalQuery = CONCAT(#finalQuery, #whereClause);
SET #finalQuery = CONCAT(#finalQuery, #orderByClause);
PREPARE stmt FROM #finalQuery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
Parameters are for values, not whole clauses. Your WHERE and ORDER BY clauses are effectively WHERE 'somestring' and ORDER BY 'somestring' (note the quotes).
Also, if I remember correctly, EXECUTEd statements do not add resultsets to a procedure's returned results; typically, the execute ends up needing to be an insert select into a temporary table that the procedure can select from directly before exiting.
Edit: Since you're already concat-ing the query together, you could just replace your ?-placeholders, ... WHERE ', #whereClause, ' ORDER BY ', #orderByClause, ';');
Parameters can't protect you from letting raw sql be "user" supplied.

mysql dynamic query in stored procedure

i am creating a dynamic query in stored procedure. my stored procedure is as follows:
CREATE PROCEDURE `test1`(IN tab_name VARCHAR(40),IN w_team VARCHAR(40))
BEGIN
SET #t1 =CONCAT("SELECT * FROM ",tab_name," where team=",w_team);
PREPARE stmt3 FROM #t1;
EXECUTE stmt3;
DEALLOCATE PREPARE stmt3;
END
when i try to run it with the following call:
call test1 ('Test','SPA');
i get the following error message:
Error Code: 1054. Unknown column 'SPA' in 'where clause'
i tested without where condition and it works fine, but with the where condition its not working, i tried using # with the variable name but it still does not work.
Thanks for your help.
Error Code: 1054. Unknown column 'SPA' in 'where clause'
This happens when you do not enclose input string within quotes, and SQL engine tries to identify it as a column in the table being queried. But it fails as it can't find it.
But what happens when it finds such column?
It fetches results when it finds some matches on the column values.
Obviously this is not what one was expecting.
How to overcome this? Use Prepared Statements with dynamic input values.
You can use placeholders like ? in stored procedures too on dynamic input values to use with Prepared Statements. The engine will handle escape characters and other string values when assigned to or compared within SQL expressions.
You just need to re-assign procedure inputs to one or more session variables, as required.
Example on your procedure:
CREATE PROCEDURE `test1`( IN tab_name VARCHAR(40), IN w_team VARCHAR(40) )
BEGIN
SET #t1 = CONCAT( 'SELECT * FROM ', tab_name, ' where team = ?' ); -- <-- placeholder
SET #w_team := w_team;
PREPARE stmt3 FROM #t1;
EXECUTE stmt3 USING #w_team; -- <-- input for placeholder
DEALLOCATE PREPARE stmt3;
END;
You missed to enclose the parameter w_team in WHERE clause.
Try like this:
SET #t1 =CONCAT("SELECT * FROM ",tab_name," where team='",w_team,"'");
Explanation:
Query from your code would be like:
SELECT * FROM Test where team=SPA
It will try find a column SPA which is not available, hence the error.
And we changed it to:
SELECT * FROM Test where team='SPA'
Try this..
CREATE PROCEDURE `test1`(IN tab_name VARCHAR(40),IN w_team VARCHAR(40))
BEGIN
SET #t1 =CONCAT("SELECT * FROM ",tab_name," where team='",w_team,"'");
PREPARE stmt3 FROM #t1;
EXECUTE stmt3;
DEALLOCATE PREPARE stmt3;
END
You are missing quotes around w_team variable..
you should print the statement that dynamically build so you can just copy printed statement and try so you can easily find this kind of problem.
select #t1 will print the statment that build dynamically..
you can add dynamic fields and condition by using CONCAT() MySQL function. I checked this is working fine.
DELIMITER $$
/*define procedure name*/
CREATE PROCEDURE getSearchData()
BEGIN
DECLARE conditions varchar(1000);
DECLARE selectField varchar(1000);
DECLARE SQL_QUERY varchar(1000);
/*define default select and condition*/
SET #selectField = 'status,id';
set #conditions = ' where return_flight=0';
SET #SQL_QUERY = CONCAT('SELECT ',#selectField, ' FROM flights ',#conditions);
/* you can add more select fields and conditions according to your requirement */
PREPARE stmt1 FROM #SQL_QUERY ;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END$$
DELIMITER ;

Changing Multiple DB Fields to Lower Case

With phpMyAdmin, I can use the following SQL to change all values in the table.field mytable.Site to lower case...
UPDATE my_table SET Site=LOWER(Site)
I have a zillion tables that have this same field, and I'd like to change all of them to lower case. Is there a SQL command that will do that - change EVERY field named Site in every table to lower case (preferably without having to list every table that has that field)?
Not EXACTLY what you want,but pretty close.Tested on my machine.
First create a procedure
delimiter //
CREATE PROCEDURE test(IN tbl CHAR(64))
BEGIN
SET #s = CONCAT('UPDATE ',tbl,' SET Site=LOWER(Site)' );
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
//
delimiter ;
And for finding tables with a certain column name:
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME IN ('Site')
AND TABLE_SCHEMA='YourDB';
For calling the procedure
CALL test('tableName')