-3

I have a table like this:

+-----+-------------------------------------+--------------------------+---------------+
| id  | parent_id |  comment_text           | date_posted              |
+-----+-------------------------------------+--------------------------+---------------+
|   1 | 0         |  1                      | 2020-01-08 20:40:00      | 
|   2 | 1         |  1.1                    | 2020-01-08 20:41:00      | 
|   3 | 0         |  2                      | 2020-01-08 20:42:00      | 
|   4 | 0         |  3                      | 2020-01-08 20:43:00      | 
|   5 | 3         |  2.1                    | 2020-01-08 20:44:00      | 
|   6 | 2         |  1.1.1                  | 2020-01-08 20:45:00      |
|   7 | 1         |  1.2                    | 2020-01-08 20:46:00      | 

How do I get ordered comments like this? (ORDER BY date_posted DESC in each comment level and using one MySQL query because I need paging them using limit and offset).

comment_text           
    3
    2
       2.1
    1
       1.2
       1.1
          1.1.1
Steve Chambers
  • 37,270
  • 24
  • 156
  • 208
DeLe
  • 2,442
  • 19
  • 88
  • 133
  • What do you mean by paging? If you mean in a MySQL query, can you explain by `LIMIT 2 OFFSET ...` is not giving you what you want? – cmbuckley Feb 24 '20 at 10:59
  • @cmbuckley yes, but how to order like my example?, i just updated for clear – DeLe Feb 24 '20 at 11:50
  • order by comment_text desc – Grumpy Feb 24 '20 at 12:01
  • @Grumpy sorry, it's my example text – DeLe Feb 24 '20 at 12:35
  • See [Nested comment system mysql ordering](https://stackoverflow.com/questions/15538168/nested-comment-system-mysql-ordering) – cmbuckley Feb 24 '20 at 16:36
  • what do you mean by paging, btw? if you had a page of 100 comments, how would you present them in such a way that you can still see the comments of top level comments? – Ja͢ck Feb 25 '20 at 02:28
  • @Ja͢ck you can see my example paging in first edit version question – DeLe Feb 25 '20 at 02:32
  • Then, having level inside your comments table could help with this, because you could just `order by level, date_posted desc` – Ja͢ck Feb 25 '20 at 02:34
  • @Ja͢ck can you show example of level?, i can add more if no other way – DeLe Feb 25 '20 at 02:39
  • the comments for 1, 2, 3 would be at level 0; then 1.1, 1.2, 2.1 would be at level 1, etc. – Ja͢ck Feb 25 '20 at 02:43
  • @Ja͢ck that seem not correct b/c it will be 3 > 2 > 1 > 1.2 > 2.1 > 1.1 ... that not the same my ordered – DeLe Feb 25 '20 at 02:57
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/208456/discussion-between-jack-and-dele). – Ja͢ck Feb 25 '20 at 02:59
  • I have a 100% working code demo that produces the exact output as you are requiring: https://ideone.com/DXxx7m . I'm not sure why someone would downvote my answer. – HoldOffHunger Feb 26 '20 at 17:29
  • 1
    What version of MySQL? 8.0 has better tools for hierarchy traversal. – Rick James Feb 26 '20 at 17:31
  • @RickJames seem i use MySQL version < 8 – DeLe Feb 27 '20 at 04:40
  • 1
    @DeLe - About how many rows in the table? How deep is the hierarchy? Can we assume there are no loops? – Rick James Feb 27 '20 at 06:03
  • @RickJames I have many rows in the table, and no limit deep :( (now i must paging them by limit and offset).is it possible? – DeLe Feb 27 '20 at 06:29
  • @DeLe - It is possible with a loop. That could be implemented either in a Stored Proc or in the application code. The loop would iterate only as often as the tree is deep. – Rick James Feb 27 '20 at 07:00
  • @RickJames if it possible can you make a MySQL query with high performance? – DeLe Feb 27 '20 at 07:10
  • Create and maintain a `path` column: https://www.db-fiddle.com/f/mE24hWHFVWFQdK2f1XVC5G/0 – Paul Spiegel Feb 27 '20 at 13:21
  • @DeLe: It seems like you have offered a 100xp bounty, received 3 answers, and didn't accept any. Did you ever find any solution to `how do I make data ordered like this`? – HoldOffHunger Mar 05 '20 at 03:50

3 Answers3

0

Seeing as you need all of the comments, and not just some of them, I wouldn't worry about the MySQL part of this, which would simply be SELECT * FROM Table ORDER BY Table.date_posted DESC. You could use WHERE, as well, but based on your example, you will need all of the comments.

Fully working example here: IDEOne Demo

Fully working example's output:

3
2
    2.1
1
    1.2
    1.1
        1.1.1

In PHP, simply build a hash, where we store a comments as so parent_id => $commentobj...

<?php

    $comment_hash = [];

    foreach($comments as $comment) {
        if(!$comment_hash[$comment['parent_id']]) {
            $comment_hash[$comment['parent_id']] = [];
        }

        $comment_hash[$comment['parent_id']][] = $comment;
    }

?>

We know that primary comments have a parent_id of 0, so we iterate over them. I use a recursive function, since comments can respond to other comments infinitely, or at least three layers according to your notes.

Our recursive display function.

<?php

    function displayComments($comments, $comment_hash, $depth) {
        foreach($comments as $comment) {
            print(str_repeat(' ', 4 * $depth));
            print($comment['comment_text']);
            print("\n");

            displayComments($comment_hash[$comment['id']], $comment_hash, $depth + 1);
        }
    }

?>

Run the recursive display function on top-level (i.e., parentid=0) comments...

<?php

    displayComments($comment_hash[0], $comment_hash, 0);

?>
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
  • thank you but i need to do that by one MySQL query b/c i need paging them like my order on PHP – DeLe Feb 25 '20 at 02:16
  • 1
    @Dele : Hey, thanks for looking. Did you even run the code up above? Your post was saying you need data like "1(newline)2(newline, indent)3", and your comment here is "It's now how I wanted it." Can you please be more descriptive? – HoldOffHunger Feb 25 '20 at 14:59
  • i want using one MySQL query to get ordered comments like my ordered example (date_posted order by DESC in each comment level) – DeLe Feb 25 '20 at 15:42
  • @Dele: I understand that. How does my code not produce that? – HoldOffHunger Feb 25 '20 at 15:50
  • thank, but I just need MySQL query to get data like my ordered (b/c i want paging them in limit and offset) – DeLe Feb 25 '20 at 15:57
  • @DeLe: I copied and pasted the code above in a sandbox, why don't you take a look at the output? https://ideone.com/DXxx7m – HoldOffHunger Feb 25 '20 at 16:38
  • yes, output is correct but i need order in mysql b/c i want paging in limit and offset. php is just only show data (not order) – DeLe Feb 26 '20 at 03:24
  • 1
    @DeLe: It seems like you have multiple times added or removed requests about `paging` from your question. That is really an entirely different issue. You should ask one question for every problem you are having, this makes it easier for answerers. – HoldOffHunger Feb 26 '20 at 04:10
0

It is not really efficient but this gets what you are wanting:

SELECT * 
FROM stuff
ORDER BY SUBSTRING_INDEX(comment_text, '.', 1), 
         SUBSTRING_INDEX(comment_text, '.', 2), 
         comment_text
derek.wolfe
  • 1,086
  • 6
  • 11
  • thank you, but we should not using comment_text, b/c it is my example. In real that can be everything? – DeLe Mar 01 '20 at 16:00
0

SQL

SELECT * FROM tbl
ORDER BY
CAST(SUBSTRING_INDEX(comment_text, '.', 1) AS UNSIGNED) DESC, -- Up to first dot (if any)
CASE WHEN LOCATE('.', comment_text) = 0 -- No dots
     THEN 4294967295                    -- Place higher when no dots
     WHEN CHAR_LENGTH(comment_text) - CHAR_LENGTH(REPLACE(comment_text, '.', '')) = 1
     THEN CAST(SUBSTRING_INDEX(comment_text, '.', -1) AS UNSIGNED) -- Number after dot
     ELSE CAST(SUBSTRING(SUBSTRING_INDEX(comment_text, '.', -2),   -- Between 2 dots
                         1,
                         LOCATE('.', SUBSTRING_INDEX(comment_text, '.', -2))) AS UNSIGNED)
END DESC,
CASE WHEN CHAR_LENGTH(comment_text) - CHAR_LENGTH(REPLACE(comment_text, '.', '')) < 2
     THEN 4294967295                    -- Place higher when less than 2 dots
     ELSE CAST(SUBSTRING_INDEX(comment_text, '.', -1) AS UNSIGNED) -- After second dot
END DESC,
date_posted DESC;

Online Demo

Rextester demo: https://rextester.com/MPXMV17458 (Includes the example in the question + a further example with some repeated values and integers higher than 10).

Explanation

The query assumes there can be a maximum of two dots. There are three ORDER BY parts (other than the final date_posted ordering), representing the three possible integers in x.y.z. For the first two parts, if there is nothing beyond, the highest possible unsigned integer is picked - so x will be higher than x.y and x.y will be higher than x.y.z (or in reverse since the ordering is DESCending). The casting to unsigned integers ensures that the ordering will work beyond 9, since an alphabetical ordering would place 10 between 1 and 2.

Community
  • 1
  • 1
Steve Chambers
  • 37,270
  • 24
  • 156
  • 208
  • thank you but we should not using comment_text, b/c it is my example. In real that can be every thing? – DeLe Mar 01 '20 at 15:59
  • Sorry, not exactly sure what you mean. Do you mean there could be more than two dots or are you saying the table has other columns that aren't mentioned in your question? – Steve Chambers Mar 01 '20 at 21:59
  • it mean, you are using comment_text column to make order, but that column is user input, that can be everything text – DeLe Mar 02 '20 at 01:10
  • If the column is user input, would suggest adding some validation to ensure it complies with the expected format. Or if "everything" really *should* be allowed, are any there any expectations on how other formats are sorted - e.g. put anything that isn't in the expected format at the end? – Steve Chambers Mar 02 '20 at 08:56