MySQL select query with where condition using string variables - mysql

Myself trying to pass string variable to where condition in MySQL query as given in this stack overflow answer as given below.
select #start := ' and Id=21';
select * from myTable where 1=1 #start;
So how can I use string variable with where condition in MySQL queries. The variables are set dynamically and the query runs within procedure.
EDIT: I also tried
SET #start = ' Id=21 ';
select * from myTable where (select #start);
But no use.

No you cannot do that. The columns and the condition in the select clause needs to be fixed when you are preparing the select statement.
So you cannot make a dynamic where clause statement like the one you posted. In that example, the values in the column are dynamic not the column names.
The manual says:
A conditional object consists of one or more conditional fragments
that will all be joined by a specified conjunction. By default, that
conjunction is AND.

I believe what you are attempting is to create a Dynamic Query using EXEC command.
You can create a varchar variable with the SQL statement and then execute it with EXEC, here an example taken from
https://www.mssqltips.com/sqlservertip/1160/execute-dynamic-sql-commands-in-sql-server/
If you want to do something like
DECLARE #city varchar(75)
SET #city = 'London'
SELECT * FROM customers WHERE City = #city
This is the Dynamic Query creation.
DECLARE #sqlCommand varchar(1000)
DECLARE #columnList varchar(75)
DECLARE #city varchar(75)
SET #columnList = 'CustomerID, ContactName, City'
SET #city = '''London'''
SET #sqlCommand = 'SELECT ' + #columnList + ' FROM customers WHERE City = ' + #city
EXEC (#sqlCommand) --This does the magic
/*
just a heads up, the user impersonating the execution needs credentials for EXEC command.
*/

Store part of your query
SET #start = ' and Id=21';
Store your query concatenating its parts
SET #s = CONCAT('select * from myTable where 1=1 ', #start);
Prepare a statement for execution
PREPARE stmt FROM #s;
EXECUTE executes a prepared statement
EXECUTE stmt;
Release the prepared statement
DEALLOCATE PREPARE stmt;
All together:
SET #start = ' and Id=21';
SET #s = CONCAT('select * from myTable where 1=1 ', #start);
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
More Details on the MySQL manual: https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html

Related

How do I pass a user variable (#variable) as a column name in a select statement? [duplicate]

is it possible to select field which name is string?
SELECT 'fieldname' FROM table
i need this for trigger to have dynamic field names
something like
SET fieldname = NEW.`name`;
UPDATE table SET fieldname = 1 ;
If the string is in your external application (like PHP), sure, just construct the MySQL statement.
If the string is inside a MySQL table, you can't. MySQL has no eval() or such function. The following is impossible:
Suppose you have a table queries with a field columnname that refers to one of the column names in the table mytable. There might be additional columns in queries that allow you to select the columnname you want.
INSERT INTO queries (columname) VALUES ("name")
SELECT (select columnname from queries) from mytable
You can however work with PREPARED STATEMENTS. Be aware this is very hacky.
SELECT columnname from queries into #colname;
SET #table = 'mytable';
SET #s = CONCAT('SELECT ',#colname,' FROM ', #table);
PREPARE stmt FROM #s;
EXECUTE stmt;
Just as a heads up to these correct answers you can also do it inside a stored procedure this worked perfectly for me in MySQL 8x Community:
CREATE DEFINER=`root`#`127.0.0.1` PROCEDURE `SP_LIST_COLLECTORS`(
IN P_email VARCHAR(60),#Admin email
IN P_password_hash VARCHAR(255),#Admin hash
IN P_filter_field VARCHAR(80),
IN P_filter_value VARCHAR(255)
)
BEGIN
DECLARE V_filter_field VARCHAR(80);
SET V_filter_field = P_filter_field;
BEGIN
GET DIAGNOSTICS CONDITION 1 #ERRNO = MYSQL_ERRNO, #MESSAGE_TEXT = MESSAGE_TEXT;
SELECT 'ERROR' AS STATUS, CONCAT('MySQL ERROR: ', #ERRNO, ': ', #MESSAGE_TEXT) AS MESSAGE;
END;
SET #statement = CONCAT('SELECT collector_id, email, address, post_code, phone, alt_phone, contact_name
FROM collectors_table
WHERE ',P_filter_field, '=\'', P_filter_value, '\';');
#SELECT collector_id, email, address, post_code, phone, alt_phone, contact_name FROM collectors_table WHERE (V_filter_field) = P_filter_value;
PREPARE stmnt FROM #statement;
EXECUTE stmnt;
END
If you want to select more than one column:
SELECT GROUP_CONCAT(COLUMN_NAME) FROM information_schema.`COLUMNS` C
WHERE table_name = 'MyTb' AND COLUMN_NAME LIKE '%whatever%' INTO #COLUMNS;
SET #table = 'MyTb';
SET #s = CONCAT('SELECT ',#columns,' FROM ', #table);
PREPARE stmt FROM #s;
EXECUTE stmt;

Dynamic table name variable in stored procedure

I want to create a function that will create a unique random id. The parameters will simply be min (the minimum number), max (the maximum number), and tablename (the name of the table to check to see if the id produced by the rand() function already exists).
I have discovered through other posts that you can't pass table names into functions, because functions can't execute dynamic SQL, but you can pass them into stored procedures. I have found numerous examples on StackOverflow of how to pass table names into stored procedures, and they all boil down to using prepared statements.
I have created a stored procedure as shown below:
DELIMITER $$
CREATE DEFINER=`user`#`localhost` PROCEDURE `rand_id`(IN `min` INT, IN `max` INT, IN `tablename` VARCHAR(20) CHARSET utf8, OUT `uid` INT)
BEGIN
DECLARE count_id int;
SET count_id = 1;
SET #s = CONCAT('COUNT(`id`) INTO count_id FROM `', tablename, '` WHERE `id` = ', uid);
WHILE count_id > 0 DO
SET uid = FLOOR(rand() * max + min);
PREPARE stmt from #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END WHILE;
END$$
DELIMITER ;
Whenever I run the following code:
CALL rand_id(1000000000, 9999999999, 'test', #id);
SELECT #id;
I get this error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NULL' at line 1
I'm at a loss for what's wrong. I saw somewhere that you can't use user variables inside a stored procedure, but that seems to be incorrect because there are a lot of examples on StackOverflow where the correct solutions do just that.
Sorry for my low level of MySQL understanding. I'm sure my code is fraught with syntax errors and poor design. I appreciate any help I can get. I researched this for quite a while and tried many things but to no avail. The above portion of code is the closest I've been able to get, and yields the least errors, but it's still not working.
Thank you.
EDIT: As per the second example in #Barmar's answer, I changed my code to look like this:
BEGIN
DECLARE count_id int;
SET count_id = 1;
SET #s = CONCAT('SELECT COUNT(`id`) INTO count_id FROM `', tablename, '` WHERE `id` = ?');
PREPARE stmt from #s;
WHILE count_id > 0 DO
SET #uid = FLOOR(rand() * max + min);
EXECUTE stmt USING #uid;
END WHILE;
DEALLOCATE PREPARE stmt;
SET uid = #uid;
END
It seems to have fixed my initial problem but now I get this error:
#1327 - Undeclared variable: count_id
EDIT: Here is my code changed to fit #slaakso's answer, and add in what #Barmar said about using #count_id:
DELIMITER $$
CREATE DEFINER=`mjrinker`#`localhost` PROCEDURE `rand_id`(IN `min` BIGINT, IN `max` BIGINT, IN `tablename` VARCHAR(128) CHARSET utf8, OUT `uid` BIGINT)
BEGIN
SET #count_id = 1;
SET uid = 0;
SET #s = CONCAT('SELECT COUNT(`id`) INTO #count_id FROM `', tablename, '` WHERE `id` = ?');
PREPARE stmt from #s;
WHILE #count_id > 0 DO
SET #uid = FLOOR(rand() * max + min);
EXECUTE stmt USING #uid;
END WHILE;
DEALLOCATE PREPARE stmt;
SET uid = #uid;
END$$
DELIMITER ;
You need to assign #s after you assign the uid variable.
You're also missing the SELECT keyword in your query.
SET #count_id = 1
WHILE #count_id > 0 DO
SET uid = FLOOR(rand() * max + min);
SET #s = CONCAT('SELECT COUNT(`id`) INTO #count_id FROM `', tablename, '` WHERE `id` = ', uid);
PREPARE stmt from #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END WHILE;
But you should actually just prepare the statement once, using a placeholder, which you fill in when using EXECUTE.
SET #count_id = 1
SET #s = CONCAT('SELECT COUNT(`id`) INTO #count_id FROM `', tablename, '` WHERE `id` = ?');
PREPARE stmt from #s;
WHILE #count_id > 0 DO
SET #uid = FLOOR(rand() * max + min);
EXECUTE stmt USING #uid;
END WHILE;
DEALLOCATE PREPARE stmt;
SET uid = #uid;
Note that the parameters to EXECUTE have to be user variables, that's why I changed uid to #uid there. Then we set the output parameter at the end of the loop.
You also need to use a user variable for INTO #count_id.
First of all it is highly unusual to use random numbers as ID's for tables. Mabye you should consider using AUTO_INCREMENT columns.
If you really want to use random numbers, couple of fixes for the code:
You should use value for uid for the first time you run the query
(without it it will be NULL, therefore the error).
You are missing SELECT in your dynamic query
The "INTO count_id" syntax will not work as count_id is not visible inside the dynamic SQL (use #var variable instead)
Your min and max values are declared as INT's, but your passed parameters exceed the INT range (-2147483648 - 2147483647)

table column as variable

I'm getting a syntax error in my stored procedure when trying to use a variable as a reference to a tables column.
BEGIN
SET #mycolumn = (SOME SELECT STATEMENT RETURNING MY COLUMN);
SELECT a.#mycolumn FROM mytable as a;
END
Question: What is wrong with my syntax?
It looks like you're trying to do dynamic SQL. Here is one way to do it:
BEGIN
SET #mycolumn = (SOME SELECT STATEMENT RETURNING MY COLUMN);
DECLARE #sql varchar(20000);
SET #sql = CONCAT('SELECT a.', #mycolumn, ' FROM mytable as a';
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
Jordan,
You might need to use table variable or Dynamic SQL for this.
Here is how I do this with table variable.
Declare #table as table (columnName dataType.
Insert into #table
SOME SELECT STATEMENT RETURNING YOUR COLUMN
Results by running the below query:-
SELECT * FROM #table

mysql field name from variable

is it possible to select field which name is string?
SELECT 'fieldname' FROM table
i need this for trigger to have dynamic field names
something like
SET fieldname = NEW.`name`;
UPDATE table SET fieldname = 1 ;
If the string is in your external application (like PHP), sure, just construct the MySQL statement.
If the string is inside a MySQL table, you can't. MySQL has no eval() or such function. The following is impossible:
Suppose you have a table queries with a field columnname that refers to one of the column names in the table mytable. There might be additional columns in queries that allow you to select the columnname you want.
INSERT INTO queries (columname) VALUES ("name")
SELECT (select columnname from queries) from mytable
You can however work with PREPARED STATEMENTS. Be aware this is very hacky.
SELECT columnname from queries into #colname;
SET #table = 'mytable';
SET #s = CONCAT('SELECT ',#colname,' FROM ', #table);
PREPARE stmt FROM #s;
EXECUTE stmt;
Just as a heads up to these correct answers you can also do it inside a stored procedure this worked perfectly for me in MySQL 8x Community:
CREATE DEFINER=`root`#`127.0.0.1` PROCEDURE `SP_LIST_COLLECTORS`(
IN P_email VARCHAR(60),#Admin email
IN P_password_hash VARCHAR(255),#Admin hash
IN P_filter_field VARCHAR(80),
IN P_filter_value VARCHAR(255)
)
BEGIN
DECLARE V_filter_field VARCHAR(80);
SET V_filter_field = P_filter_field;
BEGIN
GET DIAGNOSTICS CONDITION 1 #ERRNO = MYSQL_ERRNO, #MESSAGE_TEXT = MESSAGE_TEXT;
SELECT 'ERROR' AS STATUS, CONCAT('MySQL ERROR: ', #ERRNO, ': ', #MESSAGE_TEXT) AS MESSAGE;
END;
SET #statement = CONCAT('SELECT collector_id, email, address, post_code, phone, alt_phone, contact_name
FROM collectors_table
WHERE ',P_filter_field, '=\'', P_filter_value, '\';');
#SELECT collector_id, email, address, post_code, phone, alt_phone, contact_name FROM collectors_table WHERE (V_filter_field) = P_filter_value;
PREPARE stmnt FROM #statement;
EXECUTE stmnt;
END
If you want to select more than one column:
SELECT GROUP_CONCAT(COLUMN_NAME) FROM information_schema.`COLUMNS` C
WHERE table_name = 'MyTb' AND COLUMN_NAME LIKE '%whatever%' INTO #COLUMNS;
SET #table = 'MyTb';
SET #s = CONCAT('SELECT ',#columns,' FROM ', #table);
PREPARE stmt FROM #s;
EXECUTE stmt;

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.