Simple sql function with SELECT - mysql

I try to understand how functions work. I can make the equivalent in procedure but I can't create a simple function with select.
element is UNIQUE and
thing is PRIMARY
CREATE DEFINER=`root`#`localhost`
FUNCTION `get_element_by_thing`(`thing` VARCHAR(255))
RETURNS VARCHAR(255)
CHARSET utf8
NOT DETERMINISTIC
READS SQL DATA
SQL SECURITY DEFINER
DECLARE #return_element VARCHAR(255);
SET #return_element = (
SELECT
`element`
FROM
`table1`
WHERE
`thing` = thing
);
RETURN #return_element;
I use the phpmyadmin interface.

1) Don't declare user-defined variables.
The name of a local variable in MySQL stored program does not start with an at sign #. As an example:
DECLARE stored_program_local_variable VARCHAR(255);
SET stored_program_local_variable = 'somevalue';
The name of a user-defined variables start with an at sign #. (The at sign character is what distinguishes user-defined variables from other identifiers.) It's not valid to declare a user-defined variable in a stored program. To create a user-defined variable, just assign a value to it. For example:
SET #user_defined_variable = 'somevalue';
2) If we don't need to persist variables beyond the scope of a stored program, we typically use local variables, which exist only for the duration of the stored program execution. (Which is different behavior than user-defined variables which are at the session level.)
3) Use the SELECT ... INTO syntax to retrieve scalar values into user-defined or local variables. https://dev.mysql.com/doc/refman/5.7/en/select-into.html
Try:
DELIMITER $$
CREATE DEFINER=`root`#`localhost`
FUNCTION `get_element_by_thing`(`thing` VARCHAR(255))
RETURNS VARCHAR(255)
...
BEGIN
DECLARE return_element VARCHAR(255) ;
SELECT t1.element
INTO return_element
FROM table1 t1
WHERE t1.thing = thing
LIMIT 1 ;
RETURN return_element ;
END $$
DELIMITER ;
Note: with ambiguous identifiers (i.e. routine parameter and column with the same name in a SQL statement, the routine parameter takes precedence over the column name. Qualify the column reference with the table name or table alias so it's not ambiguous. I prefer to assign routine parameters (and local variables) names that do not match column names.
If for some reason you need to assign a value to a user-defined variable in a SQL statement, you can use the := assignment operator. This is also valid outside the context of a stored program.
SELECT #user_defined_variable := t.somecolumn
FROM mytable t
WHERE somecondition
ORDER BY someexpression
LIMIT 1

Related

MYSQL SELECT statement returns null when called inside a Stored Procedure

Hi I am trying to create a simple procedure with a select statement inside it :
DELIMITER //
CREATE PROCEDURE testProcedure(OUT login_id VARCHAR(30))
BEGIN
SELECT LOGIN_ID INTO login_id FROM USER_REG_MST WHERE USER_MOBILE_NO = 123;
END//
DELIMITER ;
The select satatement when called out side the proedure independently returns a value, but when the select is called inside a stored procedure it returns a null.
SELECT LOGIN_ID INTO login_id FROM mystrodb.USER_REG_MST WHERE USER_MOBILE_NO = 123;
Gives me a value.
But,
CALL testProcedure(#out);
SELECT #out;
returns a NULL.
Note: the columns is of type VARCHAR(30) (additional info)
The problem is that for MySQL LOGIN_ID and login_id are the same variable, so it's not recommended to name parameters and variables as the name of their objects or entities (tables, columns, etc.) of your database, see C.1 Restrictions on Stored Programs :: Name Conflicts within Stored Routines.
A couple of options: 1) Change the name of the output parameter of the stored procedure. 2) Include a qualifier to LOGIN_ID column, see 9.2.1 Identifier Qualifiers.
...
SELECT `USER_REG_MST`.`LOGIN_ID` INTO `login_id` ...
...
See dbfiddle.
You need to set OUT parameter and make sure to distinguish between column name vs parameter name:
DELIMITER //
CREATE PROCEDURE testProcedure(OUT login_id VARCHAR(30))
BEGIN
SET login_id = (SELECT u.LOGIN_ID
FROM USER_REG_MST u WHERE USER_MOBILE_NO = 123);
END//
DELIMITER ;
DBFiddle Demo

