13

Consider a table like this:

   folders_table
   -----------------------
      INT id_folder
      INT id_folder_parent
      VARCHAR folder_name

Which stores a simple directory structure. How could I get all subdirectories of a directory with a single SELECT query?

Richard Knop
  • 81,041
  • 149
  • 392
  • 552
  • Curious to know why would you want to do this at db level and not at the application layer? – paddle42380 Dec 03 '10 at 13:26
  • 6
    Because one query is much faster than the many queries it would take to build the tree at application level. – El Yobo Dec 03 '10 at 13:50
  • What is the exact result you're looking for from the query? Do you want the subdirectories of a single, given directory? Do you want a list of every directory paired with all its subdirectories? Do you want to construct the tree structure with the query? Something else? – outis Dec 03 '10 at 23:47
  • @outis I was looking to just get all subdirectories of a single directory. But I have done it in application logic with recursion for now (I am just quering db server in a recursive function in PHP). It's a little slow but it will have to work for now. – Richard Knop Dec 04 '10 at 01:17
  • I see, now. In other words, you want all descendent directories. "Subdirectories" is ambiguous, as it could be taken to mean all child directories or all descendent directories. – outis Dec 05 '10 at 01:14
  • possible duplicate of [How can I recursively obtain the "parent ID" of rows in this MySQL table?](http://stackoverflow.com/questions/4006974/how-can-i-recursively-obtain-the-parent-id-of-rows-in-this-mysql-table) – Lightness Races in Orbit Jan 11 '12 at 15:13

5 Answers5

17

It is possible, but you need to change your database structure; once the changes are made, you can retrieve a tree of any depth in one query. The queries are slightly more complex, but it's still pretty straightforward.

Bob Stein
  • 16,271
  • 10
  • 88
  • 101
El Yobo
  • 14,823
  • 5
  • 60
  • 78
  • The sitepoint link is broken. Do you know where to find another copy of that? – Cam Aug 23 '11 at 19:02
  • Sorry, I meant the MySQL article. Do you know where to find a copy of that? – Cam Aug 24 '11 at 00:44
  • 1
    No, I don't sorry, but the information was almost exactly the same. At the time that I read them, I found the Sitepoint article to be the clearer of the two, so even if the MySQL one was available I'd recommend using the Sitepoint one. – El Yobo Aug 24 '11 at 00:51
  • @Cam: The MySQL article is now on the author's personal site, I added the link. – Paŭlo Ebermann Oct 23 '11 at 13:33
  • 1
    The name of the described concepts are "Adjacency List Model" and "Nested Set Model" (with "modified preorder tree traversal algorithm"). You can find a lot of information in the net, regarding this topic in . – juwens Jan 08 '13 at 21:23
3

With the table structure you have shown, this cannot be done with MySQL as it does not support recursive queries

  • See comment below; it's not possible with this data structure, but it is possible by avoiding recursion and changing the data structure a little. – El Yobo Dec 03 '10 at 13:32
  • 1
    That's what I said ;) Recursive queries are not possible with MySQL (unlike with other DBMS) –  Dec 03 '10 at 13:35
  • But this *can* be done with mysql; the OP never said that it had to be done with recursive queries. The approach I point out does it in one query. – El Yobo Dec 03 '10 at 13:51
  • I assumed the OP wants to keep the table structure that is already defined. I edited my answer to include this assumption –  Dec 03 '10 at 13:57
0

With MySql/MariaDB you can use the Open Query Graph engine (http://openquery.com/graph/doc) which is a mysql plugin that lets you create a special table where you put the relationships, basically parentId and childId.

The magic is that you query this table with a special column latch depending of the value passed in the query will tell the OQGRAPH engine which command to execute. See the docs for details.

It handle not only tree (recursive 1-n relations), but graph data structures (recursive n-m relations) with weight (think for example that you want to store companies ownership, a company can have several subsidiaries and can also have several shareholders).

Go4It
  • 561
  • 1
  • 4
  • 14
0

The other option is to store both the depth of the node and keep an identifier for the complete path of each node and use both of these as criteria.

The way I store XML nodes in a relational database is the following:

SELECT id,value FROM element e1
INNER JOIN element e2 ON (e2.id=e1.parent_id AND name='friend')
WHERE e1.depth>4 AND e1.path like 'root[1]/users[1]/user:dana[1]/public[1]%'

In this example, I've got a field for the node name and an interator in square brackets for duplicate nodes with the same node name at each level in the tree.

When you insert each node, you have to calculate the full path by following the parents down to the root node (parent_id IS NULL) appending each level onto an array, at the same time store the depth of the path.

It is always good in any kind of hierarchy stored in a database to have a visual representation and easy access to any path as following the tree on every request can be costly, especially with mysql which does not have any direct recursive SQL syntax.

The left/right scheme of storing nodes in a hierarchy (nested set adjacency list) is too dangerous in my mind and much more can go wrong with this kind of scheme, for one it is very complicated to manage.

0

1、create a new table. tree_folder(id, id_folder, tree_id) 2、create a new table. tree(id, tree_json)

The tree table maintain a whole tree node. For example, the following tree with a root node 1.

{
    "folder_id": 1,
    "parent_folder_id": 0,
    "children": [
      {
          "folder_id": 10,
          "parent_folder_id": 1,
          "children": null
      },
      {
          "folder_id": 11,
          "parent_folder_id": 2,
          "children": null
      }
    ]
}

The table contains this row.

[id, tree_json]
[1, "xxxxx"]

Then maintain the relation between the node and tree. As you can see the tree contains the node 1, 10, 11. Then we have the table tree_folder.

[id, folder_id, tree_id]
[1,  1        ,  1]
[2,  10       , 1]
[3,  11       , 1]

When you need get the folder 10's tree. just get from the tree, then deal it in you program.

In this way, you just do recursion in memory instead of mysql.

That is you should maintain the structure when write the data, but the query is easy and fast. If the query is frequent, this works fine. But if the write is frequent, just use a cache instead of this method.

blackdog
  • 2,007
  • 3
  • 25
  • 43