Assigning a SQL result to variable from prepared statement in MySQL - mysql

I am creating a stored procedure in MySQL and need to assign the results of a SQL query to a variable. The problem is that in order to create the SELECT statement, I have to use the CONCAT() function because I am passing in parameters.
Well it appears you can't use variables within the CONCAT function. Any ideas on how I can achieve this? The procedure I am trying to write is below:
DELIMITER //
CREATE PROCEDURE `my_proc` (IN tbl VARCHAR(20), IN col VARCHAR(20), IN id INT)
BEGIN
DECLARE #myval VARCHAR(100);
SET #t1 =CONCAT('SELECT ',col,' FROM ',tbl,' INTO #myval WHERE id = ',id );
PREPARE stmt1 FROM #t1;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
END //
Executing this gives me a SQL syntax error.

The problem is the following line:
...
-- SET #t1 = CONCAT('SELECT ',col,' FROM ',tbl,' INTO #myval WHERE id = ',id );
SET #t1 = CONCAT('SELECT ', col, ' INTO #myval FROM ', tbl, ' WHERE id = ', id);
...
Check the documentation: 13.2.9.1. SELECT ... INTO Syntax.
Here is a SQL Fiddle with an example.
It is important to indicate the difference between 9.4. User-Defined Variables (#t1 and #myval) and 13.6.4.1. Local Variable Syntax DECLARE (as could be: myval and t1), are different variables, therefore, it is not necessary to declare:
-- DECLARE #myval VARCHAR (100);

Related

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 stored procedure using cursor fetched variable in sql query

I have defined a following procedure.
create procedure deleteData()
begin
DECLARE no_tbls INT;
DECLARE tbl VARCHAR(64);
DECLARE tbls_cr CURSOR for SELECT DISTINCT table_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema='db';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_tbls=1;
OPEN tbls_cr;
SET no_tbls=0;
while no_tbls=0 do
fetch tbls_cr into tbl;
select tbl.updated_at from tbl limit 1;
end while;
close tbls_cr;
end
After running this procedure i am getting an error db.tbl doesn't exist.
So i was searching if there is a way to use a cursor fetched object in another query. The problem i am doing all this tedious stuff is that i would like to delete data from all tables of a db with a particular where clause.
Note: All tables has a column updated_at with date format.
(I am a newbie to MySQL stored procs).
You cannot have a variable in place of a table name in the from clause in a plain select statement, mysql will look for a table named tbl in the database.
You need to use string concatenation and prepared statements to dynamically create and execute sql statements:
mysql> USE test;
mysql> CREATE TABLE t1 (a INT NOT NULL);
mysql> INSERT INTO t1 VALUES (4), (8), (11), (32), (80);
mysql> SET #table = 't1';
mysql> SET #s = CONCAT('SELECT * FROM ',#table);
mysql> PREPARE stmt3 FROM #s;
mysql> EXECUTE stmt3;
mysql> DEALLOCATE PREPARE stmt3;
Prepared statements also work in stored procedures, the above example demonstrates how to create the sql statement by concatenating string literals with variables, prepare the statement, execute it, and then release the prepared statement from memory.
Using Prepare Statement you can fetch data dynamically
create procedure deleteData()
begin
DECLARE no_tbls INT;
DECLARE tbl VARCHAR(64);
DECLARE tbls_cr CURSOR for
SELECT DISTINCT table_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema='db';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_tbls=1;
OPEN tbls_cr;
start_loop : LOOP
fetch tbls_cr into tbl;
set #b = concat('select ', tbl, '.updated_at from ' , tbl, ' limit 1');
prepare stmt3 from #b;
execute stmt3;
DEALLOCATE PREPARE stmt3;
END LOOP start_loop;
close tbls_cr;
end

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 ;

how to set value to output parameter in dynamic sql procedure?

I have tried with following code. But it shows error msg like this undeclared variable :nt.
CREATE DEFINER=`root`#`localhost` PROCEDURE `get_ntime`(in tb varchar(50),in d int, out nt varchar(50))
BEGIN
SET #statment = concat('Select ntime into nt from ',tb);
SET #date = CONCAT(' WHERE date = "', d, '"');
SET #statmen = CONCAT(#statment, #date);
PREPARE stmt FROM #statment;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
When used within single quotes nt will not be treated as variable but a literal.
Use local variable to set value into and assign the same to out param after execution.
Example:
DELIMITER //
CREATE DEFINER=`root`#`localhost`
PROCEDURE `get_ntime`( in tb varchar(50), in d int, out nt varchar(50) )
BEGIN
SET #staetment = concat( 'Select ntime into #nTime from ', tb );
-- SET #date = CONCAT( ' WHERE date = "', d, '"' );
SET #date = CONCAT( ' WHERE date = ?' );
SET #statement = CONCAT( #statement, #date );
SET #dt := d;
PREPARE stmt FROM #statement;
-- EXECUTE stmt;
EXECUTE stmt using #dt;
DEALLOCATE PREPARE stmt;
SET nt := #nTime;
END;
//
DELIMITER ;
Prepared statements have session scope, mysql doesn't know you want to use your prepared statement inside stored procedure only. You deallocate the statement immediately, but it doesn't have to be always like that.
Thats why mysql simply disallows using anything that has less scope inside your prepared statement - as is the case with in and out parameters, which have a scope of stored procedure.
As a workaround mentioned in mysql prepare statement manual you can use user variable inside your prepared statement and then SET your out paremeter to that user variable.

Creating Dynamic Query in Stored Procedure MySQL

I have a Table and Rows in table like below
CREATE TABLE Areas(AreaName VARCHAR(255),
PinCode VARCHAR(255))
INSERT INTO Areas(AreaName, PinCode)
VALUES('Teynampet', '6000018'),
('Ramapuram', '6000089'),
('TNagar', '6000017'),
('Mylapore', '6000014'),
('Gopalapuram', '6000087')
I Wrote a SQL Procedure as Below
DROP PROCEDURE IF EXISTS mp_test;
CREATE PROCEDURE mp_test(IN pArea VARCHAR(255))
BEGIN
SET #Query = 'SELECT PinCode FROM Areas';
IF pArea != ''
THEN
SET #City = CONCAT(' WHERE AreaName = ', pArea);
END IF;
SET #Query = CONCAT(#Query, #City);
PREPARE stmt FROM #Query;
EXECUTE stmt;
END
When I Call the Procedure
CALL mp_test('Teynampet');
When i Execute i am Not getting the Desired result i.e is 600018
How can i build query dynamically in SP
Thanks for the Help
You're concatenating the pArea parameter into the SQL unquoted. That is, the content of #Query that you prepare for execution is:
SELECT PinCode FROM Areas WHERE AreaName = Teynampet
Since Teynampet is unquoted, it is parsed as a(n unknown) SQL identifier rather than a string. You should either:
quote it in your SQL:
SET #City = CONCAT(' WHERE AreaName = ', QUOTE(pArea));
pass it to the prepared statement as a parameter:
SET #City = CONCAT(' WHERE AreaName = ?');
SET #param = pArea;
and then:
EXECUTE stmt USING #param;
However, why use prepared statements here? Your procedure can be rewritten as a simple SELECT (which raises the question of whether you need to use a stored procedure at all):
CREATE PROCEDURE mp_test(IN pArea VARCHAR(255))
SELECT PinCode FROM Areas WHERE pArea IN (AreaName, '');
(Note that I'd recommend you use NULL instead of the empty string '', in which case the above test would be pArea IS NULL OR pArea = AreaName).