Conditionally showing part of a query in MySQL stored procedure - mysql

I have a MySQL query that I build in php but want to move into a stored procedure. The entire query is good to move into the sproc, except for one complication: In php, depending on a condition, I add a "HAVING ......" as the last line. Is it possible to do a condition, case, etc. to build a query in a similar way?
For example:
PROCEDURE `GeyMyItems`(
IN query VARCHAR(100)
)
BEGIN
SELECT * FROM my_table a
JOIN another_table b ON a.id = b.id
WHERE my_column = 'this_value'
IF (query = 'abc')
HAVING a.my_value = '123';
END$$
DELIMITER ;
I know the syntax of the IF is probably wrong, but I'm just showing what I'm trying to do. Is there a better way to do this?
The only fall back I can think of is to maybe take care of the HAVING portion in php. Just don't include it at all in the SQL, and when I am building my object/array, I can just filter there in my while loop. But I'd like to see how I can utilize a stored procedure for something like this, if at all?

If you're using MySQL 5.0 and later, it's pretty easy.
DELIMITER $$
CREATE PROCEDURE `GetMyItems`(
IN query VARCHAR(100)
)
BEGIN
-- Here you construct your SQL
SET #s = 'SELECT 1';
IF query = 'abc' THEN
SET #s = CONCAT( #s, ',2');
END IF;
-- Here you execute it.
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
This was a similar question here.

to your where clause add this:
and
(
(query = 'abc' and a.my_value ='123')
or query <> 'abc'
)

Related

mysql - create dynamic union

Database is mysql/mariaDB.
I have database designed to store monthly reports about something. and their names are (example): table1, table2, table3...
I want to create function/procedure that will create/recreate view that contains all tables union (union ALL).
Something like:
1. first select all table names from information schema.
SELECT TABLE_NAME from information_schema.`TABLES` where TABLE_NAME like 'table%'
then i would try to set it in some loop to use result set from first query.
But i have problem with first step where i try to merge only one fixed table + one from first query and it returns error to me.
i try:
select * from `table4`
UNION
SELECT * from (SELECT TABLE_NAME from information_schema.`TABLES`
where TABLE_NAME like 'table%' limit 1) as dd
it returns me error: The used SELECT statements have a different number of columns ,
but when i execute sub query i get 1 result with correct name of table, and when i set that name in from clause without sub query, it works.
Any idea why it is happening, and maybe some advice how to accomplish that dynamic union.
I think a little push will help you to the correct way of handling this problem.
First, as Tim Biegeleisen suggests, the way to proceed is to use dynamic SQL, this is your only avenue if the table names cannot be absolutely determined before you try to run the query.
Second, you are correct to think that you need to start by querying the information_schema.TABLE, which you should do using a CURSOR. The results from that query should then be used to build up a query string which you then PREPARE and EXECUTE.
Third, I take it that the error message you included in your post refers specifically to the running of that query and doesn't indicate that the monthly tables differ in any way. You can't do a UNION unless the results from each part return the same number of columns.
Fourth, because we are going to build the query dynamically, this has to done within a stored procedure, it's not possible to do it in a stored function.
There are good tutorials in the mysql docs for using CURSOR and PREPARE/EXECUTE, which you should read. The version I give below will be based on those examples. I'm assuming the only input parameter will be the schema name (in case you happen to have some similarly named tables in another database on the server).
DELIMITER //
DROP PROCEDURE IF EXISTS dyn_union //
CREATE PROCEDURE dyn_union(IN v_sname VARCHAR(64))
READS SQL DATA
BEGIN
-- NB the order of declaration for variables cursor
-- and handler must be strictly observed
DECLARE sname VARCHAR(64); -- variable the schema names
DECLARE tname VARCHAR(64); -- variable the table names
DECLARE done INT DEFAULT FALSE; -- cursor control variable
DECLARE cur1 CURSOR FOR
SELECT table_schema, table_name
FROM information_schema.TABLES
WHERE table_schema = v_sname
AND table_name LIKE 'table%';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
SET #sql = ''; -- build the query string in this var
OPEN cur1;
read_loop: LOOP -- loop over the rows returned by cursor
FETCH cur1 INTO sname, tname; -- fetching the schema and table names
IF done THEN
LEAVE read_loop;
END IF;
IF #sql = '' THEN -- build the select statement
SET #sql := CONCAT('SELECT * FROM `', sname, '`.`', tname, '`');
ELSE
SET #sql := CONCAT(#sql, ' UNION ALL SELECT * FROM `', sname, '`.`', tname, '`');
END IF;
END LOOP;
CLOSE cur1;
select #sql;
PREPARE stmt FROM #sql; -- prepare and execute the dynamically
EXECUTE stmt; -- created query.
DEALLOCATE PREPARE stmt;
END //
DELIMITER ;
-- call the procedure
CALL dyn_union('your_db_name');

