Problem with MySQL nested cursors - mysql

I have the following bunch of code trying to update a field using 2 cursors the one inside the other. I use the first cursor just to get an id value (1st cursor) and then I get a list of other id values based on that id (2nd cursor). The problem is that the result set from the 2nd cursor contains the last id twice! I can't find the bug! Something in the loop, something in the termination variable for the 2 cursors?
Any help more than welcome! Thanks in advance!
DELIMITER $$
DROP PROCEDURE IF EXISTS updateNumberOfPoisForPlacesWithChildren$$
CREATE PROCEDURE updateNumberOfPoisForPlacesWithChildren()
BEGIN
DECLARE single_place_id INT(11);
DECLARE single_unique_parents_id INT(11);
DECLARE done, temp_number_of_pois, sum_number_of_pois INT DEFAULT 0;
DECLARE unique_parents_ids CURSOR FOR SELECT DISTINCT `parent_id` FROM `places` WHERE `parent_id` IS NOT NULL;
DECLARE temp_places_ids CURSOR FOR SELECT `id` FROM `places` WHERE `parent_id` = single_unique_parents_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN unique_parents_ids;
main_loop_for_parent_ids : LOOP
SET sum_number_of_pois = 0;
IF done = 1 THEN
CLOSE unique_parents_ids;
LEAVE main_loop_for_parent_ids;
END IF;
IF NOT done = 1 THEN
FETCH unique_parents_ids INTO single_unique_parents_id;
OPEN temp_places_ids;
main_loop_for_places : LOOP
IF done = 1 THEN
CLOSE temp_places_ids;
LEAVE main_loop_for_places;
END IF;
IF NOT done = 1 THEN
FETCH temp_places_ids INTO single_place_id;
SELECT COUNT(`id`) INTO temp_number_of_pois FROM `pois` WHERE `place_id` = single_place_id;
SET sum_number_of_pois = sum_number_of_pois + temp_number_of_pois;
END IF;
END LOOP;
UPDATE `places` SET `number_of_pois`=sum_number_of_pois WHERE `id` = single_unique_parents_id;
END IF;
END LOOP;
END$$
DELIMITER ;

I think your problem is about here...
IF NOT done = 1 THEN
FETCH temp_places_ids INTO single_place_id;
SELECT COUNT(`id`) INTO temp_number_of_pois FROM `pois` WHERE `place_id` = single_place_id;
SET sum_number_of_pois = sum_number_of_pois + temp_number_of_pois;
END IF;
The FETCH sets DONE to be 1 when it exhausts the result set, but you proceed because you've already passed the DONE test.
Perhaps...
FETCH temp_places_ids INTO single_place_id;
IF NOT done = 1 THEN
SELECT COUNT(`id`) INTO temp_number_of_pois FROM `pois` WHERE `place_id` = single_place_id;
SET sum_number_of_pois = sum_number_of_pois + temp_number_of_pois;
END IF;

Related

MySqlException: Subquery returns more than 1 row, for a stored procedure with a cursor

Hello I have the following stored procedure for MySQL but when it is executed in my ASP.NET Core application I get a Subquery returns more than 1 row error. What am I doing wrong here? The equivalent SQL Server version used to work without problems...
-- System Calculates Candidate’s Matching Score based on a Manager’s Answer Weights
CREATE PROCEDURE spSysCalcCandScore
(
IN Candidate_ID INT,
IN Manager_ID INT
)
Begin
DECLARE ansID INT;
DECLARE tempSum INT;
DECLARE Sum INT;
DECLARE Done INT DEFAULT FALSE;
DECLARE MyCursor CURSOR FOR
SELECT Answer_ID FROM Completed_Questionnaire
WHERE Candidate_ID = Candidate_ID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET Done = TRUE;
START TRANSACTION;
OPEN MyCursor;
myloop: LOOP
FETCH MyCursor INTO ansID;
IF Done THEN
LEAVE myloop;
END IF;
SET tempSum = (SELECT Weight_Value FROM Weight WHERE (Answer_ID = ansID AND Manager_ID = Manager_ID));
SET Sum = Sum + tempSum;
END LOOP;
CLOSE MyCursor;
IF (Sum IS NULL) THEN
SET Sum = 0;
END IF;
UPDATE `Interest`
SET Matching_Score = Sum
WHERE (Candidate_ID = Candidate_ID AND Manager_ID = Manager_ID);
COMMIT;
End//

Cursor with JOIN and WHERE MySQL

