Is it possible to pass input parameter into Cursor SELECT statement WHERE clause?
For some reason I think it isn't working.
I'm trying to pass _TAG and _ITEM_NAME into where clause.
DECLARE cursor_test cursor for SELECT itemid FROM items WHERE key_ LIKE "sometext_#_TAG_sometext_#_ITEM_NAME" AND STATUS = '0';
Here is the the Stored procedure:
DELIMITER //
CREATE PROCEDURE getSomething(IN _HOSTNAME VARCHAR(20), _TAG VARCHAR(20), _ITEM_NAME VARCHAR(50))
BEGIN
declare FINISHED BOOL default false;
DECLARE cursor_test cursor for SELECT itemid FROM items WHERE hostid = #_HOSTID AND key_ LIKE "sometext_#_TAG_sometext_#_ITEM_NAME" AND STATUS = '0';
DECLARE CONTINUE HANDLER for not found set FINISHED := true;
SET #HOSTNAME = _HOSTNAME;
PREPARE STMT1 FROM
"SELECT hostid INTO #_HOSTID FROM hosts WHERE NAME = ?";
EXECUTE STMT1 USING #HOSTNAME;
DEALLOCATE PREPARE STMT1;
open cursor_test;
SET #TOTAL_VALUE := 0;
loop_itemid: loop
fetch cursor_test into _ITEMID;
SELECT _ITEMID;
if FINISHED then
leave loop_itemid;
end if;
SET #TOTAL_VALUE := #TOTAL_VALUE + (SELECT value from history_uint WHERE itemid = _ITEMID ORDER BY clock DESC LIMIT 1);
end loop loop_itemid;
SELECT #TOTAL_VALUE;
close cursor_test;
END //
Thanks to akina's comment. Using CONCAT in where condition worked.
WHERE key_ LIKE CONCAT('sometext_', _TAG, '_sometext_', _ITEM_NAME)
Related
So I've written a fairly simple MySQL stored procedure to retrieve values from a database for a personal app that I'm building. From everything I can see, the procedure should work just fine, but it's returning the wrong results.
Here's the procedure code:
USE randyrip_kdb;
DROP PROCEDURE IF EXISTS spGetAllTracksSong;
DELIMITER //
CREATE PROCEDURE spGetAllTracksSong(IN startRecord INT, IN rowsReturned INT, IN searchArtist VARCHAR(255), IN searchTitle VARCHAR(244), IN orderBy VARCHAR(20), IN duets TINYINT(1))
BEGIN
DECLARE spStart INT;
DECLARE spRows INT;
DECLARE whereClause VARCHAR(255) DEFAULT '';
DECLARE whereArtist VARCHAR(255) DEFAULT '';
DECLARE whereSong VARCHAR(255) DEFAULT '';
DECLARE outputSQL VARCHAR(1000) DEFAULT '';
SET spStart=startRecord;
SET spRows=rowsReturned;
IF searchArtist!='' THEN SET whereArtist= CONCAT('artist LIKE \'%',searchArtist,'%\' '); END IF;
IF searchTitle!='' THEN SET whereSong= CONCAT('song_title LIKE \'%',searchTitle,'%\' '); END IF;
IF whereArtist != '' && whereSong !='' THEN SET whereClause=CONCAT('WHERE ', whereArtist,'AND ',whereSong);
ELSEIF whereArtist !='' THEN SET whereClause= CONCAT('WHERE',whereArtist);
ELSE SET whereClause = CONCAT('WHERE',whereSong);
END IF;
IF duets=1 && whereClause !='' THEN SET whereClause=CONCAT(whereClause,' AND is_duet=1');
END IF;
SET orderBy = IFNULL(orderBy, 'song_title');
IF orderBy='date' THEN SET orderBy='date_added DESC'; END IF;
/*select whereClause;
select orderBy;
select startRecord;
select rowsReturned;*/
SET outputSQL=CONCAT('SELECT song_title, artist, comments, disc_number FROM track ', whereClause,'ORDER BY ' ,orderBy,' LIMIT ' ,spStart,',',spRows);
SELECT outputSQL;
SELECT song_title, artist, comments, disc_number FROM track whereClause ORDER BY orderBy LIMIT spStart,spRows;
END//
DELIMITER ;
I'm calling the Stored Procedure with these parameters:
call spGetAllTracksSong(0,20,'elvis costello','peace, love','date',0);
The variable outputSQL is correctly generating the query I want, and when I run it it's returning two rows as expected. However, the procedure itself is returning 20 rows, none of which match the criteria.
If anyone has any ideas as to what I'm doing incorrectly, that would be great. From all that I can see, everything should be fine however.
Randy,
if you use variables in the SQL query (like "FROM track whereClause"), you need to execute with EXECUTE, otherwise it will not be evaluated. Replace your last select with this:
set #sql = outputSQL;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Alternatively, you could try not to use dynamic SQL.
Data fetched from cursor is NULL. But number of row my query select id from Projects returning is correct. I have put a select statement in my procedure loop to debug. It's returning null for id.
I have followed this [https://dev.mysql.com/doc/refman/5.5/en/cursors.html]
What's wrong with this code?
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `ReportDiffProDiffFinYear`()
BEGIN
DECLARE id INT;
DECLARE sqlstr varchar(10000) default "select financialYear as 'name' ";
DECLARE done INT DEFAULT FALSE;
DECLARE cur1 CURSOR FOR select id from Projects; /* My CURSOR QUERY*/
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO id;
select id;
IF done THEN
LEAVE read_loop;
END IF;
set sqlstr = concat(sqlstr, ", sum(if(projects_id=",id,",0,1)) ");
END LOOP;
CLOSE cur1;
set #sqlstr = concat(sqlstr," from (select * from WaterPoints where status='Waterpoint Complete') as wp, (select id,financialYear from ProjectDetails) as pd where pd.id=wp.projectDetails_id group by pd.financialYear");
prepare stmt from #sqlstr;
execute stmt;
deallocate prepare stmt;
END
I believe that your variable name id clashes with the query for the cursor, therefore the cursor query fetches the value of the id variable (null), not the value of the id from the projects table. Either change the name of the variable or use projects.id to reference the column in the query.
I tried to prototype some sort of generic serialise function in MySQL which I want to use in several triggers for storing the current state of a table row into another table.
The goal is a string with key value pairs of the data, e.g.
id: 3; name: doe; surname: john; age: 25;
whereas the keys are the names of the table columns.
So far, I came up with this:
delimiter //
DROP PROCEDURE IF EXISTS SELECTOR //
CREATE PROCEDURE SELECTOR(
col_name varchar(255),
tbl_name varchar(255),
id integer,
out result text
)
BEGIN
set #q = CONCAT('SELECT ', col_name, ' FROM ', tbl_name, ' WHERE id = ', id );
PREPARE stmt FROM #q;
EXECUTE stmt;
DEALLOCATE PREPARe stmt;
END; //
DROP FUNCTION IF EXISTS SERIALISE //
CREATE FUNCTION SERIALISE(tbl VARCHAR(255), id integer) RETURNS text
BEGIN
DECLARE num_rows INTEGER;
DECLARE i INTEGER;
DECLARE col_name varchar(255);
DECLARE result varchar(255) DEFAULT "";
DECLARE value text;
DECLARE col_names CURSOR FOR
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = tbl
ORDER BY ordinal_position;
open col_names;
SELECT FOUND_ROWS() INTO num_rows;
SET i = 1;
the_loop: LOOP
IF i > num_rows THEN
CLOSE col_names;
LEAVE the_loop;
END IF;
FETCH col_names
INTO col_name;
call SELECTOR(col_name, tbl, id, value);
// here the value would be concated as well
SET result = CONCAT(result, col_name, ", ");
SET i = i + 1;
END LOOP the_loop;
return result;
END; //
Sadly, dynamic SQL is not allowed in functions or trigger, so the SELECTOR procedure does not work.
Does anybody have some hints how I could achieve my goal, or is it truly impossible?
I want to pass the parameter to the procedure and use it for the table name on declaring cursor. The following code returns an error message: #1146 - Table 'db.table_id' doesn't exist.
How do I use the parameter when declaring cursor?
Thanks
delimiter ;;
drop procedure if exists reset_id;;
create procedure reset_id(table_id VARCHAR(25))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE id INT;
DECLARE id_new INT;
DECLARE getid CURSOR FOR SELECT entryId FROM table_id ORDER BY entryId;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
SET #id_new = 1;
OPEN getid;
FETCH getid into id;
REPEAT
UPDATE table_id SET entryId = #id_new WHERE entryId = id;
SET #id_new = #id_new + 1;
FETCH getid into id;
UNTIL done END REPEAT;
CLOSE getid;
END
;;
CALL reset_id('Test');
After modifying the procedure, still returns an error #1324 - Undefined CURSOR: getid. How do i solve this problem?
delimiter ;;
drop procedure if exists test2;;
create procedure test2(table_id VARCHAR(25))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE id INT;
DECLARE id_new INT;
DECLARE stmt1 VARCHAR(1024);
DECLARE stmt2 VARCHAR(1024);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
SET #sqltext1 := CONCAT('DECLARE getid CURSOR FOR SELECT entryId FROM ',table_id,' ORDER BY entryId');
PREPARE stmt1 FROM #sqltext1;
EXECUTE stmt1;
SET #id_new = 1;
OPEN getid;
FETCH getid into id;
REPEAT
SET #sqltext2 := CONCAT('UPDATE ',table_id,' SET entryId = ? WHERE entryId = ?');
PREPARE stmt2 FROM #sqltext2;
EXECUTE stmt2 USING #new_id, id;
SET #id_new = #id_new + 1;
FETCH getid into id;
UNTIL done END REPEAT;
CLOSE getid;
END
;;
CALL test2('Test');
The table name has to be specified in the SQL text; it cannot be a variable.
To accomplish what you are trying to do, you are going to need to dynamically create a string that contains the SQL text you want to execute.
To prepare the statement from arbitrary string:
SET #sqltext := CONCAT('UPDATE ',table_id,' SET entryId = ? WHERE entryId = ?');
PREPARE stmt FROM #sqltext;
Note that the table_id value is getting incorporated into a string variable, and then the PREPARE statement is (essentially) turning that string into an actual SQL statement.
To execute the prepared statement and supply values for the bind variables, you'd do something like this:
EXECUTE stmt USING #new_id, #id;
You can re-execute a prepared statement multiple times, without needing to prepare it again. So, the PREPARE would be done before your loop, the EXECUTE can be done inside the loop.
Once you are finished with the statement, following the loop, the best practice is to deallocate the statement like this:
DEALLOCATE PREPARE stmt;
NOTE:
The restriction about the table name not being a variable actually applies to all identifiers in a SQL statement, including names of tables, views, columns, functions, etc. Those all have to be literals in the SQL text, just like the reserved keywords do.
I wrote this function:
delimiter //
CREATE FUNCTION randomDefVal(val varchar(30), tableName varchar(30))
returns varchar(30)
BEGIN
SET #query = concat('SELECT ',val,' FROM ',tableName,' ORDER BY rand() LIMIT 1;');
SET #result = NULL;
PREPARE stmt1 FROM #query;
return (EXECUTE stmt1);
END//
But I have an error in the last line:
SQL Error (1336): Dynamic SQL is not allowed in stored function or trigger
Which suggests that I cannot write 'return (EXECUTE stmt1);'
How can I return the value, which will be the result of the 'EXECUTE' statement?
I think what you want is SELECT ... INTO. So you would have something like this within your BEGIN and END (note that I have not tested this code):
BEGIN
DECLARE var_name VARCHAR(30);
SET var_name = '';
SELECT val INTO var_name FROM tableName ORDER BY rand() LIMIT 1;
RETURN var_name;
END