递归自联接

Jos*_*osh 16 mysql join foreign-key select

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

comments
=======
id
user_id
text
parent_id
Run Code Online (Sandbox Code Playgroud)

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


现在,我如何才能select对特定评论的所有后代?
评论可能低了好几层……

Val*_*oer 17

MySQL 8现在支持分层查询,因为这些递归查询是已知的。

旧答案

或者,您可以在此处找到动态(因此具有潜在危险)的技巧:https : //stackoverflow.com/questions/8104187/mysql-hierarchical-queries

您还可以在此处找到有关如何使用其他模型而不是使用邻接列表(即列)存储分层数据的讨论:https : //stackoverflow.com/questions/192220/what-is-the-most-efficient-优雅的方式来解析一个平面表到一棵树/

祝你好运!

  • @dezso:引用查询的制作者 Quassnoi “_*这不是升级安全的*,因为 MySQL 没有明确定义会话变量行为。但是,这是在查询中及时处理邻接列表的唯一方法_。 ” 危险这个词可能太强了(_可能不稳定_?)但我更愿意在谨慎方面犯错(另外,我对 Oracle 的了解比对 MySQL 的了解更多,所以我想格外小心)。顺便说一下,感谢您的欢迎!我一直潜伏在 SE 网络中,我决定是时候回报一下了。 (3认同)
  • mysql 8.0 现在支持 WITH [RECURSIVE] 语法。https://dev.mysql.com/doc/refman/8.0/en/with.html (3认同)

red*_*guy 7

这个表设计是 Bill Karwin 所描述的 SQL 反模式“朴素树”(从他的SQL 反模式反击演示中的幻灯片 48 开始)。这种设计的具体问题是难以获取节点的所有后代(或父节点)。由于您使用的是 MySQL,因此您不能使用其他 RDBMS 中存在的公共表表达式(WITH 语句及其 RECURSIVE 修饰符)。

你剩下的是:

  • 使用分层数据结构的替代实现(这个问题的答案可能是一个很好的参考)
  • 构建具有深度限制的自连接查询。对于depth = 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)
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用支持 WITH RECURSIVE 的 RDBMS(尽管这很可能不是大多数人的选择)

  • 我在这里不同意比尔卡文的观点。邻接模型**不是**反模式。使用支持递归查询的现代 DBMS(Oracle 支持此功能已有 20 多年),这样的模型非常有效地检索*和*更新。 (2认同)

Rol*_*DBA 5

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

不久前我所做的是编写存储过程来提供这样做的模型。

我不会重新发明轮子,而是为您提供我过去关于此帖子的链接:

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

  • GetParentIDByID
  • GetAncestry
  • GetFamilyTree

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

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

子到所有父(如 GetAncestry 存储过程)

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

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

试一试 !!!