【问题标题】:An Issue with storing data with Stored Procedure Cursors使用存储过程游标存储数据的问题
【发布时间】:2019-12-01 05:04:45
【问题描述】:

在这里,我正在尝试实现以下存储过程。但价值没有按预期保存

DELIMITER $$

USE `someDB`$$

DROP PROCEDURE IF EXISTS `AAB`$$

CREATE PROCEDURE `AAB`(IN `feed_setting_user_id` BIGINT)
BEGIN
DECLARE v_friendID, FoFID BIGINT; # use datatype which is used for u.ID used int based on ur result
DECLARE exit_loop, exit_loop1 BOOLEAN DEFAULT FALSE;

DECLARE friend_cursor CURSOR FOR 

    SELECT `u`.`ID` FROM `users` `u`  
    WHERE `u`.`ID` IN (SELECT `u1`.`ID` FROM users `u1` 
    WHERE `u1`.`ID` IN (SELECT `uf`.`friendid` FROM user_friends `uf` WHERE `uf`.`status` = '2' AND `uf`.`userid` = feed_setting_user_id ) 
    OR `u1`.`ID` IN (SELECT `uf2`.`userid` FROM `user_friends` `uf2` WHERE `uf2`.`status` = '2' AND `uf2`.`friendid` = feed_setting_user_id) 
    AND `u1`.`ID` != feed_setting_user_id); 

DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;

DROP TEMPORARY TABLE IF EXISTS unique_tbl; #precaution: when sp stops with error 
CREATE TEMPORARY TABLE unique_tbl (user_id BIGINT UNIQUE); #to avoid duplicate added unique

INSERT INTO unique_tbl VALUES (feed_setting_user_id); # added input from sp

    OPEN friend_cursor;
    friend_loop: LOOP

        FETCH friend_cursor INTO v_friendID;

        IF exit_loop THEN
            LEAVE friend_loop;
        ELSE

            REPLACE INTO unique_tbl VALUES (v_friendID); # since we need all unique id's using replace if exists

            fof: BEGIN

                DECLARE friend_of_friend_cursor CURSOR FOR 


                    SELECT `u`.`ID` FROM `users` `u` WHERE `u`.`ID` NOT IN (feed_setting_user_id) AND `u`.`ID` 
            IN (SELECT `u1`.`ID` FROM `users` `u1` 
            WHERE `u1`.`ID` IN (SELECT `uf`.`friendid` FROM `user_friends` `uf` WHERE `uf`.`status` = '2' AND `uf`.`userid` = v_friendID AND `uf`.`friendid` != v_friendID) 
            OR `u1`.`ID` IN (SELECT `uf2`.`userid` FROM `user_friends` `uf2` WHERE `uf2`.`status` = '2' AND `uf2`.`friendid` = v_friendID AND `uf2`.`userid` != v_friendID) 
            AND `u1`.`ID` != v_friendID ); 

                DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop1 = TRUE;

                OPEN friend_of_friend_cursor;
                friend_of_friend_loop: LOOP

                    FETCH friend_of_friend_cursor INTO FoFID;

                    IF exit_loop1 THEN
                        LEAVE friend_of_friend_loop;
                    END IF;

                    REPLACE INTO unique_tbl VALUES (FoFID);

                END LOOP friend_of_friend_loop;
                CLOSE friend_of_friend_cursor;
            END;
        END IF;
    END LOOP friend_loop;
    CLOSE friend_cursor;

SELECT GROUP_CONCAT(user_id) FROM unique_tbl; #this will show result in comma seperated 2,34,56,78

#cleanup
DROP TEMPORARY TABLE IF EXISTS unique_tbl;

END$$

DELIMITER ;

实际结果:

+-----------------------------------+
| GROUP_CONCAT(user_id)             |
+-----------------------------------+
| 1,2,3,5,6,7,8,9,11,14,15,19,20,26 |
+-----------------------------------+
1 row in set (0,01 sec)

Query OK, 0 rows affected (0,01 sec)

预期结果:

+--------------------------------------+
| GROUP_CONCAT(user_id)                |
+--------------------------------------+
| 1,2,3,5,6,7,8,9,11,14,15,19,20,25,26 |
+--------------------------------------+
1 row in set (0,01 sec)

Query OK, 0 rows affected (0,01 sec)

ID = 25 已被删除


调试以上两条SQL:

将 feed_setting_user_id = 15 设置为以下 SQL 查询

SELECT
  `u`.`ID`
FROM
  `users` `u`
WHERE `u`.`ID` IN
  (SELECT
    `u1`.`ID`
  FROM
    users `u1`
  WHERE `u1`.`ID` IN
    (SELECT
      `uf`.`friendid`
    FROM
      user_friends `uf`
    WHERE `uf`.`status` = '2'
      AND `uf`.`userid` = feed_setting_user_id)
    OR `u1`.`ID` IN
    (SELECT
      `uf2`.`userid`
    FROM
      `user_friends` `uf2`
    WHERE `uf2`.`status` = '2'
      AND `uf2`.`friendid` = feed_setting_user_id)
    AND `u1`.`ID` != feed_setting_user_id);

结果:

+----+
| ID |
+----+
|  1 |
| 14 |
| 26 |
+----+
3 rows in set (0,00 sec)

用于递归地将上述 ID's 替换为 v_friendID 并保持 feed_setting_user_id = 15 并运行以下 SQL

SELECT
  `u`.`ID`
FROM
  `users` `u`
