I am making a stored procedure in order to check that whether a user exists or not. The problem is whenever I pass it the correct username, it executes successfully but when I pass wrong username, it go through an infinite loop.
Where am I going wrong? Here is my Stored Procedure:
CREATE PROCEDURE `VerifyUserNPass`(userParam varchar(50), out result int)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tempUser varchar(50) default '';
DECLARE count int default 0;
DECLARE noRows int;
DECLARE userList cursor for select userName from users;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
select count(*) into noRows from users;
OPEN userList;
read_loop: LOOP
FETCH userList into tempUser;
IF tempUser = userParam THEN
SET #count = count + 1;
LEAVE read_loop;
ELSEIF count > noRows THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE userList;
select count into result;
END
To fix your code, use a conditional test on the done variable to determine whether to leave the loop. (The done variable is initialized to FALSE, and then set to TRUE in the CONTINUE HANDLER when there are no more rows.)
Get rid of the query "count(*)" query, and the noRows variable; those are not needed. (In a concurrent system, it's possible for that count(*) query to return a value that differs from the number of rows returned by a later query. (Consider that its possible for other sessions to insert or delete rows while your procedure is running.)
Also get rid of the references to the #count user variable. You have a mixture of references, to both a user variable #coount and a procedure variable count. These are independent variables. There's no need for your procedure to use a user variable. Instead, stick with using variables that are declared in your procedure. (Save the user variables for when you have a need.)
-- select count(*) into noRows from users;
read_loop: LOOP
FETCH userList into tempUser;
IF done THEN
LEAVE read_loop;
END IF;
IF tempUser = userParam THEN
SET count = 1;
LEAVE read_loop;
END IF;
END LOOP;
A more efficient way to code this would be to let the database find the row(s) you are interested in, by adding a WHERE clause on your query. (There's no need for you to fetch rows that aren't going to match the condition you are interested in.)
Modify your cursor definition to include a predicate (i.e. a condition in a WHERE clause) to limit the rows returned:
DECLARE userList cursor for select userName from users
WHERE userName = userParam LIMIT 1;
I don't understand the need for a procedure here. A native SQL statement would be much more efficient, e.g.
SELECT 1 AS found FROM users u WHERE u.userName = 'foo' LIMIT 1;
Make sure you increment your loop counter for all branches not only in the succes branch and have result variable to hold your outcome.
DECLARE userFound int default 0;
......
read_loop: LOOP
FETCH userList into tempUser;
SET #count = #count + 1;
IF tempUser = userParam THEN
SET #userFound = 1
LEAVE read_loop;
ELSEIF count > noRows THEN
LEAVE read_loop;
END IF;
.....
select userFound into result;
And I would expect that this would return the same result (but I'm not an MySql expert)
CREATE PROCEDURE `VerifyUserNPass`(userParam varchar(50), out result int)
BEGIN
select COUNT(userName) into result from users where username = #userParam
END
Related
My cursor is just processing 1st record and coming out. it's not going to second record. what could be the issue? Below is my code:
There are more than 600,000 rows to process. When I execute the select query it shows me a correct number of rows but the loop is not working.
Delimiter $$
CREATE PROCEDURE p_updateHistory_1()
BEGIN
DECLARE v_symbol varchar(10);
DECLARE v_pricedate date;
DECLARE done BOOL DEFAULT FALSE;
DECLARE cur1 CURSOR FOR SELECT symbol, PriceDate
from StockData
where PriceDate > '2016-06-30'
order by symbol asc, PriceDate desc;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO v_symbol, v_pricedate;
If done THEN
LEAVE read_loop;
END IF;
Update StockData SET
20DayTPAvg = f_TP20avg(symbol,PriceDate),
20DayMFSum = f_20DayMFSum(symbol,PriceDate),
20DayVolumeSum = f_20DayVolumeSum(symbol,PriceDate),
MFFactor = f_20DayMFSum(symbol,PriceDate)/f_20DayVolumeSum(symbol,PriceDate),
50DayHighestHigh = f_50DayHighestHigh(symbol,PriceDate),
50DayLowestLow = f_50DayLowestLow(symbol,PriceDate),
50DayFactor = ((close-f_50DayLowestLow(symbol,PriceDate))/(f_50DayHighestHigh(symbol,PriceDate)-f_50DayLowestLow(symbol,PriceDate)))*100,
20DayAvgOf50DayFactor = f_20DayAvgOf50DayFactor(symbol,PriceDate),
50DayMAvg20DayBfr = f_20DayMAvg20DayBfr(symbol,PriceDate),
20DayAvgSqCh50DMA = f_20DayAvgSqCh50DMA(symbol,PriceDate),
20dStdDevOf50DayMovAvg = SQRT(f_20DayAvgSqCh50DMA(symbol,PriceDate)),
UpperBand = 50DayMovingAvg+2*SQRT(f_20DayAvgSqCh50DMA(symbol,PriceDate)),
LowerBand = 50DayMovingAvg-2*SQRT(f_20DayAvgSqCh50DMA(symbol,PriceDate)),
MidPointFactor = ((close-MidPoint)/(high-close))*100,
20DayAvgOfMPFactor = f_20DayAvgOfMPFactor(symbol,PriceDate)
Where symbol=v_symbol
And PriceDate=v_pricedate;
commit;
END LOOP;
CLOSE Cur1;
END $$
Delimiter ;
I had the same problem.
According to MariaDB doc, if any select statement returns no data, the it will consider as "Not Found" and according to handler Done becomes true, hence stop fetching cursor.
Seems you are using many functions. Do check if any of the function return empty result.
CURSORs are usually a mistake. Can't you simply do this one statement, no cursor?
UPDATE StockData
SET ...
WHERE PriceDate > '2016-06-30';
Is f_20DayAvgOf50DayFactor (etc) a UDF or a Stored Function?
Composite INDEX needed
INDEX(symbol, PriceDate)
im trying to update a table using this query:
DELIMITER $$
CREATE PROCEDURE updateDataSetHasChildren()
BEGIN
DECLARE data_set_id INT;
DECLARE done INT DEFAULT FALSE;
DECLARE result INT DEFAULT FALSE;
DECLARE data_set_cursor CURSOR FOR
SELECT id_data_set FROM data_set;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN data_set_cursor;
myloop: LOOP
-- Perform the first fetch.
FETCH data_set_cursor into data_set_id;
IF done THEN
LEAVE myloop;
END IF;
IF (SELECT COUNT(*) FROM data_sub_set WHERE id_data_set = data_set_id)>0 THEN
UPDATE data_set
SET has_children = TRUE
WHERE id_data_set = data_set_id;
ELSE
UPDATE data_set
SET has_children = FALSE
WHERE id_data_set = data_set_id;
END IF;
FETCH data_set_cursor into data_set_id;
END LOOP myloop;
CLOSE data_set_cursor;
END$$
DELIMITER ;
The problem is that when i run it it fills the column of the table with 1 0 1 0 1 0...and when i check the values are incorrect, what am i missing here?
Regards,
Remove second
FETCH data_set_cursor into data_set_id;
at the end of the myloop. Now it is working by steps:
Fetch id.
Process row.
Fetch id (loop end).
Fetch id (second loop iteration started).
Process row.
Fetch id.
Fetch id and so on...
Fetch is performed twice, and you are really processing every second row.
SET SQL_SAFE_UPDATES = 0;
use my_database;
DELIMITER $$
DROP PROCEDURE IF EXISTS Comit $$
CREATE PROCEDURE Comit ()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE ids INT;
DECLARE leftChilds INT;
DECLARE cur CURSOR FOR SELECT id FROM user;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
ins_loop: LOOP
FETCH cur INTO ids;
IF done THEN
LEAVE ins_loop;
END IF;
SET leftChilds = ( SELECT turnoverBalance FROM user WHERE proposer = ids AND side = 'left' LIMIT 1 );
INSERT INTO log(`log`) VALUES ( leftChilds );
END LOOP;
CLOSE cur;
END $$
When i call the procedure call Comit(); that return me this error :
1048 - Column 'log' cannot be null
Your subquery is generating NULL values, probably because there is no match on the proposer condition. Of course, your data could also have NULL values for turnoverBalance in user or rows with no 'left' side.
In any case, why are you using a cursor for something that is easily done as a single query? Something like this can replace all the logic:
INSERT INTO log(log)
SELECT turoverBalance
FROM user
WHERE proposer IN (SELECT id FROM user) AND side = 'left')
GROUP BY proposer;
First, its are stored procedure, not a trigger.
Check what your set leftChilds query returns, it should return some value, run it individually.
You can Check in stored procedure
If(leftChilds not NULL)
insert into log('log') values (leftChilds)
I am working on a MySQL procedure that creates a summary of one of my tables. The procedure retrieves a dataset and loops through it using a cursor. For each iteration of the loop, the procedure updates another table. The problem I'm having is the loop ends after a single iteration, event though I know that the query I'm running retrieves more than one row.
BEGIN
# Variable declaration section omitted for brevity
DECLARE cur CURSOR FOR SELECT
t.result_id,
t.athlete_id, t.`first`, t.middle, `last`, t.pref, t.birth,t.uss,
t.club_id,t.code,t.club_name,
t.meet_name,t.meet_id,t.`start`,t.`end`,
MIN(t.time) as time,t.age,t.type
FROM sometable t GROUP BY club_id ORDER BY time asc,t.start desc,club_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur;
read_loop: LOOP
FETCH cur INTO result_id,athlete_id, first_name, middle, last_name, pref, birth,uss,
club_id,club_code,club_name,
meet_name,meet_id,start_date,end_date,
result_time,age,type;
IF done=1 THEN
LEAVE read_loop;
END IF;
SET last_time = result_time;
INSERT INTO toptimes(`result_id`,`club_id`,`agegroup`,`sex`,`distance`,`course`,`stroke`,`data`,`published`)
VALUES(result_id,club_id,AgeGroupID,sex,distance,course,stroke,json,0);
END LOOP read_loop;
CLOSE cur;
END
I'm not clear what the problem is. When I run the select query manually, I get back several rows. Is there a problem running an insert statement inside the loop?
Your code chunk looks good to me.
How do you know that it's running only one iteration (i'm not seeing any
print or select statement for debug purpose)?
Are you getting any error while executing the stored procedure?
I tried to replicate the similar code with "sakila" database (mysql sample db). It's working perfectly. Please check this sql code sample, if it helps you.
DROP PROCEDURE IF EXISTS usp_select_dummy_data ;
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE usp_select_dummy_data()
BEGIN
-- Declare your variables
Declare _var_actor_id int default 0;
DECLARE _var_film_id int default 0;
-- Declare variable used for cursor and loop control
DECLARE done int;
DECLARE loop_counter INT DEFAULT 0;
-- Declare the cursor
DECLARE cur CURSOR FOR
SELECT
actor_id, film_id
FROM film_actor;
-- Declare handlers
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
-- Open the cursor
OPEN cur ;
-- Start the loop
read_loop: LOOP
FETCH cur
INTO _var_actor_id, _var_film_id ;
-- break out of the loop if
-- 1. if there is no rows or
-- 2. we've processed them all
IF done = 1 THEN
CLOSE cur ;
LEAVE read_loop ;
END IF;
-- Count the number of times looped
SET loop_counter = loop_counter + 1 ;
END LOOP read_loop ;
-- print the loop count
select loop_counter;
END
I expect the following stored routine to return a series of rows, while it only returns 1:
CREATE PROCEDURE example()
BEGIN
DECLARE current_id INT;
DECLARE done INT DEFAULT 0;
DECLARE cur_main CURSOR FOR SELECT id from tasks;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
OPEN cur_main;
FETCH cur_main into current_id;
lp:WHILE not done DO
FETCH cur_main into current_id;
IF done = 1 THEN
LEAVE lp;
END IF;
SELECT * from tasks WHERE id = current_id;
END WHILE lp;
CLOSE cur_main;
END
Any help? This is my very first time with MySQL stored routines.
Unfortunately, MySQL does not return multiple rows this way.
This procedure:
CREATE PROCEDURE example()
BEGIN
SELECT 1;
SELECT 2;
END;
will return multiple resultsets, not rows.
Could you please describe what task do you want to achieve with this stored procedure?
You might want to try this statement rather than your existing test.
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
The default action for a continue handler is EXIT for an unmatched SQLSTATE value. The "NOT FOUND" constant covers all valid 020 conditions.
Here is another possibility. In looking again at your code I noticed that you do a FETCH before your while loop which will give you the first record. You enter the WHILE loop and then do another FETCH. Typically you would enter the while loop, do your processing with the current record, then do another FETCH right before cycling the loop. In addition, by moving the next FETCH to the end of the loop body you can remove the IF test as well.
FETCH cur_main into current_id;
lp:WHILE not done DO
SELECT * from tasks WHERE id = current_id;
FETCH cur_main into current_id;
END WHILE lp;
Thanks for you replies.
I managed to do what I had do with the help of a temporary table where i INSERT all the results and then SELECTing * FROM that table.