SiteCrafting, Inc.
12 Nov
Detecting when self=parent in MySQL
Usually, it's a bad idea to allow elements in a tree to be parents of themselves, because that creates problems with trying to add other children to those elements, or even listing out the tree's structure.
But, sometimes things have to be their own parent. For example, if you want to report details of an element's children to the parent element, what happens at the top level of the tree? You can't outright exclude a tree element from the reporting, you can't create a placeholder element because that would skew the tree, and you can't add in a special case in your code base for that single item.
Well, you could, but you don't want to. You'd rather have a simple and elegant solution that doesn't require lots of special cases for tree levels, and that doesn't require maintenance. So what do you do?
Consider this function:
This function assumes, of course, that the tree is valid and perfect. No element can be a parent of itself, or it just wouldn't show up at all.
However, with a simple tweak to the where clause in the query, this can be fixed by checking the value of the parent.
There are other ways to make this work, but the only (other) way that I can think of using a single query is with the IF statement.
The limit of this method is that it doesn't detect cycles (A -> B -> C-> A) in the tree, just when an element is a parent of itself.
But, sometimes things have to be their own parent. For example, if you want to report details of an element's children to the parent element, what happens at the top level of the tree? You can't outright exclude a tree element from the reporting, you can't create a placeholder element because that would skew the tree, and you can't add in a special case in your code base for that single item.
Well, you could, but you don't want to. You'd rather have a simple and elegant solution that doesn't require lots of special cases for tree levels, and that doesn't require maintenance. So what do you do?
Consider this function:
function recursive_fetch($parent=0) {
$parent = intval($parent);
$sql = "
SELECT *
FROM recursive_table
WHERE parent=$parent
";
$results = select($sql);
foreach($results as $key => $result) {
$results[$key]['children'] = recursive_fetch($result["id"]);
}
return $results;
}
If you ran this query, it would build a hierarchical tree of all the elements in the table, starting with 0 (meaning top level), and going all the way down to the bottom of the tree.This function assumes, of course, that the tree is valid and perfect. No element can be a parent of itself, or it just wouldn't show up at all.
However, with a simple tweak to the where clause in the query, this can be fixed by checking the value of the parent.
$sql = "So when $parent is 0 (aka the first call of the function), the second portion of the where clause is "
SELECT *
FROM recursive_table
WHERE
(parent = $parent AND id != parent)
OR
(0 = $parent AND id = parent)
";
(0 = 0 AND id = parent)". So the query fetches all the items in the table that have a parent of 0, and any row that is its own parent. When the parent is explicitly set on subsequent calls of the function, "(0 = 1 AND id = parent)" is false, so any items where id=parent are excluded. The first part of the where clause takes over, and "(parent = 1 and id != parent)" fetches all items in the table that are not parents of themselves.There are other ways to make this work, but the only (other) way that I can think of using a single query is with the IF statement.
The limit of this method is that it doesn't detect cycles (A -> B -> C-> A) in the tree, just when an element is a parent of itself.
Coding Techniques, From the Workbench, MySQL, Software Engineering
by Dave Poole | 11/12/2009 11:26am | Comments (5)
by Dave Poole | 11/12/2009 11:26am | Comments (5)
Alas, the original can do an index lookup, whereas the new one will probably mean a table scan.
Left by Scott Noyes | Nov 12, 2009
Solving this problem is much simpler if you use OQGRAPH. You can simply select all children which are reachable from the "root" and then simply examine which rows are not in the set of reachable children. It would just as easily detect cycles.
Left by Antony | Nov 12, 2009
apparently you are not in the habit of using foreign keys
try creating a row with parent=0 or parent=id and see what happens
;o)
Left by r937 | Nov 13, 2009
@Scott - As always, there are tradeoffs when programming. The most efficient solution is rarely the simplest, and vice versa. Also, doing an EXPLAIN on both queries with my test table didn't show any differences.
@Antony - I haven't used OQGRAPH before, though it looks quite powerful. If you have a massive database of hierarchical objects, that would definitely be the way to go.
@r937 - If you wanted to use foreign keys, you would have to create a placeholder element in the tree to represent the root. In some cases, developers might not want to create (or simply not be able to create) entities in the tree that are just there to serve as a foreign key reference, so this method becomes useful. Creating a placeholder root value would eliminate the need for checking for self parenting situations, but it also introduces other issues to deal with.
Left by Dave Poole | Nov 13, 2009
but that's just it -- you don't have to create a placeholder entity if you use NULL as the parent value for root entities
so your parent column isn't actually a foreign key? try it with NULL, it works ;o)
Left by r937 | Nov 13, 2009