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
Related
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)
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.
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'm writing a stored procedure. I am having some problems using a variable in combination with LIMIT and a prepared statement. (See code below)
The procedure is created normally, but when I call it I get an error that variable a is undeclared. Any suggestions?
delimiter //
drop procedure if exists shop1;
create procedure shop1()
begin
declare a varchar(255) default null;
declare counter int(4) default 0;
declare row_numbers int(4);
declare s varchar(255) default null;
select count(*) into row_numbers from salesmen;
WHILE counter< row_numbers+1 DO
set #s='select fio from salesmen into a Limit ? 1';
set #counter=counter;
prepare stmt from #s;
execute stmt using #counter;
SET counter=counter+1;
insert into warehouse.place (shop, fio) values (1, a);
END WHILE;
SET counter=0;
select count(*) into row_numbers from products;
WHILE counter< row_numbers+1 DO
SET counter=counter+1;
END WHILE;
end;
//
You could use an INSERT ... SELECT to do the same thing -
INSERT INTO warehouse.place (shop, fio)
SELECT 1, fio FROM salesmen
Did you mean?
set #s='select fio from salesmen into a Limit ?,1';
How can I construct a query based on parameters I receive? I want to add to the end of the query.
I tried something like this but it didn't work:
DELIMITER $$
CREATE PROCEDURE `get_users`(IN sortstring TEXT)
BEGIN
PREPARE statement FROM
"SELECT username, password FROM users ?";
SET #param = sortstring;
EXECUTE statement USING #param;
END$$
And I would pass to sortstring something like:
ORDER BY username DESC
Can I do this simpler by using concat or something?
You need to deconstruct the sortstring and check all its parts against a whitelist of allowed terms.
See the following pseudo code. I haven't fully tested it, but lets say it's the idea that counts.
DELIMITER $$
CREATE PROCEDURE `get_users`(IN sortstring TEXT)
BEGIN
//check sortstring against a whitelist of allowed sortstrings
DECLARE sortpart VARCHAR(255);
DECLARE done BOOLEAN DEFAULT false;
DECLARE allok BOOLEAN DEFAULT true;
DECLARE i INTEGER DEFAULT 1;
WHILE ((NOT done) AND allOK) DO
SET sortpart = SUBSTRING_INDEX(sortstring,',',i);
SET i = i + 1;
SET done = (sortpart IS NULL);
IF NOT DONE THEN
SELECT 1 INTO allok WHERE EXISTS
(SELECT 1 FROM whitelist
WHERE allowed_sort_claused = sortpart AND tablename = 'users');
END IF
END WHILE;
IF allOK THEN
PREPARE statement FROM
CONCAT('SELECT username, passhashwithsalt FROM users ',sortstring);
EXECUTE statement;
DEALLOCATE PREPARE statement;
ELSE SELECT 'error' as username, 'error' as passhashwithsalt;
END IF;
END$$
See: How to prevent SQL injection with dynamic tablenames?
The error in your code
You cannot use columnnames or SQL-keywords as parameters. You can only use values as parameters. For that reason your query will never pass prepare.
The ? in SELECT x FROM t1 ? wil just be replaced by SELECT x FROM t1 'ORDER BY field1, field2' Which makes no sense.