简体   繁体   中英

How to follow parent_id through mysql table?

I have a mysql table that represents a pseudo directory system:

CREATE TABLE `file_directories` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `parent_id` int(11) DEFAULT NULL,
 `name` varchar(255) NOT NULL,
 `level` int(11) NOT NULL DEFAULT '1',
 `created` datetime NOT NULL,
 PRIMARY KEY (`name`,`id`),
 KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

When a user is browsing this system our function receives a path that is made up of entries from the name column.

So, something like first/child of first/grandchild or second/child of second/grandchild would be a valid path and would look like this in the database.

/----------------------------------------------------\
| id | parent_id | name            | level | created |
|----|-----------|-----------------|-------|---------|
| 1  | NULL      | First           | 1     | ...     |
| 2  | 1         | Child of First  | 2     | ...     |
| 3  | 2         | Grandchild      | 3     | ...     |
| 4  | NULL      | Second          | 1     | ...     |
| 5  | 4         | Child of Second | 2     | ...     |
| 6  | 5         | Grandchild      | 3     | ...     |
\----------------------------------------------------/

Now currently if I want to list a directories children my process is this:

$path = 'first/child of first'; // demo data
$path = explode('/', $path); //array('first', 'child of first');
$level = count($path);
$name = end($path);

//query is not actually built like this, it uses the Codeigniter Active Records library
//but this is effectively the end result,
$sql = "SELECT * FROM `file_directories` WHERE `name` = '$name' AND `level` = $level";

///etc

Which works fine, until we deal with the grandchild directories, which have the same name and exist at the same level.

The directory structure enforces only one directory can exist with the same parent_id and name but same name 'd directories with different parent_id 's can exist on the same level .

I cannot change what data I am passed, so the only way I can think of to do this is to start at the root node, and loop down doing multiple queries to find the correct child.

So, for the grandchild of second, the queries would be.

$parent_id = NULL;
foreach($path as $seg){
    $id = SQL: SELECT `id` FROM `file_directories` WHERE `name` = '$seg' AND `parent_id` = (IS NULL for root node) $parent_id;
}

//Get the actual node
$node = SQL: SELECT `*` FROM `file_directories` WHERE `id` = '$id';

But, that's a lot of queries, so, without changing what data I am given, is there a better way of tracing through the tree? or selecting the correct node?

learn the about wonders of recursive functions: This code is bit simplified.

function select_child($node_id, $level, $target_level) {
  if(SELECT new_node_id WHERE parent_id = $node_id)
    if($level = $target_level) {
      return($new_node);
    } else {
      $results->add(select_child($new_node_id, ($level +1), $target_level);
    }
  } else {
    return(NULL);
  }
}

this function loops through the children of the given node until it finds a "leaf" (node without child) or it reaches the targeted level. if you are working with trees, most of your functions have to be recursive (calling themself until a specified condition is matched).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM