If condition in MySQL Stored Procedure not evaluating properly - mysql

I have written the below mentioned procedure in MySQL.
CREATE DEFINER=`root`#`localhost` PROCEDURE `IsUploaderLoggedIn`(
IN `inMobile` CHAR(10),
IN `inSessionID` varchar(34)
)
BEGIN
DECLARE isLoggedIn TINYINT(1) DEFAULT 0;
DECLARE uploaderType VARCHAR(10) DEFAULT '';
CALL GetUploaderType(inMobile, #x);
SELECT #x INTO uploaderType;
IF uploaderType = "surveyor" THEN
SELECT Count(*) INTO isLoggedIn FROM surveyors WHERE Mobile = inMobile AND SessionID = inSessionID;
SELECT "surveyor";
ELSE
SELECT Count(*) INTO isLoggedIn FROM uploaders WHERE Mobile = inMobile AND SessionID = inSessionID;
SELECT "uploader";
END IF;
SELECT isLoggedIn;
END;
On executing the procedure in Navicat, for a given value of inMobile and inSessionID, the values returned are:
Result 1: usertype | surveyor
Result 1(2): uploader | uploader
Result 1(3): 0 or 1 as the case may be
Where as, the value of Result 1(2)s should have been surveyor | surveyor.
Below is definition of stored procedure GetUploaderType:
CREATE DEFINER=`root`#`localhost` PROCEDURE `GetUploaderType`(
IN `inMobile` CHAR(10),
OUT `Usertype` VARCHAR(8)
)
BEGIN
DECLARE usertype VARCHAR(8) DEFAULT '';
DECLARE userExists TINYINT DEFAULT 0;
/*SET officeExists = 0;*/
/* Check if mobile number belongs to a surveyor */
SELECT Count(*) INTO userExists FROM surveyors WHERE Mobile = inMobile;
IF userExists = 1 THEN
SET usertype = 'surveyor';
ELSE
/* If user is not surveyor, check for it in uploaders */
SELECT Count(*) INTO userExists FROM uploaders WHERE Mobile = inMobile;
IF userExists = 1 THEN
SET usertype = 'uploader';
END IF;
END IF;
SELECT usertype;
END
What is wrong with my script please?

In GetUploaderType, you redeclared the variable Usertype, thus hiding the (different) out-variable of the same name, see scope:
The scope of a local variable is the BEGIN ... END block within which it is declared. The variable can be referred to in blocks nested within the declaring block, except those blocks that declare a variable with the same name.

Related

MYSQL Error #1415 on Function Creation: Not allowed to return a result set from a function

