2013-05-18 40 views
4

我想查詢按score排序的分頁分層註釋。 score是一個整數,評論表有一個自引用parent_id列。使用閉包分頁分層數據(MySQL)

每個頁面應該有至少有一個根註釋,其次是其子女。如果數據集中只有一個根註釋,則只返回一個頁面。

因此,鑑於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並有是:

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

Page 2 be:

+----+-------+-----------+ 
| 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); 

回答

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