How to write a mysql function with dynamic table name?

I'm trying to write a my sql function doing the following things:
1- get the table name used in join as a parameter.
but I get mysql syntax 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 'table DETERMINISTIC
BEGIN select `r`.`id` AS `id`, (case ' at line 2
This is my query
DELIMITER $$
CREATE FUNCTION getTranslation (tablename varchar(50),entity varchar(20),itemid int,lang char(3))
RETURNS table
DETERMINISTIC
BEGIN
select
`r`.`id` AS `id`,
(case
when isnull(`t`.`descr`) then `r`.`descr_ml`
else `t`.`descr`
end) AS `descr`
from
(tablename `r`
left join `g001_translation` `t` ON ((`t`.`item_id` = `r`.`id`)))
END$$
DELIMITER ;
I the select part works fine with static table name by the way.
First up as mentioned by #eggyal this isn't the best way to go about things. But it can be done by using prepared statements. I.e.
DROP PROCEDURE IF EXISTS `exampleOfPrepareStatement`;
CREATE DEFINER = `user`#`%` PROCEDURE `exampleOfPrepareStatement`(inTableName VARCHAR(100))
MODIFIES SQL DATA
SQL SECURITY INVOKER
BEGIN
SET #hr1 = CONCAT('
INSERT INTO `',inTableName,'` (
-- fields (can use parameters same as table name if needed)
)
-- either VALUES () or SELECT here
');
-- Prepare, execute, deallocate
PREPARE hrStmt1 FROM #hr1;
EXECUTE hrStmt1;
DEALLOCATE PREPARE hrStmt1;
END;
You can of course add in field names etc. as needed, or use a SELECT or UPDATE etc. This is not ideal, but will do what you are looking for.
I have had to use this in some places before where the same maintenance is being performed on multiple tables which have different field names ( / table names ) and so instead of writing the same function 20 times, instead I use this type of stored procedure which can then be called to do the indexing etc.
As also mentioned by #eggyal , while this may do as you ask, it might not do as you need. If you can provide more information then you may get a better solution.
Give this a try
SET #ex = CONCAT('select `r`.`id` AS `id`,(case when isnull(`t`.`descr`) then `r`.`descr_ml` else `t`.`descr` end) AS `descr` from (',tablename,' `r` left join `g001_translation` `t` ON ((`t`.`item_id` = `r`.`id`)));');
PREPARE stmt FROM #ex;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
You will notice that ',tablename,' will use the parameter passed.

what is wrong with MySQL select Store Procedure with dynamic query

i have tried to create MySQL store procedure in that i want to make dynamic query.
code on which i working..
DELIMITER $$
DROP PROCEDURE IF EXISTS `test`.`selectp` $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `selectp`(in a_str_Condition varchar(500))
BEGIN
SET #Query = 'SELECT * from test123';
IF a_str_Condition != ''
THEN
SET #strCondition = CONCAT(' WHERE ? ');
SET #param = a_str_Condition;
ELSE
SET #strCondition = ' Order by aaa desc';
END IF
SET #Query = CONCAT(#Query, #strCondition );
PREPARE stmt FROM #Query;
EXECUTE stmt USING #param;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;
here i want pass parameter as aaa = 3 and concat it with mysql query, but it show me error as below.
please let me clear on this store procedure. Any help will be Appreciate.
Trying to answer to your ...what is wrong with MySQL select Store Procedure..?
As others mentioned you have ; missing. That only solves a syntax error.
But you have bigger problems with the way you try to construct your query and EXECUTE it:
You're incorrectly trying to treat all where conditions as one parameter with WHERE ?, instead of parameterizing values like WHERE id = ?
In case you don't pass a condition you can't use USING in EXECUTE. It'll fail.
Now since you don't execute your query multiple times, you pass conditions as a string anyway, and it seems that you're more after flexibility than security, IMHO there is no much sense in using parameters here.
That being said a more succinct version of your SP might look like this
DELIMITER $$
CREATE PROCEDURE selectp(IN _conditions VARCHAR(500), IN _orderby VARCHAR(500))
BEGIN
SET #sql = CONCAT(
'SELECT * FROM test123 WHERE 1 = 1',
COALESCE(CONCAT(' AND ', NULLIF(_conditions, '')), ''),
' ORDER BY ',
COALESCE(NULLIF(_orderby, ''), 'id DESC')
);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
Usage examples:
-- default parameters either with null or an empty string
CALL selectp('', '');
CALL selectp(NULL, NULL);
-- set where conditions
CALL selectp('id IN(1, 3)', NULL);
-- set order by
CALL selectp(NULL, 'col1 DESC, col2');
Here is SQLFiddle demo
You've lost a semicolon after END IF.
And that's what mysql is pointed you to - it always shows you the expression part it couldn't parse. So always look at the code right before the one from the error description.
There should be a semi column after the END IF statement:
ELSE
SET #strCondition = ' Order by aaa desc';
END IF; //add semicolumn here
Before this line:
SET #Query = CONCAT(#Query, #strCondition );