I am trying to create a repetition in 'cursor' that uses 'join', but it doesn't work.
Already tested without 'join' and works normally.
My code:
DECLARE hvDataAtual DATE;
DECLARE manDataAtual DATETIME;
DECLARE motCodAtual INT;
DECLARE done INT DEFAULT FALSE;
DECLARE Crs_vei_man
CURSOR FOR (
SELECT hvedatageracao, hvedatageracao, hvemotorista
FROM tableone INNER JOIN tabletwo ON hveveiculo = manveiculo
WHERE SUBSTRING(mandata, 1, 10) = SUBSTRING(hvedatageracao, 1, 10) LIMIT 10);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN Crs_vei_man;
myloop: LOOP
FETCH Crs_vei_man
INTO hvDataAtual,
manDataAtual,
motCodAtual;
-- Insert 1 row (null, null, null) ?
INSERT INTO testes VALUES(hvDataAtual, manDataAtual, motCodAtual);
IF done THEN
LEAVE myloop;
END IF;
IF motCodAtual > 0 THEN
INSERT INTO testes VALUES('hvDataAtual', 'manDataAtual', 'motCodAtual');
END IF;
END LOOP;
CLOSE Crs_vei_man;
This code is in a stored procedure.
What is happening?

How to use cursors to update some columns according to another column?

I have a table called USER. This table has 2 columns as end_date, access_date. access_date is empty right now but i want to populate it like:
if end_date exists : access_date = end_date + 1 year (anway i can make that operation) but my problem i could not construct the cursor i have not use cursor logic before.
i need something like:
DELIMITER $$
CREATE PROCEDURE user_procedure ()
BEGIN
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE v_user varchar(100) DEFAULT "";
-- declare cursor for user
DEClARE user_cursor CURSOR FOR
SELECT * FROM USER;
-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
OPEN user_cursor;
get_user: LOOP
FETCH user_cursor INTO v_user;
IF v_finished = 1 THEN
LEAVE get_user;
END IF;
-- operation
-- something like:
set #end_date = select from cursor
update expiry... etcs
END LOOP get_user;
CLOSE user_cursor;
END$$
DELIMITER ;
CALL user_procedure();
but the problem i do not know how to define the cursor because as you see in example:
DECLARE v_user varchar(100) DEFAULT "";
i am pretty sure it is wrong and i try to fetch it into
FETCH user_cursor INTO v_user;
So how can i properly define the cursor and fetch as a whole row and make change ?
Edit: some people did not understand and claimed i have asked same question again, ok i will edit the real code, now as below according to people's comment. This update must be applied to each individual row.
set #key = 'bla bla';
delimiter $$
create procedure select_or_insert()
begin
IF EXISTS (select USER_EXPIRY_DATE from USER) THEN
update USER set ACCESS_EXPIRY_DATE = DATE_ADD(USER_EXPIRY_DATE, INTERVAL 1 YEAR);
ELSE IF EXISTS (select USER_START_DATE from USER) THEN
SET #start_date = (select USER_START_DATE from USER);
SET #start_date_to_be_added = aes_decrypt(#start_date,#key)
update USER set ACCESS_EXPIRY_DATE = DATE_ADD(USER_EXPIRY_DATE, INTERVAL 1 YEAR);
END IF;
end $$
delimiter ;
but in here for example:
ELSE IF EXISTS (select USER_START_DATE from USER)
is returning more than 1 row.
You don't need a cursor for that, you can simply use an update statement:
UPDATE User
SET access_date = DATE_ADD(end_date, INTERVAL 1 YEAR)
WHERE end_date IS NOT NULL

update statement in MySQL SP showing missing end error

I have a stored procedure like the following. There is an error showing at the update statement before the cursor declaration. When I remove the cursor statement, the stored procedure works fine. Why can't be there an update statement before the cursor statement?
DROP PROCEDURE IF EXISTS GenerateAlert;
DELIMITER $$
CREATE PROCEDURE GenerateAlert()
BEGIN
DECLARE done INTEGER DEFAULT FALSE;
DECLARE temp_project_id INT DEFAULT 0;
DECLARE temp_finish_time DATETIME DEFAULT NOW();
DECLARE _message_class INT DEFAULT 3;
DECLARE proj_user_id INT DEFAULT 0;
-- get message text template
DECLARE message_template VARCHAR(255) DEFAULT
(
SELECT alert_message_template FROM alerts WHERE id = 6
);
-- get maximum allowed minutes
DECLARE allowed_time INT DEFAULT
(
SELECT alert_value_1 FROM alerts WHERE id = 4
);
-- flag all messages with class 3 to deleted = true
UPDATE messages
SET is_deleted = 1
WHERE messages_class = 3;
DECLARE _cur CURSOR FOR
SELECT d.project_id, MAX(finish_time)
FROM
client_docs AS d INNER JOIN
issues AS i ON d.project_id = i.project_id INNER JOIN
working_times AS t ON i.id = t.issue_id
WHERE
d.status = 1 AND
t.finish_time IS NOT NULL AND
t.category = 1
GROUP BY d.project_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN _cur;
read_loop: LOOP
FETCH _cur INTO temp_project_id, temp_finish_time;
IF done THEN
LEAVE read_loop;
END IF;
-- add more minutes to finish time
SET temp_finish_time = DATE_ADD(temp_finish_time, INTERVAL allowed_time MINUTE);
IF temp_finish_time < NOW() THEN
-- this project must be alerted
-- get project user
SELECT c.staff INTO proj_user_id
FROM
projects AS p INNER JOIN
clients AS c ON p.client_id = c.id
WHERE
p.id = temp_project_id;
END IF;
END LOOP read_loop;
CLOSE _cur;
END
$$
DELIMITER ;
Found the answer: The declaration statments must come before any other transactional statement in MySQL stored procedure.
SQL, missing end, but why? Second comment in the answer.

