使用闭包表(MySQL)分层分层数据

时间:2013-05-18 00:52:22

标签: mysql sql hierarchical-data transitive-closure-table

我正在尝试查询按score排序的分页层次评论。 score是一个整数,注释表有一个自引用的parent_id列。

每个页面都应包含至少一个根评论,后跟其子项。如果数据集中只有一个根注释,则只返回一个页面。

因此,考虑到comments表中的以下数据:

+----+-------+-----------+
| id | score | parent_id |
+----+-------+-----------+
|  1 |    10 |      NULL |
|  2 |     5 |      NULL |
|  3 |     0 |         1 |
|  4 |     6 |         2 |
|  5 |     0 |      NULL |
|  6 |    30 |         1 |
|  7 |     1 |         3 |
|  8 |     0 |         4 |
|  9 |    50 |      NULL |
| 10 |     2 |         2 |
+----+-------+-----------+

我希望能够SELECT * FROM comments ... LIMIT 4 OFFSET 0 Page 1 成为:

+----+-------+-----------+
| id | score | parent_id |
+----+-------+-----------+
|  9 |    50 |      NULL |
|  1 |    10 |      NULL |
|  6 |    30 |         1 |
|  3 |     0 |         1 |
+----+-------+-----------+

Page 2

+----+-------+-----------+
| id | score | parent_id |
+----+-------+-----------+
|  2 |     5 |      NULL |
|  4 |     6 |         2 |
| 10 |     2 |         2 |
|  5 |     0 |      NULL |
+----+-------+-----------+

并且 为空,因为没有根评论。

我正在使用支持闭包表,如Bill Karwin所述,因为可以使用任何注释作为根注释独立查看注释子树,这似乎是最佳解决方案。

相关表格的结构和示例数据如下:

CREATE TABLE `comments` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `score` int(11) NOT NULL,
  `parent_id` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `comments` (`id`, `score`, `parent_id`)
VALUES
  (1,10,NULL),
  (2,5,NULL),
  (3,0,1),
  (4,6,2),
  (5,0,NULL),
  (6,30,1),
  (7,1,3),
  (8,0,4),
  (9,50,NULL),
  (10,2,2);

CREATE TABLE `comments_closure` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `ancestor` int(11) unsigned NOT NULL,
  `descendant` int(11) unsigned NOT NULL,
  `depth` int(11) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `comments_closure` (`id`, `ancestor`, `descendant`, `depth`)
VALUES
  (1,1,0), (1,3,1), (1,6,1), (1,7,2),
  (2,2,0), (2,4,1), (2,10,1), (2,8,2),
  (3,3,0), (3,7,1),
  (4,4,0), (4,8,1),
  (5,5,0),
  (6,6,0),
  (7,7,0),
  (8,8,0),
  (9,9,0),
  (10,10,0);

1 个答案:

答案 0 :(得分:0)

这适用于1级深度查询:

SELECT @id_multiplier := MAX(POW(10, -(length(id) + 1))) FROM comments;
SELECT @score_multiplier := @id_multiplier * MAX(POW(10, -(length(score) + 1))) FROM comments;
SELECT c1.id
     , c1.score
     , c1.parent_id 
FROM comments c1
LEFT JOIN comments c2 
   ON c1.parent_id = c2.id
WHERE c1.parent_id IS NULL 
   OR c1.parent_id IN 
     (SELECT id FROM comments WHERE parent_id IS NULL)
ORDER BY 
   IF(ISNULL(c1.parent_id)
    , c1.score
    , c2.score + (c1.parent_id * @id_multiplier) - (1-(c1.score * @score_multiplier))
    ) DESC