WHERE `u`.`ID` NOT IN (feed_setting_user_id)
  AND `u`.`ID` IN
  (SELECT
    `u1`.`ID`
  FROM
    `users` `u1`
  WHERE `u1`.`ID` IN
    (SELECT
      `uf`.`friendid`
    FROM
      `user_friends` `uf`
    WHERE `uf`.`status` = '2'
      AND `uf`.`userid` = v_friendID
      AND `uf`.`friendid` != v_friendID)
    OR `u1`.`ID` IN
    (SELECT
      `uf2`.`userid`
    FROM
      `user_friends` `uf2`
    WHERE `uf2`.`status` = '2'
      AND `uf2`.`friendid` = v_friendID
      AND `uf2`.`userid` != v_friendID)
    AND `u1`.`ID` != v_friendID); 

结果如下:

代替v_friendID = 1和feed_setting_user_id = 15

+----+
| ID |
+----+
|  2 |
|  3 |
|  5 |
|  6 |
|  7 |
|  8 |
|  9 |
| 11 |
| 19 |
| 20 |
+----+
10 rows in set (0,00 sec)

代替v_friendID = 14和feed_setting_user_id = 15

+----+
| ID |
+----+
| 15 |
| 25 |
+----+
2 rows in set (0,00 sec)

代替v_friendID = 26和feed_setting_user_id = 15

Empty set (0,00 sec)

值=15 的 ID 正在保存到 unique_tbl。但不适用于 value=25 的 ID

【问题讨论】:

  • 我没有检查这是否真的会导致您的问题(或者是否还有其他问题),但它突然出现在我身上,所以您应该尝试一下:您没有重置 exit_loop1在inner friend_of_friend_loop-loop 之前到false。所以你第二次去那里,已经是真的了,马上就停下来。
  • 是的!你的权利@Solarflare
  • 有没有其他更好的方法来做同样的事情? @Solarflare
  • 您的意思是替代您的程序?可能,看起来a recursive query 可能能够实现相同的效果(尽管正如我所说,我实际上并没有完全检查您的代码)。虽然决定它是否“更好”(更快/更容易理解/适应/...)取决于您。

标签: mysql sql stored-procedures cursor


【解决方案1】:
BEGIN
DECLARE v_friendID, FoFID BIGINT; # use datatype which is used for u.ID used int based on ur result
DECLARE exit_loop, exit_loop1 BOOLEAN DEFAULT FALSE;

DECLARE friend_cursor CURSOR FOR 

    SELECT `u`.`ID` FROM `users` `u`  
    WHERE `u`.`ID` IN (SELECT `u1`.`ID` FROM users `u1` 
    WHERE `u1`.`ID` IN (SELECT `uf`.`friendid` FROM user_friends `uf` WHERE `uf`.`status` = '2' AND `uf`.`userid` = feed_setting_user_id ) 
    OR `u1`.`ID` IN (SELECT `uf2`.`userid` FROM `user_friends` `uf2` WHERE `uf2`.`status` = '2' AND `uf2`.`friendid` = feed_setting_user_id) 
    AND `u1`.`ID` != feed_setting_user_id); 

DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;

DROP TEMPORARY TABLE IF EXISTS unique_tbl; #precaution: when sp stops with error 
CREATE TEMPORARY TABLE unique_tbl (user_id BIGINT UNIQUE); #to avoid duplicate added unique

INSERT INTO unique_tbl VALUES (feed_setting_user_id); # added input from sp

    OPEN friend_cursor;
    friend_loop: LOOP

        FETCH friend_cursor INTO v_friendID;

        IF exit_loop THEN
            LEAVE friend_loop;
        ELSE

            REPLACE INTO unique_tbl VALUES (v_friendID); # since we need all unique id's using replace if exists

            fof: BEGIN

                DECLARE friend_of_friend_cursor CURSOR FOR 


                    SELECT `u`.`ID` FROM `users` `u` WHERE `u`.`ID` NOT IN (feed_setting_user_id) AND `u`.`ID` 
            IN (SELECT `u1`.`ID` FROM `users` `u1` 
            WHERE `u1`.`ID` IN (SELECT `uf`.`friendid` FROM `user_friends` `uf` WHERE `uf`.`status` = '2' AND `uf`.`userid` = v_friendID AND `uf`.`friendid` != v_friendID) 
            OR `u1`.`ID` IN (SELECT `uf2`.`userid` FROM `user_friends` `uf2` WHERE `uf2`.`status` = '2' AND `uf2`.`friendid` = v_friendID AND `uf2`.`userid` != v_friendID) 
            AND `u1`.`ID` != v_friendID ); 

                DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop1 = TRUE;

                OPEN friend_of_friend_cursor;
                friend_of_friend_loop: LOOP

                    FETCH friend_of_friend_cursor INTO FoFID;

                    IF exit_loop1 THEN
                        LEAVE friend_of_friend_loop;
                    END IF;

                    REPLACE INTO unique_tbl VALUES (FoFID);

                END LOOP friend_of_friend_loop;
                CLOSE friend_of_friend_cursor;
                SET exit_loop1 = FALSE; 
            END;
        END IF;
    END LOOP friend_loop;
    CLOSE friend_cursor;
    SET exit_loop = FALSE;

SELECT GROUP_CONCAT(user_id) FROM unique_tbl; #this will show result in comma seperated 2,34,56,78

#cleanup
DROP TEMPORARY TABLE IF EXISTS unique_tbl;

END$$

DELIMITER ;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-04
    • 2018-12-08
    • 1970-01-01
    • 1970-01-01
    • 2012-10-22
    相关资源
    最近更新 更多