Question

i am faced with a decicion regarding handling threaded comments in our project... I have a simple MySQL table which holds all the comments. There are two types: parents and childs. Childs represent a reply to a parent or another child.

My problem:

-Comment (depth 0)
-- Reply Child (depth 1)
--- Reply to previous child (depth 2)
-Comment (depth 0)

Imagine the above structure and a MySQL query with LIMIT 2. It would cut of the last reply (depth 2). Actually i would like to say something like: Try to limit to 2, if child left go on until the next parent. Tried several queries with no luck...

What i have right now is as followed:
SELECT SQL_CALC_FOUND_ROWS * FROM comments WHERE comment_post_id = '{$_REQUEST["ID"]}' ORDER BY comment_id, comment_date DESC LIMIT 10"

The important table fields are:
comment_id (index) | comment_parent_id (contains comment_id of parent or NULL)| comment_date

I would be very thankful for any ideas!!!

Saludos, Booosh

Was it helpful?

Solution

MySQL does not have any functions to parse tree-like structures. In the simplest scenario (child has an ID of the parent), you will need to programatically recurse into the tree to locate all the sub-nodes of a given node. MaxLevel indicates the depth you want to go to. It decrements with each recursive call so that at the end you end up with 0, which stops recursion.

e.g. (pseudo-code)

findNodes(String parentId, int maxLevel)
{
  select * from posts where parent = parentId
  foreach (result...)
  {
    if (maxLevel > 0) 
    {
      findNodes(result.nodeId, maxLevel - 1)
    }
    doSomethingWIthAResult
  }
}

To do this in a more concise way, there are a number of techniques, all of which involve some sort of index field that contains path to the current post. The path could look something like this: TopNode:Child1:Child2:Child3... In which you could do a select like this Select * from posts where path like "TopNode%" and depth = 2.

OTHER TIPS

Always think about the question you really want to ask the database and then translate that into SQL - in this case you want "a list of all top-level comments with their immediate children, if any".

eg. (simplified)

SELECT * FROM comments c1
LEFT JOIN comments c2 ON c2.parent_comment_id=c1.comment_id
WHERE c1.parent_comment_id IS NULL
ORDER BY c1.comment_date, c1.comment_id, c2.comment_date, c2.comment_id;

With that result, you can write them out in the right order - if c2.comment_id is null, it's a top-level comment with no children, and if c1.comment_id is repeated, it's another child of the same comment.

I was faced with this same problem, only I was going only one depth deep.

--Comment (depth: 0)
---Reply  (depth: 1)

I managed to use a single query to select all these records, while limiting the top level Comment records to only 10.

SELECT c.* FROM comments AS c WHERE c.OwnerId = 1 AND c.ParentId = 0 LIMIT 10
UNION
SELECT cc.* FROM comments AS cc
    INNER JOIN
    (
        SELECT CommentId FROM comments WHERE OwnerId = 1 AND ParentId = 0 LIMIT 10
    )
    AS c2
    ON cc.ParentId = c2.CommentId

This query basically does the following:

  • Gets the first 10 records that are top level comments, and have a specific owner id.
  • Gets all comments that have a parent id equal to the comment id that was returned by the first query, and unions them to the result set.

While, I think this query would be more efficient than making multiple calls to the database for each record, it still does have the flaw that it executes the first query twice. Once before the union, and one in the join on the union.

It does seem to be fairly quick, but not as quick as I would like. However, if your database is remote, and latency is an issue, this solution might serve you better than making multiple remote queries to the database.

I finally managed it based on Greg Adamskis hint... So dont vote my answer but check his one!!!

To describe the problem in short... We need a pagination for a comment list on our website. Using a standard limit could cause that some comments never would be shown... What we needed was a Limit which only affected our parent nodes not the cild nodes which are replies... Long story... however maybe one time this iis useful for someone:

    function getComments($comment_parent_id, $scope,&$comments, $db)
    {
        $res = $db->select("SELECT * FROM comments WHERE comment_post_id = '{$_REQUEST["ID"]}' AND comment_parent_id = '{$comment_parent_id}' ORDER BY comment_date DESC LIMIT {$scope}");

        while ($row = mysql_fetch_array($res, MYSQL_ASSOC)) 
        {
            $i = count($comments)+1;

            foreach ($row as $k => $v) {
                $comments[$i][$k] = $v;
            }

            //LOOK FOR REPLIES (childs of parent)
            if (mysql_num_rows($db->select("SELECT * FROM comments WHERE comment_parent_id = '{$row['comment_id']}' LIMIT 1")) != 0 ){
                getComments($row['comment_id'],100,$comments,$db);
            }
        }
    }

    //ARGUMENTS: parent_id (always starting with zero), scope, array holding comments, db class
    getComments(0,5,$comments,$db);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top