2

我想设置一个看起来像这样的多级评论:

1 this is the first comment (with 2 replies)
    1.1 first reply (with 2 replies)
        1.1.1 first reply of first
        1.1.2 second reply of first
    1.2 second reply (with 1 reply)
        1.2.1 first reply of second
2 this is the second comment (with 0 reply)
3 this is the third comment (with 0 reply)

我刚刚发现了 drupal 方法,它对查询中的数据进行排序非常酷,但我不知道如何计算评论的回复数。我不知道是否可以在 1 个 sql 查询中做到这一点。

这是我的表结构:

CREATE TABLE IF NOT EXISTS `mb_post` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `topic_id` int(11) NOT NULL,
  `user` int(11) NOT NULL,
  `message` text COLLATE utf8_unicode_ci NOT NULL,
  `date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `level` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `parent_post_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=0 ;

--
-- Dumping data for table `mb_post`
--

INSERT INTO `mb_post` (`id`, `topic_id`, `user`, `message`, `date`, `level`, `parent_post_id`) VALUES
(1, 99, 10, 'this is the first comment', '2013-10-18 11:00:00', '1', 0),
(2, 99, 20, 'this is the second comment', '2013-10-18 11:10:00', '2', 0),
(3, 99, 30, 'this is the third comment', '2013-10-18 11:20:00', '3', 0),
(4, 99, 20, 'first reply', '2013-10-18 14:30:00', '1.1', 1),
(5, 99, 50, 'first reply of first', '2013-10-18 15:00:00', '1.1.1', 4),
(6, 99, 70, 'second reply of first', '2013-10-18 15:30:00', '1.1.2', 4),
(7, 99, 80, 'second reply', '2013-10-18 14:45:00', '2.1', 2),
(8, 99, 90, 'first reply of second', '2013-10-18 15:15:00', '2.1.1', 7);

如您所见,我level在 varchar 中有列(我找不到其他使用多小数的方法)。

有没有办法计算评论的回复数量?

非常感谢!

编辑:想要的结果:

id  | topic_id  | user  | message                   | date                  | level | parent_post_id    | count
1   | 99        | 10    | this is the first comment | 2013-10-18 11:00:00   | 1     | 0                 | 2
4   | 99        | 20    | first reply               | 2013-10-18 14:30:00   | 1.1   | 1                 | 2
5   | 99        | 50    | first reply of first      | 2013-10-18 15:00:00   | 1.1.1 | 4                 | 0
6   | 99        | 70    | second reply of first     | 2013-10-18 15:30:00   | 1.1.2 | 4                 | 0
2   | 99        | 20    | this is the second comment| 2013-10-18 11:10:00   | 2     | 0                 | 1
7   | 99        | 80    | second reply              | 2013-10-18 14:45:00   | 2.1   | 2                 | 1
8   | 99        | 90    | first reply of second     | 2013-10-18 15:15:00   | 2.1.1 | 7                 | 0
3   | 99        | 30    | this is the third comment | 2013-10-18 11:20:00   | 3     | 0                 | 0

数据应按级别排序,计数列应为其直接子级数的结果。对于ie:评论1应该计算所有评论的级别1.1, 1.2, 1.3而不是1.1.1

4

2 回答 2

1
SELECT p.id, COUNT(c.id), GROUP_CONCAT(c.id) 
FROM mb_post p LEFT OUTER JOIN mb_post c ON c.level LIKE CONCAT(p.level, '.%') 
GROUP BY p.id;

+----+-------------+--------------------+
| id | COUNT(c.id) | GROUP_CONCAT(c.id) |
+----+-------------+--------------------+
|  1 |           3 | 4,5,6              |
|  2 |           2 | 7,8                |
|  3 |           2 | 7,8                |
|  4 |           2 | 5,6                |
|  5 |           0 | NULL               |
|  6 |           0 | NULL               |
|  7 |           1 | 8                  |
|  8 |           0 | NULL               |
+----+-------------+--------------------+

如果您想要一个特定评论的回复计数,只需WHERE p.id = ?在 GROUP BY 之前使用。


只计算直属子代,而不是所有后代:

SELECT p.id, COUNT(c.id), GROUP_CONCAT(c.id)
FROM mb_post p LEFT OUTER JOIN mb_post c 
  ON c.level RLIKE CONCAT('^', p.level, '[.][[:digit:]]+$')
GROUP BY p.id;

+----+-------------+--------------------+
| id | COUNT(c.id) | GROUP_CONCAT(c.id) |
+----+-------------+--------------------+
|  1 |           1 | 4                  |
|  2 |           1 | 7                  |
|  3 |           1 | 7                  |
|  4 |           2 | 5,6                |
|  5 |           0 | NULL               |
|  6 |           0 | NULL               |
|  7 |           1 | 8                  |
|  8 |           0 | NULL               |
+----+-------------+--------------------+

回复您的评论:

您可以使用 parent_post_id,除非我对其进行了测试,否则您的 parent_post_id 与列中显示的层次结构不一致level。例如,#7 是 #2 inlevel的子代,但它是 parent_post_id 中#1 的子代:

SELECT p.id, COUNT(c.id), GROUP_CONCAT(c.id)
FROM mb_post p LEFT OUTER JOIN mb_post c ON c.parent_post_id = p.id
GROUP BY p.id;

+----+-------------+--------------------+
| id | COUNT(c.id) | GROUP_CONCAT(c.id) |
+----+-------------+--------------------+
|  1 |           2 | 7,4                |
|  2 |           0 | NULL               |
|  3 |           0 | NULL               |
|  4 |           2 | 5,6                |
|  5 |           0 | NULL               |
|  6 |           0 | NULL               |
|  7 |           1 | 8                  |
|  8 |           0 | NULL               |
+----+-------------+--------------------+

这是冗余存储信息的风险——它可能会失去同步。

于 2013-10-18T18:16:05.933 回答
1

如果我理解正确,您希望第一个表作为输出。这不是一个真正的表格,因为列没有排列。这意味着,字符串连接。

然后,关键是要正确完成缩进并计算回复的数量,如下所示:

select concat(space(char_length(level)),
              level, ' ', message,
              coalesce(concat(' (with ', r.NumReplies, ' replies)'), ''))
from mb_post p left outer join
     (select parent_post_id, count(*) as NumReplies
      from mb_post
      group by parent_post_id
     ) r
     on p.id = r.parent_post_id
order by level;

这个SQLFiddle很好地了解了结果。但是,初始缩进不起作用(尽管有space()),我认为这是 SQLFiddle 的“功能”。

于 2013-10-18T18:29:06.100 回答