使用 MySQL 个存储过程从结果集中选择唯一值
Selecting unique values from the result set using MySQL stored procedures
从上述结果集中选择不同的值,即,从而消除重复值,并最终将这些值存储到具有逗号分隔值列表的变量中。这样,分配有逗号分隔值列表的变量必须作为另一个 SQL IN 运算符
的输入
DELIMITER $$
USE `someDB`$$
DROP PROCEDURE IF EXISTS `AAA`$$
CREATE PROCEDURE `AAA`(IN `feed_setting_user_id` BIGINT)
READS SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
DECLARE FoFID, FoFUsername, friendID, friendUsername TEXT;
DECLARE exit_loop BOOLEAN DEFAULT FALSE;
DECLARE friend_cursor CURSOR FOR
SELECT `u`.`ID`, `u`.`username` 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;
OPEN friend_cursor;
friend_loop: LOOP
FETCH FROM friend_cursor INTO friendID, friendUsername;
IF exit_loop THEN
LEAVE friend_loop;
END IF;
-- SELECT friendID, friendUsername;
SELECT `u`.`ID`, `u`.`username` 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` = friendID AND `uf`.`friendid` != friendID)
OR `u1`.`ID` IN (SELECT `uf2`.`userid` FROM `user_friends` `uf2` WHERE `uf2`.`status` = '2' AND `uf2`.`friendid` = friendID AND `uf2`.`userid` != friendID) AND `u1`.`ID` != friendID );
END LOOP friend_loop;
CLOSE friend_cursor;
END $$
DELIMITER ;
实际结果:
+----+-----------+
| ID | username |
+----+-----------+
| 5 | SpiderMan |
| 8 | AntMan |
| 9 | Bat |
| 11 | SuperMan |
| 12 | Arrow |
| 13 | CAmerica |
+----+-----------+
6 rows in set (0.53 sec)
+----+----------+
| ID | username |
+----+----------+
| 9 | Bat |
| 10 | BatMan |
| 13 | mustafa |
+----+----------+
3 rows in set (0.61 sec)
+----+-----------+
| ID | username |
+----+-----------+
| 5 | SpiderMan |
| 6 | Hulk |
| 9 | Bat |
| 10 | BatMan |
+----+-----------+
4 rows in set (0.69 sec)
+----+----------+
| ID | username |
+----+----------+
| 8 | AntMan |
| 9 | Bat |
| 10 | BatMan |
| 11 | SuperMan |
+----+----------+
4 rows in set (0.78 sec)
Query OK, 0 rows affected (0.86 sec)
预期结果:
5,6,8,9,11,12,10,13
我们需要将上面的逗号分隔值列表分配给一个变量。
所以我们需要从上面的值列表中删除重复项。
更新:
尝试实现嵌套游标
DELIMITER $$
USE `someDB`$$
DROP PROCEDURE IF EXISTS `AAA`$$
CREATE PROCEDURE `AAA`(IN `feed_setting_user_id` BIGINT)
READS SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
DECLARE FoFID, FoFUsername, friendID, friendUsername TEXT;
DECLARE exit_loop BOOLEAN DEFAULT FALSE;
DECLARE exit_loop1 BOOLEAN DEFAULT FALSE;
DECLARE friend_cursor CURSOR FOR
SELECT `u`.`ID`, `u`.`username` 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;
OPEN friend_cursor;
friend_loop: LOOP
FETCH FROM friend_cursor INTO friendID, friendUsername;
IF exit_loop THEN
LEAVE friend_loop;
END IF;
-- SELECT friendID, friendUsername;
DECLARE friend_of_friend_cursor CURSOR FOR
SELECT `u`.`ID`, `u`.`username` 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` = friendID AND `uf`.`friendid` != friendID)
OR `u1`.`ID` IN (SELECT `uf2`.`userid` FROM `user_friends` `uf2` WHERE `uf2`.`status` = '2' AND `uf2`.`friendid` = friendID AND `uf2`.`userid` != friendID) AND `u1`.`ID` != friendID );
DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop1 = TRUE;
OPEN friend_of_friend_cursor;
friend_of_friend_loop: LOOP
FETCH FROM friend_of_friend_cursor INTO FoFID, FoFUsername;
IF exit_loop1 THEN
LEAVE friend_of_friend_loop;
END IF;
SELECT FoFID, FoFUsername;
END LOOP friend_of_friend_loop;
CLOSE friend_of_friend_cursor;
END LOOP friend_loop;
CLOSE friend_cursor;
END $$
DELIMITER ;
结果:
Error Code: 1064
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'DECLARE friend_of_friend_cursor CURSOR FOR
SELECT `u`.`ID`, `u`.`user' at line 29
试试下面的 sp
DELIMITER $$
USE `someDB`$$
DROP PROCEDURE IF EXISTS `AAA`$$
CREATE PROCEDURE `AAA`(IN `feed_setting_user_id` BIGINT)
modifies SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
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 ;
从上述结果集中选择不同的值,即,从而消除重复值,并最终将这些值存储到具有逗号分隔值列表的变量中。这样,分配有逗号分隔值列表的变量必须作为另一个 SQL IN 运算符
的输入DELIMITER $$
USE `someDB`$$
DROP PROCEDURE IF EXISTS `AAA`$$
CREATE PROCEDURE `AAA`(IN `feed_setting_user_id` BIGINT)
READS SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
DECLARE FoFID, FoFUsername, friendID, friendUsername TEXT;
DECLARE exit_loop BOOLEAN DEFAULT FALSE;
DECLARE friend_cursor CURSOR FOR
SELECT `u`.`ID`, `u`.`username` 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;
OPEN friend_cursor;
friend_loop: LOOP
FETCH FROM friend_cursor INTO friendID, friendUsername;
IF exit_loop THEN
LEAVE friend_loop;
END IF;
-- SELECT friendID, friendUsername;
SELECT `u`.`ID`, `u`.`username` 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` = friendID AND `uf`.`friendid` != friendID)
OR `u1`.`ID` IN (SELECT `uf2`.`userid` FROM `user_friends` `uf2` WHERE `uf2`.`status` = '2' AND `uf2`.`friendid` = friendID AND `uf2`.`userid` != friendID) AND `u1`.`ID` != friendID );
END LOOP friend_loop;
CLOSE friend_cursor;
END $$
DELIMITER ;
实际结果:
+----+-----------+
| ID | username |
+----+-----------+
| 5 | SpiderMan |
| 8 | AntMan |
| 9 | Bat |
| 11 | SuperMan |
| 12 | Arrow |
| 13 | CAmerica |
+----+-----------+
6 rows in set (0.53 sec)
+----+----------+
| ID | username |
+----+----------+
| 9 | Bat |
| 10 | BatMan |
| 13 | mustafa |
+----+----------+
3 rows in set (0.61 sec)
+----+-----------+
| ID | username |
+----+-----------+
| 5 | SpiderMan |
| 6 | Hulk |
| 9 | Bat |
| 10 | BatMan |
+----+-----------+
4 rows in set (0.69 sec)
+----+----------+
| ID | username |
+----+----------+
| 8 | AntMan |
| 9 | Bat |
| 10 | BatMan |
| 11 | SuperMan |
+----+----------+
4 rows in set (0.78 sec)
Query OK, 0 rows affected (0.86 sec)
预期结果:
5,6,8,9,11,12,10,13
我们需要将上面的逗号分隔值列表分配给一个变量。 所以我们需要从上面的值列表中删除重复项。
更新:
尝试实现嵌套游标
DELIMITER $$
USE `someDB`$$
DROP PROCEDURE IF EXISTS `AAA`$$
CREATE PROCEDURE `AAA`(IN `feed_setting_user_id` BIGINT)
READS SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
BEGIN
DECLARE FoFID, FoFUsername, friendID, friendUsername TEXT;
DECLARE exit_loop BOOLEAN DEFAULT FALSE;
DECLARE exit_loop1 BOOLEAN DEFAULT FALSE;
DECLARE friend_cursor CURSOR FOR
SELECT `u`.`ID`, `u`.`username` 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;
OPEN friend_cursor;
friend_loop: LOOP
FETCH FROM friend_cursor INTO friendID, friendUsername;
IF exit_loop THEN
LEAVE friend_loop;
END IF;
-- SELECT friendID, friendUsername;
DECLARE friend_of_friend_cursor CURSOR FOR
SELECT `u`.`ID`, `u`.`username` 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` = friendID AND `uf`.`friendid` != friendID)
OR `u1`.`ID` IN (SELECT `uf2`.`userid` FROM `user_friends` `uf2` WHERE `uf2`.`status` = '2' AND `uf2`.`friendid` = friendID AND `uf2`.`userid` != friendID) AND `u1`.`ID` != friendID );
DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop1 = TRUE;
OPEN friend_of_friend_cursor;
friend_of_friend_loop: LOOP
FETCH FROM friend_of_friend_cursor INTO FoFID, FoFUsername;
IF exit_loop1 THEN
LEAVE friend_of_friend_loop;
END IF;
SELECT FoFID, FoFUsername;
END LOOP friend_of_friend_loop;
CLOSE friend_of_friend_cursor;
END LOOP friend_loop;
CLOSE friend_cursor;
END $$
DELIMITER ;
结果:
Error Code: 1064
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'DECLARE friend_of_friend_cursor CURSOR FOR
SELECT `u`.`ID`, `u`.`user' at line 29
试试下面的 sp
DELIMITER $$
USE `someDB`$$
DROP PROCEDURE IF EXISTS `AAA`$$
CREATE PROCEDURE `AAA`(IN `feed_setting_user_id` BIGINT)
modifies SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
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 ;