Trying to create a conditional-based function that will return a result. I don't if it's the way I am setting the result value that is causing the error? Making MySQL throw the error code 1415 Not allowed to return a result set from a function.
DELIMITER $$
CREATE FUNCTION GetTechFull ( table_flag INT,person_pk CHAR(11) )
RETURNS INT
BEGIN
DECLARE firstName VARCHAR(64);
DECLARE lastName VARCHAR(64);
DECLARE outputRes VARCHAR(64) DEFAULT NULL;
IF table_flag IS NULL OR person_pk IS NULL THEN
RETURN NULL;
END IF;
IF table_flag = 1 THEN
SELECT CONCAT(LEFT(ResFirstName,1), " ", ResLastName) as name,ResPhone as telephone, TPGText as pay_grade FROM cs_sp.ww_techfull;
SET outputRes = CONCAT(LEFT(firstName,1), " ", lastName);
END IF;
IF table_flag = 0 THEN
SELECT stdFirstName,stdLastName INTO firstName,lastName FROM student WHERE student.stdNo = person_pk;
SET outputRes = CONCAT(LEFT(firstName,1), " ", lastName);
END IF;
RETURN outputRes;
END$$
DELIMITER ;
your code has multiple problems, but the bggest ist that you a using a "normal select, which would return a result set, which is not allowed.
so oyu can only use, SELECT .. INTO..FROM..WHERE to get rid of the error message.
Iyour return Value doesn't correspond with the variable 'outputResthey must be f the same datatype
MySQL 8 also wants a DETERMINIsTIC added
Below you see a working code sample, so that you can go from here, to whereever you want
CREATE tABLE student(stdNo int, stdFirstName VARCHAR(64), stdLastName VARCHAR(64))
INSERT INTO student VALUES(1,'test2','testlast')
CREATE TABLe ww_techfull(ResNo int, ResFirstName VARCHAR(64), ResLastName VARCHAR(64)
,ResPhone varchar(16),TPGText varchar(64))
INSERT INTO ww_techfull VALUES(1,'testfrist', 'Testlast','012345656778','Bad')
CREATE FUNCTION GetTechFull ( table_flag INT,person_pk CHAR(11) )
RETURNS CHAR(64) DETERMINISTIC
BEGIN
DECLARE firstName CHAR(64);
DECLARE lastName CHAR(64);
DECLARE telephone CHAR(64);
DECLARE pay_grade CHAR(64);
DECLARE outputRes CHAR(64) DEFAULT NULL;
IF table_flag IS NULL OR person_pk IS NULL THEN
RETURN NULL;
END IF;
IF table_flag = 1 THEN
SELECT LEFT(CONCAT(LEFT(ResFirstName,1), " ", ResLastName),64)
,ResPhone , TPGText
INTO outputRes
,telephone, pay_grade
FROM ww_techfull WHERE ResNo = person_pk;
SET outputRes = LEFT(outputRes,64);
END IF;
IF table_flag = 0 THEN
SELECT LEFT(CONCAT(LEFT(stdFirstName,1), " ",stdLastName),64) INTO outputRes
FROM student WHERE student.stdNo = person_pk;
SET outputRes = LEFT(outputRes,64);
END IF;
RETURN outputRes;
END
SELECT GetTechFull(0,1)
| GetTechFull(0,1) |
| :--------------- |
| t testlast |
SELECT GetTechFull(1,1)
| GetTechFull(1,1) |
| :--------------- |
| t Testlast |
db<>fiddle here
You cannot use common SELECT in a function which sends its result to the output stream. You must use SELECT .. INTO {variables list}.
You may do not use intermediate variable and apply RETURN (SELECT {output column/expression} FROM ... WHERE ... ORDER BY ... LIMIT 1). The parenthesis prevents the output to be sent to the output stream and converts it to scalar value. ORDER BY LIMIT 1 usage is strongly recommended in this case, even when 1-row output is guaranteed now - it may become incorrect in future...
Your function does not process table_flag value other than NULL, 0 or 1 (which can be used, even errorneously). I'd recommend you to use something like
CASE table_flag WHEN 0
THEN RETURN ( {query 1} );
WHEN 1
THEN RETURN ( {query 2} );
ELSE RETURN NULL;
END CASE;

how does mysql user defined function know a selected row was found?

a MYSQL user defined function selects a row from a table. How does the UDF code determine if the selected row was found in the table?
CREATE FUNCTION snippetFolder_folderPath(folder_id int)
RETURNS varchar(512)
BEGIN
declare vFolder_id int;
declare vParent_id int;
declare vPath varchar(512) default '';
declare vFolderName varchar(256) default '';
set vFolder_id = folder_id;
build_path:
while (vFolder_id > 0) do
/* -------- how to know this select statement returns a row?? ---------- */
select a.parent_id, a.folderName
into vParent_id, vFolderName
from SnippetFolder a
where a.folder_id = vFolder_id;
if vPath = ' ' then
set vPath = vFolderName;
else
set vPath = concat_ws( '/', vFolderName, vPath );
end if ;
set vFolder_id = vParent_id;
end while ;
return vPath;
END
https://dev.mysql.com/doc/refman/8.0/en/select-into.html says:
If the query returns no rows, a warning with error code 1329 occurs (No data), and the variable values remain unchanged.
So you could declare a continue handler on warnings, something like the example from the documentation:
BEGIN
DECLARE i INT DEFAULT 3;
DECLARE done INT DEFAULT FALSE;
retry:
REPEAT
BEGIN
DECLARE CONTINUE HANDLER FOR SQLWARNING
BEGIN
SET done = TRUE;
END;
IF done OR i < 0 THEN
LEAVE retry;
END IF;
SET i = i - 1;
END;
UNTIL FALSE END REPEAT;
END
I'll leave it to you to read the documentation and adapt that example to your table and your loop.
Alternatively, if you're using MySQL 8.0 you can use recursive common table expression:
CREATE FUNCTION snippetFolder_folderPath(vFolder_id int)
RETURNS varchar(512)
BEGIN
DECLARE vPath varchar(512) DEFAULT '';
WITH RECURSIVE cte AS (
SELECT folderName, parent_id, 0 AS height
FROM SnippetFolder WHERE folder_id = vFolder_id
UNION
SELECT f.folderName, f.parent_id, cte.height+1
FROM SnippetFolder AS f JOIN cte ON cte.parent_id = f.folder_id
)
SELECT GROUP_CONCAT(folderName ORDER BY height DESC SEPARATOR '/')
INTO vPath
FROM cte;
RETURN vPath;
END
The recursive CTE result is all the ancestors of the row matching vFolder_id, and then one can use GROUP_CONCAT() to concatenate them together as one string.

