MySQL stored procedure: OUT parameter not being set - mysql

I've got a stored procedure in MySQL that gets the next unique ID from a table, to use as an ID for 2 other tables (not the best way to do it, I'm sure, but I'm modifying someone else's code here). The procedure is as follows:
DELIMITER $$
CREATE DEFINER=`root`#`%` PROCEDURE `GetNextID`( OUT id bigint )
BEGIN
DECLARE uid VARCHAR(255);
SET uid = uuid();
INSERT INTO `ident_column_generator` (u) VALUES (uid);
SELECT ID INTO id FROM `ident_column_generator` WHERE u = uid;
DELETE FROM `ident_column_generator` WHERE u = uid;
END$$
When I call the procedure from MySQL Workbench:
CALL GetNextID( #id );
SELECT #id;
#id is NULL. I can't work out what's going wrong? Even if I run SET #id = 0; before calling the procedure, it ends up as NULL afterwards. If I call the functions within the procedure manually from MySQL Workbench, #id outputs fine, e.g.:
SET #uid = uuid();
INSERT INTO `ident_column_generator` (u) VALUES (#uid);
SELECT ID INTO #id FROM `ident_column_generator` WHERE u = #uid;
DELETE FROM `ident_column_generator` WHERE u = #uid;
SELECT #id;
This outputs #id as being a valid number.
Any ideas why id isn't being set properly?

Typically, spent 3 hours on this, then JUST after I posted the question I find the problem. So, for future reference: It appears MySQL is case insensitive where variables are concerned. The ID column name and id variable apparently completely confused it.
I changed the procedure's input parameter name to retId and then it worked perfectly.

Thanks Nick, I also had same issue. The column name and variable name were same due to which we were getting the issue.

Related

Why is DECLARE in the wrong spot? [MySQL Workbench]

I have been spinning my wheels trying to figure out what I have wrong. I'm new to MySQL so it could be something simple. I am creating a stored procedure to increase a user's in-game currency. I try an DECLARE a variable for the procedure and workbench gives me an error saying "not valid in this position, expecting END". Everything that I've looked up online says to do it this way. If I move where the DECLARE is to above the SET TRANSACTION I can get it so there are no errors but the procedure doesn't change the value in currency. I believe this is because the variable isn't declared and so it doesn't have anywhere to store the starting balance ergo can't add the amount to the balance. I did see some articles that mentioned not putting in the semi-colon but I tried changing that but that generates different errors. Any help would be much appreciated.
CREATE DEFINER=`root`#`localhost` PROCEDURE `addCurrencyBalance`(userID INT, amount INT)
BEGIN
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
DECLARE balance INT;
SET balance = (SELECT currency
FROM users
WHERE user_id=#userID);
SET #amount = balance + #amount;
UPDATE users
SET
currency = #amount
WHERE
user_id = #userID;
SELECT currency FROM users WHERE user_id=userID;
COMMIT;
END
You are mixing user variables and local variables, which makes the code messy. For example userID is an argument to the function, but then you refer to it using #userID. Also, the variable names clash with actual column names of the table.
Bottom line, I don't think you need that complicated logic. It seems like a simple update statement does what you want. So your code becomes a single-statement procedure, like so:
create procedure addcurrencybalance (p_user_id int, p_amount int)
update users
set currency = currency + p_amount
where user_id = p_user_id
;
Do not use the same names for parameters, declared variables and column, and do not confuse users defined (at) variables with local (declared variables). Your code corrected is
delimiter $$
CREATE PROCEDURE p1(p_userID INT, p_amount INT)
BEGIN
DECLARE balance INT;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET balance = (SELECT currency FROM t WHERE user_id=P_userID);
SET p_amount = balance + p_amount;
UPDATE t
SET currency = p_amount
WHERE user_id = p_userID;
#SELECT currency FROM t WHERE user_id=p_userID;
END $$
#GMB answer is better..

MySql variable stored with select count statement always returns 1

Hello in a test database i have only one row in the table customer.I have created a stored procedure to do some work. Every time i execute this query
select count(*) from Customer where Email= ....
in my database i get a count 0 but in my stored procedure the result is always 1 without any error.This is the code inside my stored procedure.
BEGIN
START TRANSACTION;
SET #var=(select count(*) from Customer where Email=email);
select #var;
if (#var>0) then
update Customer Set Password=temppass,TemporaryPassword=temppass where
Email=email;
COMMIT;
ELSE
ROLLBACK;
END IF;
END
Any ideas please?Thanks!
The problem is your condition Email = email. This is always going to be true -- because both references to email are referring to the same thing, the email column in the table.
Call the variable by a different name. I often use the suffix v_, so where customer.email = v_email.
That said, I think you can just do:
begin
update Customer c
set c.Password = v_temppass,
c.TemporaryPassword = v_temppass
where c.email = v_email;
end;
I don't see what value the transaction gives.
I assume that you are not storing passwords as clear-text. That is definitely a no-no if you care at all about security and access.

MySQL stored procedure returns wrong values

Im new to MySQL stored procedures and I was following some tutorial on how to use them, but I ran into an interesting thing with the following:
DELIMITER $$
CREATE DEFINER=`user`#`%` PROCEDURE `CalculateScores`(IN ID INT, OUT test INT)
BEGIN
SELECT COUNT(*)
INTO test
FROM myTable
WHERE id = ID;
END$$
DELIMITER ;
I run it with this:
CALL CalculateScores(252, #test);
and then just:
SELECT #test;
The strange thing is that #test returns the total row count of the entire table not just for the id I sent as a parameter.
What am I missing here? The tutorial never mention this, and I can't find an answer to why this is happening, I might suck at searching..
It looks like MySQL cannot differentiate between id and ID:
SELECT COUNT(*)
INTO test
FROM myTable
WHERE id = ID;
And it treats it like 1 = 1 which is always true (if column is not nullable).
You could add alias to indicate that id is column and not parameter.
CREATE PROCEDURE `CalculateScores`(IN ID INT, OUT test INT)
BEGIN
SELECT COUNT(*)
INTO test
FROM myTable t
WHERE t.id = ID;
END
db<>fiddle demo

Add multiple tags to table

I have a table tags in database and I want create a query that was based on add values if not exist name of tag, next selected ID and if name exist selected tag ID
I creat a simply code:
DELIMITER $$
USE `cc`$$
DROP FUNCTION IF EXISTS getTagId$$
CREATE FUNCTION getTagId (tag CHAR(20)) RETURNS INT CHARSET utf-8
BEGIN
DECLARE id INT
set #id = (select id from tags where name = #tag)
if (#id is null) then
insert into tags(name) values(#tag)
set #id = (select last_insert_id())
end if
SELECT #id
END
delimiter;
But this code doesn't work (This is my first function in MySQL).
I want add to database more tags then 10 in single query, do you have any ideas on how to optimize?
But probably my old answer is useless. The real question is: is this Stored Function really needed? I guess you are trying to do this just to avoid duplicates.
To reach the same goal:
add a UNIQUE index to tags.name
use INSERT IGNORE (no error if that tag already exists)
You should add a $$ before last line.
And also, you forgot a lot of ;
Returning a number and specifying the charset is absurd, and probably is a syntax error.
Drop all lines with #id and simply add RETURN last_insert_id();
IF NOT EXISTS ((select id from tags where name = tag)) THEN
(also note that I removed # from tag!)
Probably there are more errors, but star with these :)

MySQL Procedure check if record exists before insert not working

I have looked at the other questions on here about this. It isn't working.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `environment_admin`(
IN environment_id TEXT,
IN user_id TEXT,
IN username VARCHAR(75),
IN password VARCHAR(512)
)
BEGIN
DECLARE env_id INT;
DECLARE admin_id INT;
SET env_id = CAST(environment_id AS SIGNED INT);
SET admin_id = CAST(user_id AS SIGNED INT);
IF NOT EXISTS(SELECT 1 FROM `environment`.`environment_accounts` WHERE environment_id = env_id AND user_id = admin_id) THEN
BEGIN
INSERT INTO
`environment`.`environment_accounts`
(
`environment_id`,
`user_id`,
`username`,
`password`,
`is_active`,
`is_admin`,
`is_mod`
)
VALUES
(
env_id,
admin_id,
username,
password,
1,
1,
1
);
END;
END IF;
END
So if I run:
CALL `environment`.`environment_admin`('22','1','kacieh','512c9ad228332bbd30d09ce7ffb8896e00a1610e914a5fa180bf15ce702b90423e6a9540579f672315ae3c6cb1b8d06ee2b784b4761e806675aa88c2a915553e');
I get 0 row(s) effected
and sure enough, nothing happened. -_- I have been working on this hours
I tested the conditional query, it works.
I have tested just the insert statement inside a stored proc, it works as well.
Stop doing it like that, it's inefficient and it could be worse if two insertare running concurrently! :)
Use INSERT.... ON DUPLICATE KEY UPDATE ... see here
One trick is to do ON DUPLICATE KEY UPDATE environment_id = env_id (not changing the column, so nothing will be updated, ensuring the INSERT will not work without any error condition, you might check number of modified/inserted rows after that)
I realise you've solved your problem (and +1 for Parallelis's answer, especially for highlighting the concurrency issue), but just in case it helps someone else...
MySQL was probably getting confused between your parameters environment_id and user_id and the environment_accounts columns environment_id and user_id. I suspect the parameters were taking precedence in the WHERE clause, meaning as long as there's at least one row in environment_accounts, the NOT EXISTS clause would always return false, and your insert would never run.
For example, if your environment_id and user_id parameters had values of 1 and 2 respectively, the NOT EXISTS clause would evaluate as
IF NOT EXISTS(SELECT 1 FROM `environment`.`environment_accounts` WHERE 1 = 1 AND 2 = 2) THEN
Might be worth having a naming convention for your parameters (and other variables), such as adding a prefix like p_ for parameter.