我正在为 MySQL 和 PHP 开发线程评论系统。我选择了 Closure Table 模式,但我有问题。我需要查询(查询)来获取整棵树。怎么做?我对此进行了很多搜索,但找不到任何最佳选择。如果您有更好的主题评论,请告诉我。谢谢你的回复。
3 回答
如果这个用例在您的应用程序中很常见,只需存储根 id(树根的 id。这可以是这些评论所属的帖子的 id)。现在,当您需要获取整个评论树时,您只需要:
SELECT * FROM comments WHERE root_id = <root_id>
或您的设计的等效查询。如果您提供表定义,我可以帮助您解决特定查询。
更新:
$dbh = new PDO($dsn, $user, $password);
$sql = "SELECT A.*, GROUP_CONCAT(descendant) as descendants FROM Comments AS A INNER JOIN Paths AS B ON A.id = B.ancestor WHERE A.item = ? GROUP BY A.id";
$stmt = $dbh->prepare($sql);
$stmt->execute(array($item));
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
$adjacency_list = array(); $comments = array();
foreach($data as $row) {
$comments[$row['id']] = $row;
$descendants = explode(',', $row['descendants']);
$adjacency_list[$row['id']] = $descendants;
}
echo '<UL>';
foreach($adjacency_list[$item] as $top_level_comment) {
printTree($top_level_comment, $adjacency_list[$top_level_comment]);
}
echo '</UL>';
function printTree($node, $descendants) {
echo '<LI>'.$node;
if(sizeof($descendants) > 0) {
echo '<UL>';
foreach($descendants as $descendant) {
$d = array();
if(!empty($adjacency_list[$descendant])) $d = $adjacency_list[$descendant];
printTree($descendant, $adjacency_list[$descendant]);
}
echo '</UL>';
}
echo '</LI>';
}
另一方面,嵌套集模型不是评论系统的好解决方案,因为插入和更新量很高。如果您的数据很少更新,这是一个有效的解决方案。
这是我到目前为止所拥有的:
SELECT `Comments`.* FROM `Comments`
LEFT JOIN `TreePaths` ON `Comments`.`iD` = `TreePaths`.`descendant`
WHERE `TreePaths`.`ancestor` = <Root ID>
这会将给定树的所有节点从关闭表中拉出,但目前它们的排序不正确。如果我弄清楚最后一部分,我会更新这篇文章。以编程方式,您可以使用结果集中的信息获得正确的顺序,但我希望它在结果中是正确的。
我遇到了您的问题,因为我也在查看闭包表。我认为如果您遵循 Bill Karwin 在他的“Sql 反模式”一书中关于闭包表的建议,您的问题就可以解决:
为树中共享祖先/后代关系的每一对节点在此表中存储一行, 即使它们在树中被多个级别分隔。
(我的重点)
因此,一个查询parentId
将一次性生成父级的所有子级。(在 SQL Server 中,这可以通过递归查询来完成)。
和:
您可以改进 Closure Table 以更轻松地查询直接父节点或子节点。将 TreePaths.path_length 属性添加到 Closure Table 设计中。
即:将项目之间的距离存储在闭包表中。
我想你会很喜欢他的例子。这也与评论线程有关。