3

I have a table 'table' with following structure

   ID   LinkedWith
    12     13
    13     12
    14     13
    15     14
    16      0
    17      0
    18     21

We are given an ID say "12" (or may be 15), with it we have to fetch all other related IDs.

Here 12 is linked to 13, 14 is linked to 13, and 15 is linked to 14 and so on. In our case, there are 4 IDs indirectly related, there could be many. For example, in above case we need the final result of IDS 12,13,14,15.

I have got this incomplete function, but couldn't think of further. I know we might need recursive function but not sure how to do that

function testFetchRelated($id){
        $arrayIDs = array($id);


        try{
            $dbh = new PDOConfig("db_test");
            $stmt = $dbh->prepare("SELECT * FROM table WHERE ID='".$id."' OR LinkedWith='".$id."'");
            $stmt->execute();

            if($stmt->rowCount()>0){
                $fetch = $stmt->fetchAll(PDO::FETCH_OBJ);

                foreach($fetch as $f){
                    if($f->LinkedWith>0){
                        array_push($arrayIDs, $f->LinkedWith);
                    }

                    if($f->ID!=$id){
                        array_push($arrayIDs, $f->ID);
                    }
                }
            }else{

            }

            $stmt = null;
            $dbh = null;

            return $arrayIDs;
        }catch(PDOException $e){
            echo "AN ERROR OCCURRED: ".$e->getMessage();
        }

Can anybody help me on this

WatsMyName
  • 4,240
  • 5
  • 42
  • 73
  • Did you try executing query in MySQL workbench or phpmyadmin or any other tool? If yes, what are results you got? – A J Nov 11 '16 at 07:01
  • @AJ I m not sure which query you meant, as I am not stuck in query but I m stuck with a logic, I m sure we need to fetch data recursively, but could not think any further – WatsMyName Nov 11 '16 at 07:06
  • I'm just saying are you getting expected results in the query `SELECT * FROM table WHERE ID='".$id."' OR LinkedWith='".$id."'`? – A J Nov 11 '16 at 07:07
  • @AJ Yes that query results in one row, My code doesn't work, thats just to show at least I have tried something, but couldn't think of the exact logic to get desired output – WatsMyName Nov 11 '16 at 07:10
  • Interesting question, but, then again, I would say wrong database schema. Sorry. I would stay away from recursion. If you're running the PHP locally, why not run each seperate query until you find the end node? That would be O(1) lookup in each case if indexed correctly, if I am not mistaken? – Eugene Nov 11 '16 at 10:30
  • Seems like you're trying to store a linked list, which might help with your Google-ing: http://stackoverflow.com/questions/17537043/mysql-query-for-linked-list for example... – Eugene Nov 11 '16 at 10:32
  • For the record MySQL is very very fast at set operations on large tables. So, if you can change your schema, what about `table` being a link table (composite /primary key are the two keys/fields) where each row is a link. Then just do `SELECT * FROM \`table\` WHERE id = 12` etc... – Eugene Nov 11 '16 at 10:35
  • @Eugene, Unfortunately I can't change DB schema. It already has several thousands of records. – WatsMyName Nov 11 '16 at 10:39
  • OK, that's what I thought, then have you tried just following the results each time and performing each query? Too slow? – Eugene Nov 11 '16 at 10:41
  • @Eugene there could be countless linked records so, It is not possible to try each of them – WatsMyName Nov 11 '16 at 10:46
  • several thousands of records is actually a very small amount. Are you able to create tables? Would take seconds to create a link table... – Eugene Nov 11 '16 at 10:48
  • @Eugene i cannot change table structure. Actually the table has around 1 million records. – WatsMyName Nov 11 '16 at 11:10
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/127886/discussion-between-eugene-and-watsmyname). – Eugene Nov 11 '16 at 11:19

1 Answers1

1

I have knocked this up which does seem to do what you want in a MySQL procedure:-

DELIMITER ;;

DROP PROCEDURE IF EXISTS `find_relations`;;
CREATE PROCEDURE `find_relations`(IN_id INT)
BEGIN

    CREATE TABLE ids_tmp 
    (
        ID  INT(11),
        PRIMARY KEY (ID)
    );

    INSERT INTO ids_tmp
    SELECT a.ID FROM some_table a WHERE a.LinkedWith = IN_id
    UNION 
    SELECT a.LinkedWith FROM some_table a WHERE a.ID = IN_id
    UNION 
    SELECT a.LinkedWith FROM some_table a WHERE a.LinkedWith = IN_id
    UNION 
    SELECT a.ID FROM some_table a WHERE a.ID = IN_id;

    WHILE (ROW_COUNT() > 0) DO
        INSERT INTO ids_tmp
        SELECT a.ID FROM some_table a 
        INNER JOIN ids_tmp b ON a.LinkedWith = b.ID 
        LEFT OUTER JOIN ids_tmp c ON a.ID = c.ID 
        WHERE c.ID IS NULL
        UNION 
        SELECT a.ID FROM some_table a 
        INNER JOIN ids_tmp b ON a.ID = b.ID 
        LEFT OUTER JOIN ids_tmp c ON a.LinkedWith = c.ID 
        WHERE c.ID IS NULL;
    END WHILE;

    SELECT ID FROM ids_tmp;

    DROP TABLE IF EXISTS ids_tmp;

END;;

DELIMITER ;

You can invoke it using CALL find_relations(12);

Not full debugged it (it would be better to use a temporary table, but it relies on accessing the same table multiple times in various single queries, which MySQL doesn't support for temp tables), but hopefully give you some ideas.

If you want to actually use this then probably need to ensure that the created table has a name that is unique for the session.

Kickstart
  • 21,403
  • 2
  • 21
  • 33
  • Thanks, This works but is very slow, takes around 6 seconds to fetch 4 rows – WatsMyName Nov 13 '16 at 09:23
  • @WatsMyName - Not sure what data you are using or the power of the database, but it running this with WAMP on my PC to bring back the 4 records from your original data takes 0.0001 seconds. Performance will depend a bit on the table you have and its indexes, but not that much I wouldn't think. – Kickstart Nov 15 '16 at 17:16