【问题标题】:Retrieving parent value from mysql self join从mysql self join中检索父值
【发布时间】:2010-10-01 22:51:24
【问题描述】:

我有一个包含字段的表格:

  • 身份证
  • 姓名
  • parent_id
  • grandparent_id

我要选择id、姓名、父母姓名、祖父母姓名。

我可以通过自加入来做到这一点吗?我基本上想检索 parent_id = id 的“名称”值并返回一行。

例子:

id     name       parent_id     grandparent_id
-----------------------------------------------
 1     Milton     NULL          NULL
 2     Year 3     1             NULL
 3     Class A    2             1

所以我想选择第 3 行 (id = 3),但不是只返回 parent_id 和 grandparent_id,而是希望查询根据它们的 id 返回这些记录的名称。我可以创建一个复合字段,比如称为 parent_id_name 和 grandparent_id_name 吗?

我很确定我正在做的事情可以通过自联接或子查询来实现,但是到目前为止我尝试过的所有代码都无法正常工作。任何帮助将不胜感激。

【问题讨论】:

    标签: sql mysql hierarchical-data


    【解决方案1】:

    这是您要求的查询:

    # By using LEFT JOINs you will be able to read any record,
    # even one with missing parent/grand-parent...
    SELECT
        child.id,
        child.name,
        parent.id,
        parent.name,
        gparent.id,
        gparent.name
    FROM
        some_table child
        LEFT JOIN some_table parent ON
            parent.id = child.parent_id
        LEFT JOIN some_table gparent ON
            gparent.id = child.grandparent_id
    WHERE
        child.id = 3
    

    但我还要补充一点,拥有一个字段 grandparent_id 的冗余对我来说并不正确......

    你的桌子应该是:

    id     name       parent_id
     1     Milton     NULL
     2     Year 3     1
     3     Class A    2
    

    请注意,如果我知道 12 的父级,我不需要在记录中再次重复相同的信息 3...

    在最后一种情况下,您的选择可能是这样的:

    SELECT
        child.id,
        child.name,
        parent.id,
        parent.name,
        gparent.id,
        gparent.name
    FROM
        some_table child
        LEFT JOIN some_table parent ON
            parent.id = child.parent_id
        LEFT JOIN some_table gparent ON
            gparent.id = parent.parent_id  -- See the difference?
    WHERE
        child.id = 3
    

    查询的工作方式相同,您还将拥有更多“规范化”的数据库。

    编辑:这是非常基本的东西,但我想它与这个答案有关......

    不应使用这种非规范化(即在同一记录上同时具有 parent_idgrandparent_id),因为它允许数据库不一致。

    例如,假设插入了一条新记录:

    id     name            parent_id   grandparent_id
     1     Milton          NULL        NULL
     2     Year 3          1           NULL
     3     Class A         2           1
     4     Invalid Rec     2           3
    

    这没有任何意义,对吧?记录 4 声明 3 是它的祖父母。因此,3 应该是记录 2 的父项。但这不是3 本身记录的内容。哪个记录是正确的?

    您可能认为这是一个奇怪的错误,并且您的数据库永远不会变成这样。但我的经验并非如此——如果可能发生错误,它最终会发生。应该避免非规范化,不仅仅是因为某些数据库专家这么说,而是因为它确实增加了不一致性,并使维护变得更加困难。

    当然,非规范化数据库可能会更快。但是,根据经验,您应该您的系统准备好投入生产之后考虑性能,并且您通过一些自动化或经验测试的方式感知到之后,即存在瓶颈。相信我,我之前看到过更糟糕的设计选择被错误的性能预期证明是合理的......

    【讨论】:

    • 我倾向于同意。像我的回答一样,非规范化可以保存连接。
    • 之所以有parent和grandparent字段,是因为网上看,有人说递归(只是parent_id)效率不高,效率最高的方式(嵌套集)复杂。所以这种“血统”式的结构是一种快乐的媒介。就我而言,我知道最多只有 2 个层次结构。
    • 另外,只有parent_id,是否容易在层次结构中向下选择?例如如果我想获取记录 1 的所有子/孙记录,如何在“标准化”模型中实现?
    • @Dan Spencer:呃……完全一样?好的,您的问题中有一个问题 - 记录 1 没有父母。在这种情况下,您应该将 INNER JOIN 更改为 LEFT JOIN(或 LEFT OUTER JOIN,就像 Martin 所做的那样)。同样,请理解非规范化数据库很常见,但只有当性能成为问题时,IMO 才是合理的。很多时候,这是一个高昂的代价——因为非规范化允许错误,当错误可能发生时,它就会发生。我将编辑我的答案并更好地解释它。
    • @rsenna 感谢您的 cmets... 我现在已经按照您的建议更改了 db 结构,现在唯一的问题是如果没有孙子组,mysql 返回一个空结果 - 不是所有的时间我会有父母,孩子和孙子。有时它只是父、子组,甚至只是父。有没有办法让mysql优雅地失败并返回到它可以找到的任何级别?再次感谢您的所有帮助。
    【解决方案2】:
    SELECT t1.name, 
        MAX(CASE WHEN t2.id = t1.parent_id then t2.name end) as Parent,
        MAX(CASE WHEN t2.id = t1.grandparent_id then t2.name end) as GrandParent
    FROM your_table t1
    LEFT OUTER JOIN your_table t2 ON t2.id IN (t1.parent_id, t1.grandparent_id)
    WHERE t1.id = 3
    group by t1.id,  t1.name
    

    【讨论】:

      【解决方案3】:

      试试这个,这是给单亲的

      SELECT e.entity_name AS 'entity', 
             m.entity_name AS 'parent'
      FROM table_name AS e 
      LEFT OUTER JOIN table_name AS m ON e.entity_parent =m.entity_id
      

      或 检查以下链接:

      http://databases.about.com/od/sql/a/selfjoins.htm

      【讨论】:

        猜你喜欢
        • 2021-03-02
        • 2012-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多