I have written a procedure in which one statement is not executing properly:
我已编写了一个程序,其中有一个语句没有正确执行:
SELECT thumb_image into v_thumb_image FROM RESTAURANT_IMAGE WHERE
RESTAURANT_ID = v_restaurant_id
The reason, I investigated is if at any point of time resultset is empty, procedure doesn't run statements further.
我调查的原因是,如果在任何时间点resultset为空,那么过程不会进一步运行语句。
Please note that I am calling this within a LOOP.
请注意,我在一个循环中调用这个。
My concern is not to stop execution if for any v_restaurant_id
, resultset is empty.
我关心的是,如果对于任何v_restaurant_id, resultset为空,则不要停止执行。
FULL PROCEDURE:
完整的程序:
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `populate_restaurant_details`()
BEGIN
DECLARE v_finished_cuisines,
v_finished,
v_restaurant_id,
v_count_discount
INT DEFAULT 0;
DECLARE v_cuisines,
v_thumb_image
varchar(200) DEFAULT "";
DECLARE cuisine_title varchar(50) DEFAULT "";
-- Fetch all restaurant id
DECLARE restaurant_cursor CURSOR FOR
SELECT id FROM delhifoodonline.restaurant order by id desc;
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished = 1;
OPEN restaurant_cursor;
get_restaurant: LOOP
FETCH restaurant_cursor INTO v_restaurant_id;
IF v_finished = 1 THEN
LEAVE get_restaurant;
END IF;
SET v_finished_cuisines ="";
SET v_thumb_image = "";
begin
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_thumb_image = NULL;
SELECT thumb_image into v_thumb_image
FROM restaurant_image
WHERE restaurant_id = v_restaurant_id
ORDER BY id
LIMIT 1;
end;
SELECT count(*) into v_count_discount FROM restaurant_discount WHERE
restaurant_id = v_restaurant_id;
BLOCK2: BEGIN
DECLARE cuisines_cursor CURSOR FOR
SELECT cuisine.title FROM restaurant_cuisine INNER JOIN cuisine
ON restaurant_cuisine.cuisine_id = cuisine.id
WHERE
restaurant_cuisine.restaurant_id = v_restaurant_id
LIMIT 0,5;
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET v_finished_cuisines = 1;
SET v_cuisines = "";
OPEN cuisines_cursor;
get_cuisine: LOOP
FETCH cuisines_cursor INTO cuisine_title;
IF v_finished_cuisines = 1 THEN
LEAVE get_cuisine;
END IF;
SET v_cuisines = CONCAT(cuisine_title,", ",v_cuisines);
END LOOP get_cuisine;
CLOSE cuisines_cursor;
END BLOCK2;
SET v_cuisines = TRIM(BOTH ", " FROM v_cuisines);
IF v_count_discount > 0 THEN
SET v_count_discount = 1;
ELSE
SET v_count_discount = 0;
END IF;
UPDATE restaurant SET
thumb_image = v_thumb_image,
cuisines_list = v_cuisines,
discount_available = v_count_discount
WHERE id= v_restaurant_id;
END LOOP get_restaurant;
CLOSE restaurant_cursor;
END
1 个解决方案
#1
4
From the documentation:
从文档:
NOT FOUND is 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'. To detect this condition, you can set up a handler for it (or for a NOT FOUND condition). For an example, see Section 13.6.6, “Cursors”. This condition also occurs for SELECT ... INTO var_list statements that retrieve no rows.
“NOT FOUND”是以“02”开头的SQLSTATE值类的简写。这在游标的上下文中是相关的,用来控制当游标到达数据集的末尾时会发生什么。如果没有更多的行可用,则不会出现SQLSTATE值'02000'的数据条件。要检测此条件,可以为其设置一个处理程序(或为未找到的条件)。例如,请参阅第13.6.6节“游标”。SELECT…也会出现这种情况。进入var_list语句,不检索行。
So your select from restaurant_image
table also meets the NOT FOUND
state when it returns no rows, and invokes the defined handler which causes leaving the loop.
因此,如果选择restaurant_image表不返回任何行,并且调用了导致退出循环的已定义处理程序,那么它也会满足未找到的状态。
One solution is to declare another handler for that select by putting it inside a BEGIN...END
block:
一个解决方案是声明另一个处理程序,该处理程序将其放入开始…块:
begin
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_thumb_image = NULL;
SELECT thumb_image into v_thumb_image
FROM restaurant_image
WHERE restaurant_id = v_restaurant_id
ORDER BY id
LIMIT 1;
end;
After all, why are doing that using a stored procedure and cursors which would be slow. You can achieve the same functionality executing a single statement:
毕竟,为什么要使用存储过程和游标来实现这一点呢?执行一个语句可以实现相同的功能:
UPDATE restaurant
SET thumb_image = (
SELECT thumb_image
FROM restaurant_image
WHERE restaurant_id = restaurant.id
ORDER BY id
LIMIT 1),
discount_available = IF(EXISTS(
SELECT 1
FROM restaurant_discount
WHERE restaurant_id = restaurant.id), 1, 0),
cuisines_list = (
SELECT group_concat(cuisine.title separator ', ')
FROM restaurant_cuisine
INNER JOIN cuisine ON restaurant_cuisine.cuisine_id = cuisine.id
WHERE restaurant_cuisine.restaurant_id = restaurant.id
LIMIT 0,5)
Or make it even faster by eliminating sub queries for every row:
或者通过消除对每一行的子查询使其更快:
UPDATE restaurant r
LEFT JOIN
(SELECT restaurant_id, count(*) AS discount_available
FROM restaurant_discount
GROUP BY restaurant_id) d ON r.id = d.restaurant_id
LEFT JOIN
(SELECT restaurant_id, thumb_image
FROM restaurant_image r1
WHERE NOT EXISTS (
SELECT 1 FROM restaurant_image r2 WHERE r2.restaurant_id = r1.restaurant_id AND r2.id < r1.id
)) t ON r.id = t.restaurant_id
LEFT JOIN
(SELECT rc.restaurant_id, SUBSTRING_INDEX(GROUP_CONCAT(c.title SEPARATOR ', '), ',', 5) AS cuisines_list
FROM restaurant_cuisine rc
INNER JOIN cuisine c ON rc.cuisine_id = c.id
GROUP BY rc.restaurant_id
) rc ON r.id = rc.restaurant_id
SET r.discount_available = IF(d.discount_available = 0, 0, 1),
r.thumb_image = t.thumb_image,
r.cuisines_list = rc.cuisines_list
Try these sub-queries separately to find a better understanding.
分别尝试这些子查询以找到更好的理解。
#1
4
From the documentation:
从文档:
NOT FOUND is 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'. To detect this condition, you can set up a handler for it (or for a NOT FOUND condition). For an example, see Section 13.6.6, “Cursors”. This condition also occurs for SELECT ... INTO var_list statements that retrieve no rows.
“NOT FOUND”是以“02”开头的SQLSTATE值类的简写。这在游标的上下文中是相关的,用来控制当游标到达数据集的末尾时会发生什么。如果没有更多的行可用,则不会出现SQLSTATE值'02000'的数据条件。要检测此条件,可以为其设置一个处理程序(或为未找到的条件)。例如,请参阅第13.6.6节“游标”。SELECT…也会出现这种情况。进入var_list语句,不检索行。
So your select from restaurant_image
table also meets the NOT FOUND
state when it returns no rows, and invokes the defined handler which causes leaving the loop.
因此,如果选择restaurant_image表不返回任何行,并且调用了导致退出循环的已定义处理程序,那么它也会满足未找到的状态。
One solution is to declare another handler for that select by putting it inside a BEGIN...END
block:
一个解决方案是声明另一个处理程序,该处理程序将其放入开始…块:
begin
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_thumb_image = NULL;
SELECT thumb_image into v_thumb_image
FROM restaurant_image
WHERE restaurant_id = v_restaurant_id
ORDER BY id
LIMIT 1;
end;
After all, why are doing that using a stored procedure and cursors which would be slow. You can achieve the same functionality executing a single statement:
毕竟,为什么要使用存储过程和游标来实现这一点呢?执行一个语句可以实现相同的功能:
UPDATE restaurant
SET thumb_image = (
SELECT thumb_image
FROM restaurant_image
WHERE restaurant_id = restaurant.id
ORDER BY id
LIMIT 1),
discount_available = IF(EXISTS(
SELECT 1
FROM restaurant_discount
WHERE restaurant_id = restaurant.id), 1, 0),
cuisines_list = (
SELECT group_concat(cuisine.title separator ', ')
FROM restaurant_cuisine
INNER JOIN cuisine ON restaurant_cuisine.cuisine_id = cuisine.id
WHERE restaurant_cuisine.restaurant_id = restaurant.id
LIMIT 0,5)
Or make it even faster by eliminating sub queries for every row:
或者通过消除对每一行的子查询使其更快:
UPDATE restaurant r
LEFT JOIN
(SELECT restaurant_id, count(*) AS discount_available
FROM restaurant_discount
GROUP BY restaurant_id) d ON r.id = d.restaurant_id
LEFT JOIN
(SELECT restaurant_id, thumb_image
FROM restaurant_image r1
WHERE NOT EXISTS (
SELECT 1 FROM restaurant_image r2 WHERE r2.restaurant_id = r1.restaurant_id AND r2.id < r1.id
)) t ON r.id = t.restaurant_id
LEFT JOIN
(SELECT rc.restaurant_id, SUBSTRING_INDEX(GROUP_CONCAT(c.title SEPARATOR ', '), ',', 5) AS cuisines_list
FROM restaurant_cuisine rc
INNER JOIN cuisine c ON rc.cuisine_id = c.id
GROUP BY rc.restaurant_id
) rc ON r.id = rc.restaurant_id
SET r.discount_available = IF(d.discount_available = 0, 0, 1),
r.thumb_image = t.thumb_image,
r.cuisines_list = rc.cuisines_list
Try these sub-queries separately to find a better understanding.
分别尝试这些子查询以找到更好的理解。