【问题标题】:FULL OUTER JOIN with SQLite使用 SQLite 进行全外连接
【发布时间】:2010-12-27 18:03:49
【问题描述】:

SQLite 只有 INNER 和 LEFT JOIN。

有没有办法使用 SQLite 进行 FULL OUTER JOIN?

【问题讨论】:

    标签: sql sqlite join full-outer-join


    【解决方案1】:

    是的,请参阅Wikipedia 上的示例。

    SELECT employee.*, department.*
    FROM   employee 
           LEFT JOIN department 
              ON employee.DepartmentID = department.DepartmentID
    UNION ALL
    SELECT employee.*, department.*
    FROM   department
           LEFT JOIN employee
              ON employee.DepartmentID = department.DepartmentID
    WHERE  employee.DepartmentID IS NULL
    

    【讨论】:

    • 维基百科示例正在形成三个查询的UNION,而您的示例只有两个。你能解释一下区别吗?
    • @GrahamBorland:我的答案中的代码取自维基百科。我认为维基百科自从我发布以来已经被编辑过。不同之处在于它们使用内部连接和两个选择来获取不匹配的记录(每个表一个)。我的答案中的查询将内部联接和其中一个选择组合成左联接。与 (A+B)+(C) 相比,它是 (A)+(B)+(C)。
    • 这仍然是最新的 SQLite (3.7.x) 的方式还是可以使用标准的 OUTER JOIN?
    • 如果您使用 UNION ALL,重要的是两个选择之一排除“内部连接”行,例如显示的 WHERE 条件。或者,您可以使用普通的 UNION 并使其重复消除避免重复“内部连接”行。
    • @01es 根据current SQLite doc pageFULLRIGHT 仍然不是有效的OUTER JOIN 运算符。
    【解决方案2】:

    根据 Jonathan Leffler 在 Mark Byers 的 answer 中的评论,这里有一个替代答案,它使用 UNION 而不是 UNION ALL

    SELECT * FROM table_name_1 LEFT OUTER JOIN table_name_2 ON id_1 = id_2
    UNION
    SELECT * FROM table_name_2 LEFT OUTER JOIN table_name_1 ON id_1 = id_2
    

    编辑: 上面 SQLite 示例的原始来源以及可以从中找到更多 SQLite 示例的位置是 http://sqlite.awardspace.info/syntax/sqlitepg06.htm,但现在该站点似乎返回了 404 Not Found 错误。

    【讨论】:

    • 我认为 WHERE employee.DepartmentID IS NULL 是必须的,虽然你不介意得到重复行
    • 如果我没记错的话,这应该是一样的结果,UNION会匹配重复的记录,但是效率不如UNION ALL。
    • UNION (without ALL) 删除重复行
    • 如果列具有相同的名称,SELECT * 不会产生正确的输出。 SELECT table_name_1.*, table_name_2.* 为我工作。
    • 谢谢@Wolf。我已经编辑了我的答案,声明链接的网站不再存在。
    【解决方案3】:

    对于人们来说,寻找一个模拟 Distinct Full Outer Join 的答案: 由于 SQLite 既不支持完全外连接,也不支持右连接,因此我不得不模拟一个不同的完全外连接/倒置内连接(不管你怎么称呼它)。 下面的维恩图显示了预期的输出:


    为了接收这个预期的输出,我结合了两个 Left Join 子句(这个例子是指两个相同的构建表,但数据部分不同。我只想输出出现在表 A 中的数据或在表 B) 中。

    SELECT A.flightNumber, A.offblockTime, A.airspaceCount, A.departure, A.arrival FROM D2flights A
        LEFT JOIN D1flights B
            ON A.flightNumber = B.flightNumber
            WHERE B.flightNumber IS NULL
    UNION 
    SELECT A.flightNumber, A.offblockTime, A.airspaceCount,  A.departure, A.arrival FROM D1flights A
        LEFT JOIN D2flights B
            ON A.flightNumber = B.flightNumber
            WHERE B.flightNumber IS NULL
    

    上面的 SQLite 语句在一个查询中返回预期的结果。看来,UNION 子句也确实通过 flightNumber 列对输出进行排序。

    代码已经用 SQLite 3.32.2 版本测试过

    【讨论】:

      【解决方案4】:

      我迟早会投入我的 2 美分。考虑下面 2 个简单的表 people1 和 people2:

         id   name age
      0   1    teo  59
      1   2   niko  57
      2   3  maria  54 
      
      
          id   name weight
      0   1    teo    186
      1   2  maria    125
      2   3    evi    108
      

      首先,我们创建一个临时视图 v_all,在其中我们使用 UNION 连接两个相反的 LEFT JOINS,如下所示:

      CREATE TEMP VIEW v_all AS
                    SELECT p1.name AS name1, p1.age,
                          p2.name AS name2, p2.weight
                    FROM people1 p1
                    LEFT JOIN people2 AS p2 
                    USING (name)
                    UNION
                    SELECT p1.name AS name1, p1.age,
                        p2.name AS name2, p2.weight
                    FROM people2 AS p2
                    LEFT JOIN people1 AS p1
                    USING (name);
      

      但是,我们最终得到 2 个名称列,名称 1 和名称 2,它们可能具有空值或相等值。我们想要的是将 name1 和 name2 组合在一个列名中。我们可以通过如下 CASE 查询来做到这一点:

      SELECT age,weight,
                      CASE
                        WHEN name1 IS NULL
                          THEN name2
                        WHEN name2 IS NULL
                          THEN name1
                        WHEN name1=name2
                          THEN name1
                      END name
                    FROM v_all
      

      我们最终得到:

            name weight   age
      0    evi    108  None
      1  maria    125    54
      2   niko   None    57
      3    teo    186    59
      

      当然,您可以将两者结合在一个查询中,而无需创建临时视图。我避免这样做,以强调仅 2 个左连接和一个联合的不足,这是我迄今为止所看到的推荐。

      【讨论】:

        猜你喜欢
        • 2021-02-19
        • 2019-01-12
        • 2022-01-23
        • 2020-07-01
        • 1970-01-01
        • 2011-02-23
        • 2020-01-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多