I am running a stored procedure on my MySQL database and it says it runs fine, no errors come back, but when I check the table it's supposed to have updated nothing has changed.
When I manually run each individual part of the procedure it works fine, I get back what I expect. All the selects run fine.
I'm fairly new in dealing with stored procedures so I'm not sure how to debug this. Can I get it to output the different stages it's at so I can make sure it gets to the update query? Can I check the results of the queries it's running?
I googled the issue but I didn't find anything helpful. The manual only had information on how to set the procedure up, not how to debug it when it wasn't working (unless I missed something).
This is my whole procedure:
DELIMITER //
CREATE PROCEDURE QuestionStatistics(IN quizId INT(11))
BEGIN
DECLARE bDone INT;
DECLARE qqId INT;
DECLARE totalAnswers INT;
DECLARE totalCorrect INT;
DECLARE totalValue INT;
DECLARE curs CURSOR FOR SELECT qqId FROM quizQuestions WHERE qqQuizId = quizId;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET bDone = 1;
OPEN curs;
SET bDone = 0;
REPEAT
FETCH curs INTO qqId;
SELECT COUNT(*)
FROM quizAnswers
WHERE qaQuizQuestionId = qqId
AND qaIsMarked = 1
INTO totalAnswers;
SELECT COUNT(*)
FROM quizAnswers
WHERE qaQuizQuestionId = qqId
AND qaIsCorrect = 1
INTO totalCorrect;
SELECT SUM(qaValue)
FROM quizAnswers
WHERE qaQuizQuestionId = qqId
AND qaIsMarked = 1
INTO totalValue;
UPDATE quizQuestions
SET qqAveragePoints = ROUND(totalValue / totalAnswers, 2),
qqPercentageCorrect = ROUND(100 * totalCorrect / totalAnswers)
WHERE qqId = qqId;
UNTIL bDone END REPEAT;
CLOSE curs;
END//
DELIMITER ;
I'd appreciate it if anyone could point me in the right direction on how to debug this, or if they could spot the issue.
You can debug your procedure using debugger feature in dbForge Studio for MySQL (try trial version).
I suggest you to optimize your queries, e.g. use one SELECT query instead of three ones -
SELECT
COUNT(IF(qaIsMarked = 1, 1, NULL)),
COUNT(IF(qaIsCorrect = 1 , 1, NULL)),
SUM(IF(qaIsMarked = 1, qaValue, 0)
INTO totalAnswers, totalCorrect, totalValue
FROM
quizAnswers
WHERE
qaQuizQuestionId = qqId;
To avoid errors - do not use the same names for parameters, variables and object names like columns.
Also, try to avoid using cursors at all, write one complex UPDATE query.
Related
I'm trying to implement a cursor in MYSQL. Inside that cursor, I have a select statement that might or might not return any rows.
I'm facing a problem when that query does not return any rows.
According to the MYSQL documentation,
NOT FOUND: Shorthand for the class of SQLSTATE values that begin with '02'. This is relevant within the context of cursors and is used to control what happens when a cursor reaches the end of a data set. If no more rows are available, a No Data condition occurs with SQLSTATE value '02000'. You can set up a handler for it or a NOT-FOUND condition to detect this condition. For another example, see Section 13.6.6, “Cursors”. The NOT FOUND condition also occurs for SELECT ... INTO var_list statements that retrieve no rows.
My termination of the cursor is implemented as usual.
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET FINISHED = 1;
The problem is that the termination is triggered because of the query inside the cursor returning 0 rows. Is there a way to differentiate between those two cases so that the cursor continues despite my query returning 0 results?
Thanks in advance.
DROP PROCEDURE IF EXISTS CURSOR_PLACEHOLDER;
DELIMITER $$
CREATE PROCEDURE CURSOR_PLACEHOLDER()
BEGIN
DECLARE FINISHED INTEGER DEFAULT 0;
DECLARE CURRENT_ROW_ID VARCHAR(256);
DEClARE CURRENT_ROW
CURSOR FOR
SELECT ID
FROM A
WHERE ID_P IN (SELECT ID_P
FROM A
GROUP BY ID_P
HAVING COUNT(ID_P) > 1);
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET FINISHED = 1;
OPEN CURRENT_ROW;
GET_ACTION:
LOOP
FETCH CURRENT_ROW INTO CURRENT_ROW_ID;
IF FINISHED = 1 THEN
LEAVE GET_ACTION;
END IF;
SET #CURRENT_P_ID := (SELECT ID_P FROM A WHERE ID = CURRENT_ROW_ID);
SELECT a.ID_U, a.ID_R, a.A, a.FROMDATE, a.TODATE
INTO #ID_U, #ID_R, #A, #FROM_DATE, #TO_DATE
FROM ASSIG a
WHERE a.ID_G = #CURRENT_P_ID; # <- Query that returns 0 rows and terminates the cursor
IF (#ID_U IS NOT NULL) THEN
SET #ASSIG_ID := GENERATE_ID();
INSERT INTO ASSIG
VALUES (#ASSIG_ID,
#G_ID,
#ID_U,
#ID_R,
#A,
#FROM_DATE,
#TO_DATE);
END IF;
UPDATE A
SET ID_P = #G_ID
WHERE ID = CURRENT_ROW_ID;
END LOOP GET_ACTION;
CLOSE CURRENT_ROW;
END
$$
DELIMITER ;
can You help.
This code should update quantity of product in packages to by equal to minimum stock of product that are forming the package. My stocks are updated based on data form Oracle ERP every 5 minutes, but ERP does not know abort packages - they exists only in Prestashop, and they have to be updated independently in cycles (job). I try to do it by procedure.
CREATE OR REPLACE PROCEDURE B2C_P_QUANTYTY_UPDATE
BEGIN
FOR i IN
(SELECT ps_pack.id_product_pack, min(ps_stock_available.quantity) min_quantity
FROM ps_pack, ps_stock_available
WHERE ps_pack.id_product_item = ps_stock_available.id_product
GROUP BY ps_pack.id_product_pack)
LOOP
UPDATE ps_stock_available
SET ps_stock_available.quantity = i.min_quantity
WHERE ps_stock_available.id_product = i.id_product_pack ;
END LOOP ;
END;
2 errors has been found in analysis.
Unrecognized data type. (near "ps_pack" at position 81)
Unrecognized data type. (near "(" at position 109)
MySQL returned:
#1064 - Something is wrong in your syntax near 'BEGIN
FOR i IN
(SELECT ps_pack.id_product_pack, min(ps_stock_available.qua' in line 2
I don't understand why, the select query works fine. But wrapped inside procedure stops recognizing data types.
Thanks to #Barranka answer to post SQL - Looping through ever row of table in mysql? i was able to do it.
And the code looks like that:
DELIMITER $$
CREATE OR REPLACE PROCEDURE B2C_P_QUANTYTY_UPDATE ()
BEGIN
DECLARE c_product int;
DECLARE c_min_quantity int;
DECLARE done int default false;
DECLARE quantity_cursor cursor FOR SELECT ps_pack.id_product_pack AS product , MIN(ps_stock_available.quantity) min_quantity
FROM ps_pack, ps_stock_available
WHERE ps_pack.id_product_item = ps_stock_available.id_product
GROUP BY ps_pack.id_product_pack;
DECLARE continue handler FOR not found
SET done = true;
OPEN quantity_cursor;
quantity_loop: LOOP
FETCH quantity_cursor INTO c_product, c_min_quantity;
IF done THEN
leave quantity_loop;
END IF;
UPDATE ps_stock_available
SET ps_stock_available.quantity = c_min_quantity
WHERE ps_stock_available.id_product = c_product;
END loop ;
CLOSE quantity_cursor;
COMMIT;
END$$
DELIMITER ;
I have a medium sized stored procedure going on here below. My problem is that it doesn't do anything and I have no idea why.
1.) First of all, the code:
DROP PROCEDURE IF EXISTS deleteabundant_fixshared_shiftResources;
DELIMITER //
CREATE PROCEDURE deleteabundant_fixshared_shiftResources ()
BEGIN
DECLARE finish_flag BOOLEAN DEFAULT FALSE;
DECLARE id INT(11);
DECLARE startTime DATETIME;
DECLARE endTime DATETIME;
DECLARE shid INT(11);
DECLARE resid INT(11);
DECLARE id_inner INT(11);
DECLARE startTime_inner DATETIME;
DECLARE endTime_inner DATETIME;
DECLARE shid_inner INT(11);
DECLARE resid_inner INT(11);
DECLARE cr130 CURSOR FOR SELECT shift_resource_id, start_date, end_date, shift_id, resource_id FROM temp_shift_resource;
DECLARE cr131 CURSOR FOR SELECT shift_resource_id, start_date, end_date, shift_id, resource_id FROM temp_shift_resource;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finish_flag = TRUE;
START TRANSACTION;
OPEN cr130;
OPEN cr131;
OUTERLOOP: LOOP
FETCH cr130 into id, startTime, endTime, shid, resid;
IF finish_flag THEN LEAVE OUTERLOOP; END IF;
INNERLOOP: LOOP
FETCH cr131 INTO id_inner, startTime_inner, endTime_inner, shid_inner, resid_inner;
IF finish_flag THEN LEAVE INNERLOOP; END IF;
IF (id!=id_inner) THEN
IF (resid=resid_inner AND shid_inner!=9) THEN
-- logic to determine if the dates are wrong:
IF (startTime<=startTime_inner AND endTime>=endTime_inner) THEN
INSERT INTO repairchange ( shift_resource_id, changetype, shift_id, resource_id, start_date, end_date )
VALUES ( id_inner, "FD", shid_inner, resid_inner, startTime_inner, endTime_inner );
DELETE FROM temp_shift_resource WHERE shift_resource_id = id_inner;
ELSEIF (endTime>=endTime_inner AND startTime<=endTime_inner) THEN
INSERT INTO repairchange ( shift_resource_id, changetype, shift_id, resource_id, start_date, end_date )
VALUES ( id_inner, "FU", shid_inner, resid_inner, startTime_inner, endTime_inner );
UPDATE temp_shift_resource set endTime_inner=(startTime - INTERVAL 1 DAY) where shift_resource_id = id_inner;
ELSEIF (startTime<=startTime_inner AND endTime>=startTime_inner) THEN
INSERT INTO repairchange ( shift_resource_id, changetype, shift_id, resource_id, start_date, end_date )
VALUES ( id_inner, "FU", shid_inner, resid_inner, startTime_inner, endTime_inner );
UPDATE temp_shift_resource set startTime_inner=(endTime + INTERVAL 1 DAY) where shift_resource_id = id_inner;
END IF;
END IF;
END IF;
END LOOP INNERLOOP;
SET finish_flag = FALSE;
END LOOP OUTERLOOP;
CLOSE cr130;
CLOSE cr131;
COMMIT;
END //
DELIMITER ;
call deleteabundant_fixshared_shiftResources();
2.) Description of what I want to do:
Basically, I have a table full of workshifts. Due to code bugs, some of these shifts have a wrong date assigned to them, and I have to fix the database.
I have to run through the whole table, and compare the rows that are assigned to the same resource_id, which represents a person. So if a person has two shifts that look like (2016-05-10 to 2016-05-20) and (2016-05-15 to 2016-05-23) for example, I have to fix it so that one of them will be trimmed to (2016-05-10 to 2016-05-14) and (2016-05-15 to 2016-05-23).
A shift that is a nightshift, marked as shift_id=9, must not be modified at all.
I insert rows into the repairchange table if a change or a deletion has been made
3.) The procedure runs, but does nothing. I have examples in the database for wrong rows, one example is the one I wrote above. I suspect it is the nested loop, because I want to loop and fetch through the same table, but I haven't found anything on that.
I got the message
0 row(s) affected, 1 warning(s): 1329 No data - zero rows fetched, selected, or processed
but I have seen this before and my stored procedures have worked even though they output this warning.
Any ideas or tips are welcome. Thank you for your time!
I figured it out, after quite some debugging:
I opened the cursors before both of the loops. This meant that after the first walk-through of the inner loop, the cursor was standing at +1 of the LAST row of the table, and when the new outer loop iteration started the second inner loop iteration, the cursor was still at the end position.
Thus it did not run. I replaced the inner-cursor opening and closing into the outer loop, and now it works properly.
I am writing a MySQL Stored Procedure for the first time, and I am running into an issue - I think with the Handler Code. Basically, I want this code to update all rows in the pps_users table, but for some reason I am hitting the 'finished condition' for the handler after only two rows are fetched.
I tried the same thing with the REPEAT syntax and got the same result. If I just run the cursor query I correctly get the 10,000 records I expect, but when I run the whole thing as is, I hit the finished code after only 1 or 2 records.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `changeNFLFavTeams`()
BEGIN
DECLARE favNFLTeam varchar(100) DEFAULT "";
DECLARE favNCAATeam varchar(100) DEFAULT "";
DECLARE v_finished INTEGER DEFAULT 0;
DECLARE user_id bigint(20);
DECLARE fullNameOfTeam varchar(100) DEFAULT "";
DECLARE update_favs CURSOR FOR select id, favorite_nfl_team from pps_users WHERE favorite_nfl_team is not null;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished = 1;
OPEN update_favs;
updaterecord: LOOP
FETCH update_favs INTO user_id, favNFLTeam;
select user_id, favNFLTeam as "Test";
if v_finished = 1
then
select "finished" as "finished";
LEAVE updaterecord;
end if;
select full_name into fullNameOfTeam
from teams t
inner join display_names dt on dt.entity_id = t.id
and dt.entity_type = 'teams'
and dt.first_name = favNFLTeam
and team_key like 'l.nfl.com%' LIMIT 1;
select user_id, fullNameOfTeam AS "BeforeUpdate";
IF fullNameOfTeam != ''
THEN
-- here for whatever_transformation_may_be_desired
-- Find the Full name for the record they chose
UPDATE pps_users p
SET favorite_nfl_team = fullNameOfTeam
WHERE user_id = p.id;
ELSE
SELECT 'A' AS 'A'; -- no op
END IF;
end loop updaterecord;
CLOSE update_favs;
END
This is because if your SELECT full_name into fullNameOfTeam... query returns no rows, then it will set v_finished to 1. That, apparently, happens early on, and forces an exit from the main loop.
The key is to realize that the CONTINUE HANDLER for NOT FOUND does not apply to the cursor alone.
You should either put the secondary query into its own BEGIN..END block with its own CONTINUE handler, or (easier) just set v_finished = 0 after the SELECT full_name into fullNameOfTeam... statement.
I am in the process of converting a SQL Server 2005 database to MySQL and having problems with a Stored procedure. I'm new to MySQL stored procedures so I'm sure it is a problem with my conversion but I'm not seeing it.
The stored procedure is supposed to generate a temporary table which is used to populate a Data Grid View in a vb.net application. However, I'm getting the error "Data No Data - Zero rows fetched, selected or processed.". Seems simple enough but the select procedure in the stored procedure will get data if I just run it as a query which is why I don't understand why the error.
I'm really hoping someone can tell me why because I have several hundred stored procedures to convert and I'm having this problem on the very first one.
Here's the Stored Procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS `usp_get_unassigned_media`$$
CREATE DEFINER=`showxx`#`67.111.11.110` PROCEDURE `usp_get_unassigned_media`()
BEGIN
/* GET CURSOR WITH LOCAL LOCATIONS */
DECLARE intKey INT;
DECLARE dteDateInserted DATETIME;
DECLARE vchIdField VARCHAR(200);
DECLARE vchValueField VARCHAR(200);
DECLARE intLastKey INT;
/*TAKE OUT SPECIFIC PLAYLIST ITEMS IF TOO SLOW*/
DECLARE csrMediaToBeAssigned CURSOR FOR
SELECT
`media`.`key` AS `key`,
`media`.`date_inserted` AS `date_inserted`,
`media_detail_types`.`id` AS `id`,
`media_details`.`value` AS `value`
FROM (`media`
LEFT JOIN (`media_detail_types`
JOIN `media_details`
ON ((`media_detail_types`.`key` = `media_details`.`detail_key`)))
ON ((`media_details`.`media_key` = `media`.`key`)))
WHERE ((`media`.`is_assigned` = 0)
AND ((`media_detail_types`.`id` = 'Volume Name')
OR (`media_detail_types`.`id` = 'Drive Id')))
ORDER BY `media`.`key`,`media`.`date_inserted`,`media_detail_types`.`id`;
OPEN csrMediaToBeAssigned;
DROP TEMPORARY TABLE IF EXISTS temp_unassigned_media;
CREATE TEMPORARY TABLE temp_unnassigned_media
(temp_key INT, DateInserted DATETIME, IdField VARCHAR(200), ValueField VARCHAR (200))
ENGINE=MEMORY;
SET intLastKey = 0;
/*--GET FIRST RECORD */
FETCH FROM csrMediaToBeAssigned
INTO intKey, dteDateInserted, vchIdField, vchValueField;
/*--LOOP THROUGH CURSOR */
WHILE intLastKey = 0 DO
/*--DATA SHOULD BE IN DRIVE ID THEN VOLUME NAME */
INSERT INTO temp_unnassigned_media
VALUES (intKey, dteDateInserted, vchValueField, '');
FETCH NEXT FROM csrMediaToBeAssigned
INTO intKey, dteDateInserted, vchIdField, vchValueField;
UPDATE temp_unnassigned_media
SET IdField = vchValueField
WHERE temp_key = temp_key;
FETCH NEXT FROM csrMediaToBeAssigned
INTO intKey, dteDateInserted, vchIdField, vchValueField;
END WHILE;
SELECT *
FROM temp_unnassigned_media
ORDER BY date_inserted;
CLOSE csrMediaToBeAssigned;
/*DEALLOCATE csrMediaToBeAssigned */
/*DROP TABLE #temp_unnassigned_media */
END$$
DELIMITER ;
You never hit a condition where that WHILE loop will exit; you initialize intLastKey variable, but it never changes, so you fetch through the entire resultset. The exception is thrown when you fetch again, after the last record.
The normative pattern is to declare a CONTINUE HANDLER, which MySQL will execute when the NOT FOUND condition is triggered. The handler is normally used to set a variable, which you can then test, so you know when to exit the loop.
In your case, it looks like just adding this line, after your DECLARE CURSOR statement and before the OPEN statement, would be sufficient:
DECLARE CONTINUE HANDLER FOR NOT FOUND SET intLastKey = 1;