递归自我联接


15

我有一张comments桌子,可以简化为:

comments
=======
id
user_id
text
parent_id

where parent_id可为空,但可能是其父注释的键。


现在,我该如何select评论所有后代?
评论可能会降低几个级别...

Answers:


16

众所周知,递归查询是分层查询,MySQL不支持。

但是,Oracle,Microsoft SQL Server,DB2和PostgreSQL等都支持它们。

如果您需要解决方法,则可以在此处找到动态的(因此可能有危险的)技巧:https : //stackoverflow.com/questions/8104187/mysql-hierarchical-queries

您还可以在这里找到有关如何使用分层模型(而不是邻接表)(即“ 父级”列)存储分层数据的讨论:https : //stackoverflow.com/questions/192220/what-is-the-most-efficiency-优雅的方法,将一张平桌子解析成一棵树/

祝好运!


我不知道您第二个链接中的解决方案可能有多危险。你能解释一下吗?否则,欢迎光临本站!
dezso 2013年

3
@dezso:Quassnoi是查询本身的技巧,“ *不是升级安全的*,因为MySQL并未明确定义会话变量的行为。但是,这是在查询中及时处理邻接列表的唯一方法。” “危险”一词可能太强了(可能会不稳定?),但我更倾向于谨慎行事(而且,我在Oracle中比MySQL更了解,所以我要格外谨慎。)谢谢您的欢迎,顺便说一句!我长期以来一直是SE网络的潜伏者,因此我决定该是时候还点钱了。
Valmoer 2013年

2
mysql 8.0现在支持WITH [RECURSIVE]语法。dev.mysql.com/doc/refman/8.0/en/with.html
ClearCrescendo

6

此表设计是由Bill Karwin(在他的“ SQL Antipatterns Strike Back”演示文稿中从幻灯片48凝视)描述的一个SQL反模式“天真树” 。这种设计的问题特别是难以获得节点的所有后代(或父代)。由于您使用的是MySQL,因此无法使用其他RDBMS中存在的公用表表达式(WITH语句及其RECURSIVE修饰符)。

您剩下的是:

  • 使用分层数据结构的替代实现(对此问题的答案可能是对此的一个很好的参考)
  • 建立具有深度限制的自联接查询。对于深度= 5,您可以在以下行中使用:

    SELECT *
    FROM comments AS c1
      JOIN comments AS c2 ON (c2.parent_id = c1.id)
      JOIN comments AS c3 ON (c3.parent_id = c2.id)
      JOIN comments AS c4 ON (c4.parent_id = c3.id)
      JOIN comments AS c5 ON (c5.parent_id = c4.id)
  • 使用支持WITH RECURSIVE的RDBMS(尽管大多数人不太可能选择这种方式)


2
我在这里不同意比尔·卡尔文。邻接模型不是反模式。使用支持递归查询的现代DBMS(Oracle已经支持递归查询20多年了),这种模型非常有效地进行检索更新。
a_horse_with_no_name 2013年

5

MySQL不支持您需要的递归查询。

我前一段时间做的是编写存储过程,该存储过程提供了这样做的模型。

我将向您提供我以前的帖子链接,而不是重新发明轮子:

简而言之,我使用队列处理进行了存储过程的预排序树遍历

  • GetParentIDByID
  • GetAncestry
  • GetFamilyTree

所有孩子的父母(例如GetFamilyTree存储过程)

  • STEP01)从parent_id队列中开始
  • STEP02)将下一个parent_id作为当前队列
  • STEP03)排队所有id具有当前值的值parent_id
  • STEP04)打印或收集评论
  • STEP05)如果队列不为空,则转到 STEP02
  • STEP06)完成!!!

所有父级的子级(例如GetAncestry存储过程)

  • STEP01)从id队列中开始
  • STEP02)将下一个id作为当前队列
  • STEP03)parent_id取当前值id
  • STEP04)打印或收集评论
  • STEP05)如果队列不为空,则转到 STEP02
  • STEP06)完成!!!

请查看我其他帖子中的存储过程,以查看实现。

试试看 !!!


2
SELECT  group_concat(@id :=
        (
        SELECT  id
        FROM    comments
        WHERE   parent_id = @id
        )) AS comment
FROM    (
        SELECT  @id := 1
        ) vars
STRAIGHT_JOIN
        comments
WHERE   @id IS NOT NULL

小提琴

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.