Mysql Stored Proc not returning a VARCHAR out parameter

Below is my stored procedure. It works fine but my problem is I can't get the output parameter as VARCHAR.
The part where I'm having problem is the assignment of #curcName to the out parameter op_resultMessage
BEGIN
SET op_resultMessage = #curcName;
END;
Here's the Stored Procedure.
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCurriculum`(
IN p_curcName varchar(100),
IN p_description TEXT,
IN p_yearLevel VARCHAR(50),
IN p_syStart INT,
IN p_syEnd INT,
IN p_creator VARCHAR(50),
OUT op_resultMessage VARCHAR(50))
BEGIN
DECLARE curcName VARCHAR(20) ;
IF EXISTS
(SELECT #curcName := `name`
FROM curriculum
WHERE
yearLevel = p_yearLevel
AND syStart = p_syStart
AND syEnd = p_syEnd )
THEN --
BEGIN
SET op_resultMessage = #curcName;
END;
ELSE
BEGIN
INSERT INTO curriculum(`name`, description, yearLevel, syStart, syEnd, creator)
VALUES(p_curcName,p_description,p_yearLevel,p_syStart,p_syEnd,p_creator);
END;
END IF;
END
I'm trying to return a message IF name EXISTS
So it should go something like
SET op_resultMessage = #curcName 'already uses the school year and year level you're trying to insert';
But I don't know how to properly concatenate and assign values. I'm still confused with := SET and = operators. I guess that's where I'm having problems with.
If I change the out parameter's type to an INT like
OUT op_resultMessage VARCHAR(50)
then assigns a number to op_resultMessage like SET op_resultMessage = 1;
It returns the number 1 as out parameter values. It just won't work with varchar.
So when I try to call the procedure
CALL `enrollmentdb`.`addCurriculum`
('Test Curriculum ','Test ','Grade 1',2015,2016,'jordan',#outputMsg);
SELECT #outputMsg; -- this doesn't return any value even if Grade 1, 2015 and 2016 exists
I'd appreciate any help. I actually just learned mysql recently.
Thanks.
drop procedure if exists addCurriculum;
delimiter $$
CREATE PROCEDURE `addCurriculum`(
IN p_curcName varchar(100),
IN p_description TEXT,
IN p_yearLevel VARCHAR(50),
IN p_syStart INT,
IN p_syEnd INT,
IN p_creator VARCHAR(50),
OUT op_resultMessage VARCHAR(50))
BEGIN
DECLARE curcName VARCHAR(20) ;
SELECT `name` into #curcName
FROM curriculum
WHERE
yearLevel = p_yearLevel
AND syStart = p_syStart
AND syEnd = p_syEnd
LIMIT 1;
-- Note change above. When selecting into a variable (or more than 1)
-- then 0 or 1 rows can come back max or an Error occurs
IF #curcName is not null then
SET op_resultMessage = #curcName;
ELSE
BEGIN
INSERT INTO curriculum(`name`, description, yearLevel, syStart, syEnd, creator)
VALUES(p_curcName,p_description,p_yearLevel,p_syStart,p_syEnd,p_creator);
END;
SET op_resultMessage = 'GEEZ I am right here'; -- Drew added this
END IF;
END$$
delimiter ;
Note the commentary in the stored procedure, especially the part of only 0 or 1 rows returning else an Error will occur with a select into var pattern. So LIMIT 1. That may or may not be the row you want (limit 1), but that is where it is at right now.

MySql Fetch cursor into variable return null

The problem is that the FETCH INTO (in the loop) does not put the value into the variable. I've looked at MYSQL | SP | CURSOR - Fetch cursor into variable return null but the table is already populated.
The transaction table looks like this:
CREATE TABLE `transactionentry` (
`transactionid` bigint(20) NOT NULL AUTO_INCREMENT,
...
PRIMARY KEY (`transactionid`),
...
) ENGINE=InnoDB AUTO_INCREMENT=651 DEFAULT CHARSET=utf8;
The stored procedure:
PROCEDURE `doTxnHouseKeeping`()
BEGIN
-- Loop invariant
DECLARE noEntries INTEGER DEFAULT FALSE;
-- Error codes
DECLARE code CHAR(5) DEFAULT '00000';
DECLARE msg TEXT;
-- Txn vars
DECLARE transactionId BIGINT(20);
DECLARE lastTransactionId BIGINT(20) DEFAULT 0;
-- testing
DECLARE counter INT(11) DEFAULT 0;
DEClARE txnEntryCur CURSOR FOR
SELECT
`transactionid`
FROM
`transactionentry`
LIMIT 1;
DECLARE CONTINUE HANDLER FOR
NOT FOUND SET noEntries = TRUE;
DECLARE EXIT HANDLER FOR
SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
code = RETURNED_SQLSTATE, msg = MESSAGE_TEXT;
SELECT CONCAT('Error fetching transaction entries code: ', code, ' message: ', msg);
END;
OPEN txnEntryCur;
mainLoop: LOOP
FETCH
txnEntryCur
INTO
transactionId;
IF noEntries THEN
LEAVE mainLoop;
END IF;
IF transactionId IS NOT NULL THEN
INSERT INTO debugTable (`bigintval`) VALUES (transactionId);
ELSE
INSERT INTO debugTable (`strval`) VALUES ('transactionId is NULL');
END IF;
SET counter = counter + 1;
END LOOP mainLoop;
CLOSE txnEntryCur;
SELECT CONCAT("Count: ", counter);
END
Running the stored procedure returns this result:
+--------------------------+
|CONCAT("Count: ", counter)|
+--------------------------+
| Count: 1|
+--------------------------+
The result in the debug table is:
+------------+---------+-----------------------+
|iddebugTable|bigintval| strval|
+------------+---------+-----------------------+
| 1| NULL|"transactionId is NULL"|
+------------+---------+-----------------------+
Which means that the value was not copied in
When running the SQL (as it is in the stored procedure), it returns:
+-------------+
|transactionid|
+-------------+
| 591|
+-------------+
I found the problem and it is weird. It doesn't cause any error and / or exceptions, just doesn't put any values into the variables. The solution is to change the cursor declare statement from:
DECLARE txnEntryCur CURSOR FOR
SELECT
`transactionid`
FROM
`transactionentry`
LIMIT 1;
To:
DECLARE txnEntryCur CURSOR FOR
SELECT
`transactionentry`.`transactionid`
FROM
`transactionentry`
LIMIT 1;
Not even the documentation indicated that it might have been a problem (https://dev.mysql.com/doc/refman/5.7/en/declare-cursor.html)
I only fully qualify the SELECT (and WHERE) part of the SQL statement if I'm selecting from more than one table and thus never picked this up on more complex queries.
I hope this will save someone some time in the future.
Your problem is here:
DECLARE transactionId BIGINT(20);
You declare a variable named transactionId so when you do this:
DEClARE txnEntryCur CURSOR FOR
SELECT
`transactionid`
FROM
`transactionentry`
LIMIT 1;
Your cursor's select is picking up the variable you declared which is why fully qualifying the field works. However, if you don't want to fully qualify the field in your select you can rename your variable.
Try giving the variable transactionId a default value
...
DECLARE transactionId BIGINT(20) DEFAULT 0
...
and also replace
DECLARE noEntries INTEGER DEFAULT FALSE;
with
DECLARE noEntries BOOLEAN DEFAULT FALSE;
since you want to use it as a BOOLEAN value and set it to TRUE later in the procedure.

How to compare an argument value input with a variable in stored procedure mysql?

I'm trying to compare a variable declared within the procedure body with an input argument , but the result is always false.
Here es my code:
create procedure UserLogin(email VARCHAR(64),
reg_id VARCHAR(355),
code_version VARCHAR(10))
SELECT REG_ID INTO v_mi_regId
from USUARIO
where ID_USUARIO = email;
IF reg_id != v_mi_regId THEN <-- always false
UPDATE USUARIO
SET REG_ID = reg_id
WHERE ID_USUARIO = email;
END IF;
END
In the USUARIO table, the value REG_ID of a specific user is different from the argument. The update operation is never performed
Avoid naming variables and parameter as columns of your tables.
Try:
/* CODE FOR DEMONSTRATION PURPOSES */
DELIMITER //
DROP PROCEDURE IF EXISTS `UserLogin`//
CREATE PROCEDURE `UserLogin`(
`p_email` VARCHAR(64),
`p_reg_id` VARCHAR(355),
`code_version` VARCHAR(10)
)
BEGIN
DECLARE `v _mi_regId` VARCHAR(355);
SELECT `REG_ID` INTO `v _mi_regId`
FROM `USUARIO`
WHERE `ID_USUARIO` = `p_email`;
IF `p_reg_id` != `v_mi_regId` THEN
UPDATE `USUARIO`
SET `REG_ID` = `p_reg_id`
WHERE `ID_USUARIO` = `p_email`;
END IF;
END//
DELIMITER ;
/* CODE FOR DEMONSTRATION PURPOSES */
SQL Fiddle demo
See:
13.6.4.2 Local Variable Scope and Resolution
Name Conflicts within Stored Routines in D.1 Restrictions on Stored Programs