Search code examples
mysqlsqlstored-procedurescursorunique

Selecting unique values from the result set using MySQL stored procedures


Selecting distinct values from the above result set i.e, thereby eliminating duplicate values and finally storing these values into a variable with a comma-separated list of values. Such that variable assigned with a comma-separated list of values must be given as an input to another SQL IN Operator

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 ;

Actual Result:

+----+-----------+
| 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)

Expected Result:

5,6,8,9,11,12,10,13

We need to assign the above list of comma separated values into a variable. So that we need to eliminate duplicates from the above list of values.


Update:

Tried to implement nested cursors

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 ;

Results:

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

Solution

  • Try the below 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 ;