I'm learning stored procedures/functions/triggers in MySQL this morning and I'm having some problems trying to use variable table and column names in queries.
DELIMITER |
USE R2R |
DROP FUNCTION IF EXISTS getCategoryName |
CREATE FUNCTION getCategoryName(LMID INT, levelNum INT)
RETURNS VARCHAR(255)
BEGIN
DECLARE levelName VARCHAR(255) DEFAULT makeLevelName(levelNum);
DECLARE levelID INT;
DECLARE levelNameID VARCHAR(255) DEFAULT CONCAT(levelName, 'ID');
DECLARE ret VARCHAR(255);
SELECT #levelNameID INTO levelID FROM LevelMaster WHERE id=LMID;
SELECT description INTO ret FROM #levelName WHERE id=levelID;
RETURN ret;
END;
|
DROP FUNCTION IF EXISTS makeLevelName |
CREATE FUNCTION makeLevelName(levelNum INT)
RETURNS VARCHAR(255)
BEGIN
DECLARE word VARCHAR(255);
DECLARE ret VARCHAR(255);
IF levelNum=2 THEN SET word='Two';
ELSEIF levelNum=3 THEN SET word='Three';
ELSEIF levelNum=4 THEN SET word='Four';
ELSEIF levelNum=5 THEN SET word='Five';
END IF;
SET ret=CONCAT('Level', word);
RETURN ret;
END;
|
SELECT getCategoryName(347, 2) |
It's the first function (getCategoryName) that's causing me the problems, I need the two variables marked with # to be the table/column names - these two lines:
SELECT #levelNameID INTO levelID FROM LevelMaster WHERE id=LMID;
SELECT description INTO ret FROM #levelName WHERE id=levelID;
I want to keep this function as a function rather than a procedure if possible, but would accept answers for a procedure if it's the only way.
Thanks for you help,
Richard
Use User/Global Vars for this along with PREPARE & EXECUTE:
SET #columnName='myColumn';
SET #tableName='myTable';
SET #whatEver='requiredValue';
SET #query=CONCAT('SELECT ', #columnName, ' FROM ', #tableName, ' WHERE Column=', #whatEver);
PREPARE QUERY FROM #QUERY;
EXECUTE QUERY;
Haven't tested this EXACT code but something along these lines will work. Also has to be inside a Procedure, cannot be used with a function or trigger, if anyone has a soloution for that then please post.
Related
I have an issue in my code where i took this : (ApliEMAIL int) in below code but when i execute function it return empty values because in table i took email filed as varchar.
When i write code with this (ApliEMAIL varchar) it does not create function and gives error.
DELIMITER $$
#DROP FUNCTION IF EXISTS `get_appli_name`$$
CREATE FUNCTION get_appli_name_by_email(ApliEMAIL int)
RETURNS VARCHAR(255)
BEGIN
DECLARE A_NAME VARCHAR(255) DEFAULT"";
SELECT apli_fname INTO A_NAME
FROM tbl_signup
WHERE apli_email = ApliEMAIL;
RETURN A_NAME;
END$$
DELIMITER ;
DELIMITER $$
#DROP FUNCTION IF EXISTS `get_appli_name`$$
CREATE FUNCTION get_appli_name_by_email(ApliEMAIL varchar)
RETURNS VARCHAR(255)
BEGIN
DECLARE A_NAME VARCHAR(255) DEFAULT"";
SELECT apli_fname INTO A_NAME
FROM tbl_signup
WHERE apli_email = ApliEMAIL;
RETURN A_NAME;
END$$
DELIMITER ;
I tested your function. I got this error.
ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you might want to use the less safe log_bin_trust_function_creators variable)
By the way, in the future, when you are asking for help with an error, include the error message in your post. Help us to help you! Don't make us guess!
Read the documentation about READS SQL DATA here: https://dev.mysql.com/doc/refman/8.0/en/stored-programs-logging.html
So I added that option to your function:
CREATE FUNCTION get_appli_name_by_email(ApliEMAIL VARCHAR(255))
RETURNS VARCHAR(255)
READS SQL DATA
BEGIN
DECLARE A_NAME VARCHAR(255) DEFAULT"";
SELECT apli_fname INTO A_NAME
FROM tbl_signup
WHERE apli_email = ApliEMAIL;
RETURN A_NAME;
END$$
It works now:
mysql> select get_appli_name_by_email('thefrog#muppets.org');
+------------------------------------------------+
| get_appli_name_by_email('thefrog#muppets.org') |
+------------------------------------------------+
| Kermit |
+------------------------------------------------+
You are missing the length of the ApliEMAIL-parameter.
Insted of:
CREATE FUNCTION get_appli_name_by_email(ApliEMAIL varchar)
you should have:
CREATE FUNCTION get_appli_name_by_email(ApliEMAIL varchar(255))
..or whatever the length of ApliEMAIL is.
You must add deterministic or reads sql data flag.
DELIMITER $$
CREATE FUNCTION get_appli_name_by_email(ApliEMAIL varchar(50))
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
RETURN ifnull((SELECT apli_fname FROM tbl_signup WHERE apli_email = ApliEMAIL LIMIT 1), '');
END$$
DELIMITER ;
I want to create a table dynamical but my built string to create the table is NULL - why?
The goal is to get column values from an existing table and create a new table with columns named with these values.
DELIMITER $$
DROP PROCEDURE IF EXISTS fanart_test.get_incomplete_artwork $$
CREATE PROCEDURE fanart_test.get_incomplete_artwork()
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE type_id INT;
DECLARE type_name INT;
DECLARE build_string VARCHAR(20000);
DECLARE cursor1 CURSOR FOR
SELECT type_id,type_name FROM fanart_types WHERE type_section = 3;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cursor1;
get_results: LOOP
FETCH cursor1 INTO type_id, type_name;
IF finished = 1
THEN LEAVE get_results;
END IF;
IF build_string = ""
THEN SET build_string = CONCAT('CREATE TABLE `tmp_incomplete_artwork`(`', type_name, '` TEXT');
ELSE SET build_string = CONCAT(build_string,',', type_name);
END IF;
SET build_string = CONCAT(build_string,')');
END LOOP get_results;
CLOSE cursor1;
SET #s = build_string;
PREPARE build FROM #s;
EXECUTE build;
DEALLOCATE PREPARE build;
END $$
DECLARE build_string VARCHAR(20000);
The build_string is not set to anything initially, so it'll probably be NULL.
IF build_string = ""
This will never return true, since NULL = "" is not true.
ELSE SET build_string = CONCAT(build_string,',', type_name);
Concatenating any string with NULL returns NULL.
Re your comment:
You have named your variables type_id and type_name which are the same as your column names in your table. This creates an ambiguity, and it turns out that MySQL prefers to interpret the identifiers as the local variables, instead of column names.
So this:
SELECT type_id,type_name FROM fanart_types WHERE type_section = 3;
Will return the current value of type_id and type_name, which is uninitialized, i.e. NULL. Therefore a pair of NULLs are returned for all rows of the table.
Just rename the variables to be distinct from your table's column names, or else qualify the columns so they are clearly columns instead of variables:
SELECT f.type_id, f.type_name FROM fanart_types f WHERE f.type_section = 3;
Also you probably want to declare type_name as TEXT instead of INT.
I have this:
SELECT TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name = 'whatever'
but what I need is something like this:
SELECT TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_data = 'whatever'
So, in words, I have a value and I have no idea where it's stored. Is there a way to literally check the entire database and return the table, column?
aaaand yes, I know, db admins wouldn't be happy!
This might get you going in the right direction.
1. Create find_column stored procedure
DROP PROCEDURE IF EXISTS `find_column`;
DELIMITER $$
CREATE PROCEDURE `find_column`(IN i_value varchar(200),
OUT o_columns varchar(2000),
OUT o_message varchar(500))
MAIN_BLOCK : BEGIN
DECLARE is_numeric boolean;
CHECK_NUMERIC : BEGIN
set is_numeric = i_value REGEXP '^(-|\\+){0,1}([0-9]+\\.[0-9]*|[0-9]*\\.[0-9]+|[0-9]+)$';
END CHECK_NUMERIC;
FIND_IT : BEGIN
DECLARE bNoMoreRows BOOLEAN DEFAULT FALSE;
DECLARE v_schema varchar(64);
DECLARE v_table varchar(64);
DECLARE v_column varchar(64);
DECLARE v_data_type varchar(64);
DECLARE v_count int;
-- all schemas, tables and columns in DB
DECLARE columns CURSOR FOR
select table_schema,table_name,column_name,data_type from information_schema.columns;
DECLARE EXIT HANDLER for SQLEXCEPTION set o_message := concat('Unexpected error while trying to find schema, table and column for value : ',i_value);
declare continue handler for not found set bNoMoreRows := true;
open columns;
set o_columns = "";
COLUMN_LOOP: loop
fetch columns
into v_schema,v_table,v_column,v_data_type;
if (
(v_data_type in ('int','bigint','tinyint','decimal','smallint','mediumint') and is_numeric=1)
or (v_data_type not in ('int','bigint','tinyint','decimal','smallint','mediumint') and is_numeric=0)
)
then
SET #dyn_sql=CONCAT('select count(*) into #c from `',v_schema,'`.`',v_table,'` where `',v_column,'`=?');
SET #c = 0;
SET #v_value = i_value;
PREPARE stmt FROM #dyn_sql;
EXECUTE stmt using #v_value;
DEALLOCATE PREPARE stmt;
SET v_count = #c;
if v_count > 0 then
if length(o_columns <= 1800) then
set o_columns = concat(o_columns,",",v_schema,".",v_table,".",v_column);
end if;
end if;
end if;
if bNoMoreRows then
set o_columns = substring(o_columns,2);
close columns;
leave COLUMN_LOOP;
end if;
END loop COLUMN_LOOP;
END FIND_IT;
END MAIN_BLOCK$$
DELIMITER ;
2. Call find_column stored procedure with your value
call `find_column`('whatever',#columns,#message);
3. Check out the results
select #columns;
The is_numeric bit is lovingly ripped-off JBB's answer from this post.
It ain't perfect (what happens if the number of columns that your value exists exceeds 10 or so? If that is the case then this will only return the first 10 or so columns (depends on how long the schema.table.column name string is).
Hopefully it'll get you going in the correct direction.
An you're right. You're DB admins will be unhappy with you. But if you don't annoy them once in a while then you're not trying hard enough IMHO ;-)
Good luck.
I have a stored function in MySQL and it works partially.
DELIMITER $$
DROP FUNCTION IF EXISTS `getsubdomain`$$
CREATE FUNCTION getsubdomain(page_id int(11))
RETURNS CHAR(255)
DETERMINISTIC
READS SQL DATA
SQL SECURITY INVOKER
BEGIN
declare current_p_id int(11);
declare current_p_parent_id int(11);
declare current_p_address_type char(255);
declare current_p_adress char(255);
SET current_p_id = page_id;
WHILE (current_p_id) <> 0
DO
select p_parent_id, p_address_type, p_adress from opu_pages where p_id = current_p_id into current_p_parent_id, current_p_address_type, current_p_adress;
IF current_p_address_type <> ''
THEN
IF current_p_address_type = 'subdomain'
THEN
RETURN current_p_adress;
ELSE
SET current_p_id = current_p_parent_id;
END IF;
ELSE
RETURN NULL;
END IF;
END WHILE;
RETURN NULL;
END$$
DELIMITER ;
If I call in query SELECT getsubdomain(p_id) FROM opu_pages; it works Ok. But if I call it in SELECT * FROM opu_pages WHERE getsubdomain(p_id)='library'; the database is collapsed and freezing.
Query and function work with one table.
What did I do wrong?
I thought that it can be caused by the table format MyISAM. But I can't change it to InnoDB because I use FULLTEXTFORMAT fields in this table.
Table opu_pages (MyISAM) scheme
p_id INT
p_parent_id INT
p_address_type ENUM (path, subdomain)
p_adress VARCHAR
Based on your post I would say that your code is entering an infinite loop for some of your input parameters.
In particular the case where p_id = p_parent_id in the opu_pages table and the current_p_address_type = 'subdomain'
I am not a MYSQL developer, but just had to write some code. Please accept my apologies if I am doing anything silly in my code.
My need is to get a single row like select * from users where userID = 1 limit 1 and assign it to a variable and access columns for doing some calculation. Firstly, is this possible?
I have tried to go through step by step, that is why I wrote a simple function like below
DELIMITER $$
CREATE DEFINER=`user`#`%` FUNCTION `GetReportees`(userid VARCHAR(255)) RETURNS varchar(50) CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE Var1 varchar(120);
DECLARE Var2 varchar(120);
Select #Var1=forename, #Var2=surname from company_users.users where userID = #userid limit 1;
return #Var1;
END
when I try to save this function, it says that ERROR 1415: Not allowed to return a result set from a function. But I clearly return a varchar variable.
Could anyone tell me what I am doing wrong? it should not be this much hard, I believe.
Many thanks
Regards
You should read the documentation on the difference between user-defined variables and local variables.
In your example, you have a parameter and 2 local variables, so you should use them like this:
DELIMITER $$
CREATE DEFINER=`user`#`%` FUNCTION `GetReportees`(p_userid VARCHAR(255))
RETURNS varchar(120) CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE Var1 varchar(120);
DECLARE Var2 varchar(120);
Select forename, surname
into Var1,Var2
from company_users.users
where userID = p_userid
limit 1;
return Var1;
END