【问题标题】:Explanation of self-joins自联接的解释
【发布时间】:2011-01-28 08:20:55
【问题描述】:

我不明白自加入的必要性。谁能给我解释一下?

一个简单的例子会很有帮助。

【问题讨论】:

    标签: sql self-join


    【解决方案1】:

    您可以将自联接视为两个相同的表。但是在规范化中,您不能创建表的两个副本,因此您只需模拟两个具有自联接的表。

    假设你有两个表:

    emp1

    Id Name Boss_id            
    1   ABC   3                   
    2   DEF   1                   
    3   XYZ   2                   
    

    emp2

    Id Name Boss_id            
    1   ABC   3                   
    2   DEF   1                   
    3   XYZ   2                   
    

    现在,如果您想获取每个员工的姓名及其老板的姓名:

    select c1.Name , c2.Name As Boss
    from emp1 c1
        inner join emp2 c2 on c1.Boss_id = c2.Id
    

    会输出下表:

    Name  Boss
    ABC   XYZ
    DEF   ABC
    XYZ   DEF
    

    【讨论】:

    • 在这个例子中,我不知道谁是老板。虽然 exp 很好而且很容易理解。
    • left join 我认为最好不要把没有老板的员工(或老板)排除在外;顶级狗!
    • 应该来自 emp c1 而不是 emp1 c1 ? @pointlesspolitics
    【解决方案2】:

    当您有一个引用自身的表时,这很常见。示例:一个员工表,其中每个员工都可以有一个经理,并且您想要列出所有员工及其经理的姓名。

    SELECT e.name, m.name
    FROM employees e LEFT OUTER JOIN employees m
    ON e.manager = m.id
    

    【讨论】:

      【解决方案3】:

      自联接是表与自身的联接。

      一个常见的用例是当表存储实体(记录)时,它们之间具有层次关系。例如,包含人员信息(姓名、出生日期、地址...)的表格并包括其中包含父亲(和/或母亲)的 ID 的列。然后用一个像

      这样的小查询
      SELECT Child.ID, Child.Name, Child.PhoneNumber, Father.Name, Father.PhoneNumber
      FROM myTableOfPersons As Child
      LEFT OUTER JOIN  myTableOfPersons As Father ON Child.FatherId = Father.ID
      WHERE Child.City = 'Chicago'  -- Or some other condition or none
      

      我们可以在同一个查询中获取有关孩子和父亲(以及母亲,以及第二次自我加入等,甚至是祖父母等)的信息。

      【讨论】:

        【解决方案4】:

        假设你有一张桌子users,设置如下:

        • 用户 ID
        • 用户名
        • 用户的经理 ID

        在这种情况下,如果您想在一个查询中同时提取用户信息经理信息,您可以这样做:

        SELECT users.user_id, users.user_name, managers.user_id AS manager_id, managers.user_name AS manager_name INNER JOIN users AS manager ON users.manager_id=manager.user_id
        

        【讨论】:

          【解决方案5】:

          想象一个名为Employee 的表,如下所述。所有员工都有一个经理,这也是一个员工(也许除了 CEO,其 manager_id 将为空)

          Table (Employee): 
          
          int id,
          varchar name,
          int manager_id
          

          然后您可以使用以下选择来查找所有员工及其经理:

          select e1.name, e2.name as ManagerName
          from Employee e1, Employee e2 where
          where e1.manager_id = e2.id
          

          【讨论】:

            【解决方案6】:

            如果您的表是自引用的,它们会很有用。例如,对于页面表,每个页面可能有一个nextprevious 链接。这些将是同一个表中其他页面的 ID。如果在某个时候您想要获得三个连续页面,您可以在 nextprevious 列上与同一个表的 id 列进行两次自联接。

            【讨论】:

              【解决方案7】:

              如果表无法引用自身,我们就必须为层次结构级别创建与层次结构中的层数一样多的表。但由于该功能可用,您将表连接到自身,sql 将其视为两个单独的表,因此所有内容都很好地存储在一个地方。

              【讨论】:

              • 但现在你(希望)明白如果自引用不可用会发生什么。
              【解决方案8】:

              除了上面提到的答案(解释得很好)之外,我想添加一个示例,以便可以轻松地展示 Self Join 的使用。 假设您有一个名为 CUSTOMERS 的表,它具有以下属性: 客户 ID、客户姓名、联系人姓名、城市、国家/地区。 现在你想列出所有来自“同一个城市”的人。 您将不得不考虑该表的副本,以便我们可以在 CITY 的基础上加入它们。下面的查询将清楚地显示它的含义:

              SELECT A.CustomerName AS CustomerName1, B.CustomerName AS CustomerName2, 
              A.City
              FROM Customers A, Customers B
              WHERE A.CustomerID <> B.CustomerID
              AND A.City = B.City 
              ORDER BY A.City;
              

              【讨论】:

              • +1 这个答案非常重要,因为在 SO 上有很多 SQL 问题的答案是“使用自联接”,而人们在没有明确的答案时往往看不到(分层)自我引用。
              • 即使这是从 w3schools 复制的意大利面,我认为上面的答案并不能解释自联接,而是内联接,这是不同的。
              【解决方案9】:

              这里有很多正确的答案,但也有一个同样正确的变体。您可以将连接条件放在连接语句中,而不是 WHERE 子句中。

              SELECT e1.emp_id AS 'Emp_ID'
                , e1.emp_name AS 'Emp_Name'
                , e2.emp_id AS 'Manager_ID'
                , e2.emp_name AS 'Manager_Name'
              FROM Employee e1 RIGHT JOIN Employee e2 ON e1.emp_id = e2.emp_id
              

              请记住,有时您需要 e1.manager_id > e2.id

              了解这两种情况的好处是有时您有大量 WHERE 或 JOIN 条件,并且您希望将自联接条件放在另一个子句中以保持代码可读性。

              没有人解决当员工没有经理时会发生什么。嗯?它们不包含在结果集中。如果您想包括没有经理但又不想返回错误组合的员工怎么办?

              试试这只小狗;

              SELECT e1.emp_id AS 'Emp_ID'
                 , e1.emp_name AS 'Emp_Name'
                 , e2.emp_id AS 'Manager_ID'
                 , e2.emp_name AS 'Manager_Name'
              FROM Employee e1 LEFT JOIN Employee e2 
                 ON e1.emp_id = e2.emp_id
                 AND e1.emp_name = e2.emp_name
                 AND e1.every_other_matching_column = e2.every_other_matching_column
              

              【讨论】:

              • 嗯,在小狗中,你为什么加入“大于”而不是“等于”?
              • 嗨。我已经看到一些示例使用“FROM xxx, yyy WHERE”和其他一些“FROM xxx JOIN yyy WHERE”。请您解释一下区别吗?
              • @Skan 这是一个非常好的问题。简短的回答是它是旧的速记方法,将被弃用。十多年前我在学校里用过它,但在实践中很少看到它。这是我能找到的最简洁的描述:bidn.com/blogs/KathiKellenberger/sql-server/2875/…
              【解决方案10】:

              一个用例是检查数据库中的重复记录。

              SELECT A.Id FROM My_Bookings A, My_Bookings B
              WHERE A.Name = B.Name
              AND A.Date = B.Date
              AND A.Id != B.Id
              

              【讨论】:

              • 使用 GROUP BY 和 HAVING 子句查找重复项要快得多。 SELECT name, email, COUNT() FROM My_Bookings GROUP BY name, date HAVING COUNT() > 1
              • @GeorgeK 是的。我想这仅对模糊匹配是必要的(除了按 TRIM(LOWER(Name)) 分组),而不是严格相等。
              【解决方案11】:

              当您必须自行评估表的数据时,自连接很有用。这意味着它将关联同一个表中的行。

              Syntax: SELECT * FROM TABLE t1, TABLE t2 WHERE t1.columnName = t2.columnName

              例如,我们要查找初始指定等于当前指定的员工的姓名。我们可以通过以下方式使用 self join 来解决这个问题。

              SELECT NAME FROM Employee e1, Employee e2 WHERE e1.intialDesignationId = e2.currentDesignationId
              

              【讨论】:

                【解决方案12】:

                它相当于一个链表/树的数据库,其中一行包含对另一行的某种容量的引用。

                【讨论】:

                • 实际上,考虑到不止一行可以引用“父级”,它也可以是一棵树,例如经常引用的员工->经理示例。
                • 我只是想进行一个简单的类比,但是是的,一棵树也可以工作。
                【解决方案13】:

                这里是自加入的通俗解释。自联接不是不同类型的联接。如果您了解其他类型的连接(内连接、外连接和交叉连接),那么自连接应该是直截了当的。在 INNER、OUTER 和 CROSS JOINS 中,您连接 2 个或更多不同的表。但是,在自联接中,您使用其加入同一个表。在这里,我们没有 2 个不同的表,而是使用表别名将同一个表视为不同的表。如果还不清楚,我建议观看以下 youtube 视频。

                Self Join with an example

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多