Dynamic Views in Mysql Stored Procedure does not update

I have a table containing 100,000+ records from which i have some calculations to do based on a value which is stored in a specific column.
For this i have written a stored procedure which takes 100 rows at a time from the base table. Since cursors don't support dynamic sql. What i did was i create a dynamic view and the cursor always selects data from that view.
The code :
BEGIN
-- VARIABLES TO RETURN
DECLARE ORIG_COUNT INT ;
DECLARE SERVED_COUNT INT;
-- VARIABLED FOR LOOPING
DECLARE no_more_rows BOOLEAN;
DECLARE no_more_rows_sub BOOLEAN;
DECLARE num_rows INT DEFAULT 0;
SET #ORIG_COUNT =0;
SET #SERVED_COUNT=0;
SELECT COUNT(*) FROM table_one AS A WHERE A.year='2011' INTO #ORIG_COUNT ;
DELETE FROM table_test;
insert into table_test values ('Total Rows',#ORIG_COUNT,NULL);
DROP TABLE IF EXISTS table_request;
CREATE TABLE table_request (
BN_NUM VARCHAR(25) NOT NULL,
DN_NUM varchar(10) NOT NULL,
A_TOTAL FLOAT DEFAULT 0,
B_TOTAL FLOAT DEFAULT 0,
C_TOTAL FLOAT DEFAULT 0,
PRIMARY KEY (BN)
);
WHILE (#SERVED_COUNT<#ORIG_COUNT) DO
DROP VIEW IF EXISTS pbs_history.temp_view;
SET #query = CONCAT('CREATE VIEW temp_view as SELECT A.BN,A.DN
FROM table_one AS A
WHERE A.year='2011'
ORDER BY A.ID
LIMIT ', #SERVED_COUNT,',100');
PREPARE stmt from #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
/* ================= HERE ==================== */
SELECT * FROM temp_view;
BLOKA:BEGIN
DECLARE BN VARCHAR(25);
DECLARE DN VARCHAR(10);
DECLARE num_rows INT(5) DEFAULT 0;
DECLARE bn_count INT(5) DEFAULT 0;
DECLARE sel_recs CURSOR FOR SELECT * FROM temp_view;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
insert into table_test values('Start Served',#SERVED_COUNT,#query);
OPEN sel_recs;
SELECT FOUND_ROWS() INTO #num_rows;
insert into table_test values('Found Rows in View',#num_rows,NULL);
the_loop: LOOP
FETCH sel_recs INTO BN,DN;
IF no_more_rows THEN
CLOSE sel_recs;
LEAVE the_loop;
END IF;
SET #bn_count=0;
SELECT COUNT(*) FROM table_request WHERE BILL_NUMBER=BN INTO #bn_count;
insert into table_test VALUES(BN,DN,#bn_count);
IF(#bn_count=0) THEN
INSERT INTO table_request VALUES (BN,DN,0,0,0);
END IF;
BLOKB:BEGIN
DECLARE CAT VARCHAR(5);
DECLARE T1 FLOAT;
DECLARE data_fetch CURSOR FOR SELECT CAT_ID, TYPE_1 FROM transactions WHERE ID=BN;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows_sub = TRUE;
OPEN data_fetch;
sub_loop: LOOP
FETCH data_fetch INTO CAT,T1;
IF no_more_rows_sub THEN
CLOSE data_fetch;
LEAVE sub_loop;
END IF;
IF (CAT='P1') THEN
UPDATE table_request SET A_TOTAL = A_TOTAL+T1 WHERE BN_NUM=BN;
ELSEIF (CAT='P2') THEN
UPDATE table_request SET B_TOTAL = B_TOTAL+T1 WHERE BN_NUM=BN;
ELSEIF (CAT='P3') THEN
UPDATE table_request SET N_TOTAL = N_TOTAL+T1 WHERE BN_NUM=BN;
END IF;
END LOOP sub_loop;
END BLOKB;
SET #SERVED_COUNT = #SERVED_COUNT+1;
END LOOP the_loop;
insert into table_test values ('End the_loop',0,NULL);
END BLOKA;
END WHILE;
END
I'm testing the code with a subset from the original table. And the subset has 461 records.
And with this code there should be 5 iterations. And in the final iteration the view should contain only 61 records. But with this code each iteration has 100 records, and during each iteration it takes into consideration only the first 100 records.
But if i run SELECT * FROM temp_view in mysql console after the procedure has run, it shows the last 61 records.
Anybody can point out what i'm doing wrong here?