MySQL Call procedure inside function for count

Is it possible to use a procedure inside a function? For example, I would like to gather all my rows related to an id but I would also like to count the rows and use it in a select statement. This is not working:
drop procedure if exists relatives;
create procedure relatives(in parent int(11),out counted int(11))
begin
set counted=(select count(*) from category where related=parent);
end;
drop function if exists relatives_count;
create function relatives_count(parent parent(11)) returns int(11)
begin
declare count int(11);
call relatives(parent,counted);
return counted;
end;
So that I can use the count
select relatives_count(id) from category
This is just for curiosity purposes. It may look senseless since I can just call a single select query and get the same results but I want to know how I can use my procedure out variable in a function.
Yes, a MySQL FUNCTION can call a MySQL PROCEDURE.
But... the operations the procedure performs will be limited to the operations allowed by a function. (We can't use a procedure to workaround the limitations placed on a function.)
"is not working" is so nebulously vague as to be practically useless in debugging the issue. What exact behavior is being observed?
My suspicion is that the SQL statements shown are failing, because there is no override for the default statement delimiter.
Also, parent(11) is not a valid datatype.
Be aware that when an identifier for a column in a SQL statement in a MySQL stored program matches an identifier used for an argument or local variable, MySQL follows a rule about which (the column name or the variable) that is being referenced.
Best practice is to adopt a naming convention for arguments and local variables that do not match column names, and to qualify all column references with a table name or table alias.
Personally, I use a prefix for arguments and local variables (a for argument, l for local, followed by a datatype i for integer, d for date/datetime, n for decimal, ...
DELIMITER $$
DROP PROCEDURE IF EXISTS relatives$$
CREATE PROCEDURE relatives(IN ai_parent INT(11),OUT ai_counted INT(11))
BEGIN
SELECT COUNT(*)
INTO ai_counted
FROM category c
WHERE c.related = ai_parent
;
END$$
DROP FUNCTION IF EXISTS relatives_count$$
CREATE FUNCTION relatives_count(ai_parent INT(11))
RETURNS INT(11)
BEGIN
DECLARE li_counted INT(11);
CALL relatives(ai_parent,li_counted);
RETURN li_counted;
END$$
DELIMITER ;
Please identify the exact behavior you observe. Error message when creating the procedure? Error message when executing the function? Unexpected behavior. That's much more precise and informative than telling us something "is not working".

Creating MySQL Function

I am trying to write a function that looks for a value assigned to its configuration in a parent-child tree, if the value is null or empty it looks one level up for the value.
I am currently getting syntax errors when trying to create the function.
This is what i have so far,
DELIMITER //
CREATE FUNCTION `db`.`Configuration`(
`ColumnName` VARCHAR(128),
`CID` INT
)
RETURNS VARCHAR(256)
NOT DETERMINISTIC
BEGIN
DECLARE Config VARCHAR(256) DEFAULT NULL;
DECLARE Parent INT;
WHILE (#Config IS NULL OR #Config = "") DO
SELECT #ColumnName INTO #Config, `ParentID` INTO #Parent FROM `Table` WHERE `ID`=#CID;
END WHILE;
RETURN CONCAT(#Config, '::', #Parent);
END ;
//
DELIMITER ;
I am getting the following error when I try to add the function:
1327 - Undeclared variable: ParentID
Any help would be greatly appreciated!
You receive the error message in the question because you have multiple into clauses, whereas according to mysql manual on select ... into ... you can only have one. So, to get rid of this specific error message you nee to rewrite your select statement as:
SELECT #ColumnName, `ParentID` INTO #Config, #Parent FROM `Table` WHERE `ID`=#CID;
However, there are some further issues with your code:
varname and #varname do not refer to the same variable. The first one is either a function / stored proc parameter or local variable, while the 2nd one is a user-defined variable. In your code you must remove the # from the variable names.
You cannot use a variable in place of a field name in an sql statement. You must use dynamic sql with prepared statements to achieve this. See the following SO question on how to this: How To have Dynamic SQL in MySQL Stored Procedure
You do not overwrite CID parameter in your while loop. This means that if the first iteration the configuration will remain null, then you have an infinite loop. You should change the value of CID in your loop.
I cannot guarantee that there are no further errors in your code.
There are a few problems with your function:
You are using SELECT...INTO incorrectly. When selecting multiple values you should only use INTO once. For example SELECT a,b into #a,#b FROM...
You are using user-defined variables with similar names to your function parameters, but they are not the same thing. In your code CID and #CID are different. I suggest using standard naming prefixes to clarify this: for example use p_ for function parameters and v_ for local function variables. You shouldn't need to use user-defined variables at all.
Your WHILE loop is bound to lead to infinite loops since the query criteria never changes. If it returns NULL or empty string once, it will keep returning them forever.
Here's a quick rewrite to address the above issues. I'll leave it to you to implement the WHILE loop correctly:
DELIMITER //
CREATE FUNCTION `db`.`Configuration`(
p_column_name VARCHAR(128),
p_id INT
)
RETURNS VARCHAR(256)
READS SQL DATA
BEGIN
DECLARE v_config VARCHAR(256) DEFAULT NULL;
DECLARE v_parent INT;
SELECT p_column_name,`ParentID`
INTO v_config, v_parent
FROM `Table`
WHERE `ID`=p_id;
RETURN CONCAT(v_config, '::', v_parent);
END ;
//
DELIMITER ;

MySQL Stored Procedure and Identifiers

I am learning to write MySQL stored procedures and I have encountered some difficulties. Here I have two stored procedures:
First stored procedure:
CREATE PROCEDURE sp1 (IN `username` TEXT, OUT `user_id` INT)
BEGIN
DECLARE rowcount INT;
SELECT count(`User ID`) INTO rowcount FROM user WHERE `Username`=username;
SET user_id = rowcount;
END|
Second stored procedure:
CREATE PROCEDURE sp2 (IN `doc_id` INT, IN `content` LONGTEXT)
BEGIN
UPDATE doc SET `Content`=content WHERE `Doc ID`=doc_id;
END|
(Delimiter is |.)
Question:
I observe that the result of the first stored procedure is the same as calling SELECT count(`User ID`) FROM user;. However, the second stored procedure does its job and gets the content updated with the new content.
So why does the first stored procedure treat `Username` and username as equal identifiers but the second stored procedure treats `Content` and content as different identifiers? The two identifiers in both cases are the same except the capitalization of the first letter.
I figure it out after reading the official MySQL documentation about the scope of local variables.
It states that:
A local variable should not have the same name as a table column. If an SQL statement, such as a SELECT ... INTO statement, contains a reference to a column and a declared local variable with the same name, MySQL currently interprets the reference as the name of a variable.

MySQL stored procedure with OUT parameter

I keep getting a null value when using an out parameter.
CREATE PROCEDURE `getTotalPoints`(IN `uEmail` VARCHAR(255), OUT `iTotal` DECIMAL(5,2) UNSIGNED)
begin
select
coalesce(
coalesce(sum(points_earned),0) -
coalesce(sum(points_lost),0) +
coalesce(sum(points_taken),0) -
coalesce(sum(points_traded),0),0)
from user_activities
where user_email=#uEmail into #iTotal;
end
I know the select statement works fine as without the out parameter, I get 0.00 as a result.
I'm sure it's something simple, but everything I've researched and tried has resulted in the same NULL return value.
Remove the "#" symbols on the variable references.
"#uEmail" and "#iTotal" are references to user-defined variables, not the procedure arguments.
You want to reference the procedure arguments as "uEmail" and "iTotal", without the "#".
Or, you could probably get your statement to work if you did the necessary assignments to and from the user-defined variables, something like this in the body of the procedure:
-- set user-defined variable to value from procedure argument
SET #uEmail = uEmail;
-- statement references user-defined variables
SELECT ...#uEmail ... INTO #iTotal;
-- set procedure OUT argument from user-defined variable
SET iTotal = #iTotal;