<p>I have the following tables, the <code>groups</code> table which contains hierarchically ordered groups and <code>group_member</code> which stores which groups a user belongs to. </p>

<pre><code>groups
---------
id  
parent_id
name

group_member
---------
id
group_id
user_id

ID  PARENT_ID  NAME
---------------------------
1   NULL       Cerebra
2   1          CATS 
3   2          CATS 2.0 
4   1          Cerepedia 
5   4          Cerepedia 2.0
6   1          CMS 

ID GROUP_ID USER_ID
---------------------------
1  1        3
2  1        4
3  1        5
4  2        7
5  2        6
6  4        6
7  5        12
8  4        9
9  1        10
</code></pre>

<p>I want to retrieve the visible groups for a given user. That it is to say groups a user belongs to and children of these groups. For example, with the above data: </p>

<pre><code>USER  VISIBLE_GROUPS
9     4, 5 
3     1,2,4,5,6
12    5 
</code></pre>

<p>I am getting these values using recursion and several database queries. But I would like to know if it is possible to do this with a single SQL query to improve my app performance. I am using MySQL. </p>

[{'answer_id': 59594, 'author': 'Prestaul', 'author_id': 5628, 'author_profile': 'https://Stackoverflow.com/users/5628', 'pm_score': 0, 'selected': False, 'text': '<p>I don\'t think that this can be accomplished without using recursion.  You can accomplish it with with a single stored procedure using mySQL, but recursion is not allowed in stored procedures by default.  <a href="http://dev.mysql.com/doc/refman/5.0/en/stored-routines.html" rel="nofollow noreferrer">This article</a> has information about how to enable recursion.  I\'m not certain about how much impact this would have on performance verses the multiple query approach.  mySQL may do some optimization of stored procedures, but otherwise I would expect the performance to be similar.</p>\n'}, {'answer_id': 59632, 'author': 'Kevin Fairchild', 'author_id': 3743, 'author_profile': 'https://Stackoverflow.com/users/3743', 'pm_score': 0, 'selected': False, 'text': "<p>Didn't know if you had a Users table, so I get the list via the User_ID's stored in the Group_Member table...</p>\n\n<pre><code>SELECT GroupUsers.User_ID,\n        (\n        SELECT \n            STUFF((SELECT ',' + \n            Cast(Group_ID As Varchar(10))\n            FROM Group_Member Member (nolock) \n            WHERE Member.User_ID=GroupUsers.User_ID\n        FOR XML PATH('')),1,1,'') \n        ) As Groups\nFROM (SELECT User_ID FROM Group_Member GROUP BY User_ID) GroupUsers\n</code></pre>\n\n<p>That returns:</p>\n\n<pre><code>User_ID    Groups\n3          1\n4          1\n5          1\n6          2,4\n7          2\n9          4\n10         1\n12         5\n</code></pre>\n\n<p>Which seems right according to the data in your table.  But doesn't match up with your expected value list (e.g. User 9 is only in one group in your table data but you show it in the results as belonging to two)</p>\n\n<p>EDIT:  Dang. Just noticed that you're using MySQL.  My solution was for SQL Server.  Sorry.</p>\n\n<p>-- Kevin Fairchild</p>\n"}, {'answer_id': 59765, 'author': 'Hank Gay', 'author_id': 4203, 'author_profile': 'https://Stackoverflow.com/users/4203', 'pm_score': -1, 'selected': False, 'text': "<p><del>There's no way to do this in the SQL standard, but you can usually find vendor-specific extensions, e.g., <code>CONNECT BY</code> in Oracle.</del></p>\n\n<p>UPDATE: As the comments point out, this was added in SQL 99.</p>\n"}, {'answer_id': 59767, 'author': 'JosephStyons', 'author_id': 672, 'author_profile': 'https://Stackoverflow.com/users/672', 'pm_score': 3, 'selected': False, 'text': '<p>Two things come to mind:</p>\n\n<p><strong>1 -</strong> You can repeatedly outer-join the table to itself to recursively walk up your tree, as in:</p>\n\n<pre><code>SELECT *\nFROM\n  MY_GROUPS MG1\n ,MY_GROUPS MG2\n ,MY_GROUPS MG3\n ,MY_GROUPS MG4\n ,MY_GROUPS MG5\n ,MY_GROUP_MEMBERS MGM\nWHERE MG1.PARENT_ID = MG2.UNIQID (+)\n  AND MG1.UNIQID = MGM.GROUP_ID (+)\n  AND MG2.PARENT_ID = MG3.UNIQID (+)\n  AND MG3.PARENT_ID = MG4.UNIQID (+)\n  AND MG4.PARENT_ID = MG5.UNIQID (+)\n  AND MGM.USER_ID = 9\n</code></pre>\n\n<p>That\'s gonna give you results like this:</p>\n\n<pre><code>UNIQID PARENT_ID NAME      UNIQID_1 PARENT_ID_1 NAME_1 UNIQID_2 PARENT_ID_2 NAME_2  UNIQID_3 PARENT_ID_3 NAME_3 UNIQID_4 PARENT_ID_4 NAME_4 UNIQID_5 GROUP_ID USER_ID\n4      2         Cerepedia 2        1           CATS   1        null        Cerebra null     null        null   null      null       null   8        4        9\n</code></pre>\n\n<p>The limit here is that you must add a new join for each "level" you want to walk up the tree.  If your tree has less than, say, 20 levels, then you could probably get away with it by creating a view that showed 20 levels from every user.</p>\n\n<p><strong>2 -</strong> The only other approach that I know of is to create a recursive database function, and call that from code.  You\'ll still have some lookup overhead that way (i.e., your # of queries will still be equal to the # of levels you are walking on the tree), but overall it should be faster since it\'s all taking place within the database.</p>\n\n<p>I\'m not sure about MySql, but in Oracle, such a function would be similar to this one (you\'ll have to change the table and field names; I\'m just copying something I did in the past):</p>\n\n<pre><code>CREATE OR REPLACE FUNCTION GoUpLevel(WO_ID INTEGER, UPLEVEL INTEGER) RETURN INTEGER\nIS\nBEGIN\n  DECLARE\n    iResult INTEGER;\n    iParent INTEGER;\nBEGIN\n  IF UPLEVEL &lt;= 0 THEN\n    iResult := WO_ID;\n  ELSE\n    SELECT PARENT_ID\n    INTO iParent\n    FROM WOTREE\n    WHERE ID = WO_ID;    \n    iResult := GoUpLevel(iParent,UPLEVEL-1);  --recursive\n  END;\n  RETURN iResult;\n  EXCEPTION WHEN NO_DATA_FOUND THEN\n    RETURN NULL;\n  END;\nEND GoUpLevel;\n/\n</code></pre>\n'}, {'answer_id': 60588, 'author': 'Paul Kroll', 'author_id': 6280, 'author_profile': 'https://Stackoverflow.com/users/6280', 'pm_score': 2, 'selected': False, 'text': '<p>Joe Cleko\'s books "SQL for Smarties" and "Trees and Hierarchies in SQL for Smarties" describe methods that avoid recursion entirely, by using nested sets. That complicates the updating, but makes other queries (that would normally need recursion) comparatively straightforward.  There are <a href="http://www.dbmsmag.com/9604d06.html" rel="nofollow noreferrer">some examples in this article</a> written by Joe back in 1996. </p>\n'}, {'answer_id': 60974, 'author': 'Grzegorz Gierlik', 'author_id': 1483, 'author_profile': 'https://Stackoverflow.com/users/1483', 'pm_score': 0, 'selected': False, 'text': '<p>There was already similar <a href="https://stackoverflow.com/questions/20426/how-to-maintain-a-recursive-invariant-in-a-mysql-database">question</a> raised.</p>\n\n<p>Here is my answer (a bit edited):</p>\n\n<p>I am not sure I understand correctly your question, but this could work <a href="http://www.depesz.com/index.php/2008/04/11/my-take-on-trees-in-sql/" rel="nofollow noreferrer">My take on trees in SQL</a>.</p>\n\n<p>Linked post described method of storing tree in database -- PostgreSQL in that case -- but the method is clear enough, so it can be adopted easily for any database.</p>\n\n<p>With this method you can easy update all the nodes depend on modified node K with about N simple SELECTs queries where N is distance of K from root node.</p>\n\n<p>Good Luck!</p>\n'}, {'answer_id': 1046311, 'author': 'MSpreij', 'author_id': 126584, 'author_profile': 'https://Stackoverflow.com/users/126584', 'pm_score': 0, 'selected': False, 'text': '<p>I don\'t remember which SO question I found the link under, but <a href="http://www.sitepoint.com/article/hierarchical-data-database/1/" rel="nofollow noreferrer">this article on sitepoint.com</a> (second page) shows another way of storing hierarchical trees in a table that makes it easy to find all child nodes, or the path to the top, things like that. Good explanation with example code.</p>\n\n<hr>\n\n<p>PS. Newish to StackOverflow, is the above ok as an answer, or should it really have been a comment on the question since it\'s just a pointer to a different solution (not exactly answering the question itself)?</p>\n'}]
['https://Stackoverflow.com/questions/59544', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/2138/']