How to run mysql queries that are stored in a mysql table using a procedure

I am developing a simple WPF(C#) application where i am storing all insert, update and delete queries in a table. These queries are then executed on server one by one by simply selecting and then executing using ExecuteNonQuery() function in c#. The problem is that if have a large number of queries then it becomes very slow and sometimes due to network connection it is falling out.
Is it possible to create a stored procedure that can execute the queries stored within a table on the same server?
Please answer as soon as possible. Thanks.
Here is some code that i have tried till now.
DELIMITER $$
CREATE PROCEDURE `MyProc`(wo varchar(100))
BEGIN
DECLARE x INT;
DECLARE str text;
set x = 0;
select count(*) into x from tblqueries where isexecutedonserver = false and woid = wo;
SET str = '';
REPEAT
select `query` into str from tblqueries where id = 2976;
SET x = x - 1;
UNTIL x > 0
END REPEAT;
##select str;
prepare stmt from #str;
execute stmt;
deallocate prepare stmt;
END $$
DELIMITER ;
Please check and tell me where I am wrong.
This is simple (as simple as googling "mysql stored procedure execute")
Declare a CURSOR in your stored procedure.
Execute the cursor, then prepare a statement with the output:
PREPARE stmt FROM #sql;
EXECUTE stmt USING #myvar;
This code will help you with it:
MySQL Pass table name to cursor select
It sounds like what you need is a view.
Views (including updatable views) are available in MySQL Server 5.0. Views are stored queries that when invoked produce a result set. A view acts as a virtual table. Views are available in binary releases from 5.0.1 and up.
http://dev.mysql.com/doc/refman/5.0/en/views.html

How to select return value from mysql prepared statement?

I am writing a stored procedure in MySQL. The following is the code I wrote:
SET #qry = 'SELECT id into #wid FROM work_profile where candidate_id = 1223 limit 1';
PREPARE statement FROM #qry;
EXECUTE statement;
However, #wid is null after execution.
SELECT id FROM work_profile where candidate_id = 1223 limit 1
This returns a value of 1443.
The following works for me:
USE test;
DELIMITER $$
CREATE PROCEDURE GETNID()
BEGIN
SET #query = 'SELECT 100 INTO #nid';
PREPARE statement1 FROM #query;
EXECUTE statement1;
END$$
DELIMITER ;
And to call the procedure:
CALL GETNUMBER();
SELECT #nid;
However, this does not seem to work in the MySQL Query Browser. I had to resort to using the MySQL command line client. I suspect the Query Browser clears the session after each statement.
For more EXECUTE examples, see the MySQL manual:
http://dev.mysql.com/doc/refman/5.1/en/sql-syntax-prepared-statements.html
The query may only return one row for use in this syntax. Either specify a WHERE clause which returns one row, or add LIMIT 1
Try this
set #qry := 'test';
execute statement using #qry
Remove the INTO #wid from your query and add LIMIT 1. The query will return a single row with the value you want.