【问题标题】:Join tables on a unique ID field, returning exactly one result row per Id value在唯一 ID 字段上连接表,每个 ID 值只返回一个结果行
【发布时间】:2014-07-29 05:31:02
【问题描述】:

我有几个表,它们都有一个唯一的 ID 字段。我想在这个 ID 字段上加入他们。更准确地说,我想从每个表中提取一列,并将所有提取的列与 ID 值对齐。这些表没有所有 ID 值的条目;我希望结果对于至少一个表中存在的每个 ID 值都有一行。如果其中一个表中缺少 Id,则结果应具有 null。我不在乎如果 Id 值在表中不是唯一的会发生什么(在我的数据中,它们是唯一的)。

玩具示例:我有一张plant 桌子

Id  Name            Class
1   larch           pinopsida
3   horse chestnut  angiosperms

还有一个animal

Id  Name            Cry
1   cat             meow
2   dog             bow wow
4   carp            

还有一个mineral

Id  Name            Color
2   diamond         white
3   emerald         green

我想将它们组合成

Id  Plant           Animal  Mineral
1   larch           cat     (null)
2                   dog     diamond
3   horse chestnut  (null)  emerald
4   (null)          carp    (null)

据我所知,这不是直接的 inner join,因为它只会保留所有数据库中存在的 ID,也不是直接的 left joinright join,因为这只会保留存在于其中一张表(没有一张表包含所有 Id 值),也不是直接的 outer joincross join,因为它们会将 Id 分布在多行上。

数据库是 SQL Server 2012。我直接访问它,而不是通过其他编程语言。真正的查询是 https://data.stackexchange.com/cs/query/36599/show-all-types — 肯定有比列出所有可以相等的 Id 列对更好的方法。

【问题讨论】:

    标签: sql sql-server join


    【解决方案1】:

    您可以使用FULL JOIN

    Select COALESCE(p.Id, a.Id, m.Id) Id
         , MAX(p.Name) Plant
         , MAX(a.Name) Animal
         , MAX(m.Name) Mineral
    FROM   Plant p
           FULL JOIN Animal a ON p.Id = a.Id
           FULL JOIN Mineral m ON p.Id = m.Id
    GROUP BY COALESCE(p.Id, a.Id, m.Id)
    ORDER BY COALESCE(p.Id, a.Id, m.Id)
    

    SQLFiddle demo


    如果您不想在查询中使用 COALESCEGROUP,则可以先获取 Id,然后将 JOIN 获取表

    WITH Ids AS (
      SELECT Id FROM Plant
      UNION
      SELECT Id FROM Animal
      UNION
      SELECT Id FROM Mineral
    )
    SELECT Ids.Id
         , p.Name Plant
         , a.Name Animal
         , m.Name Mineral
    FROM   Ids
           LEFT JOIN Plant p ON Ids.Id = p.Id
           LEFT JOIN Animal a ON Ids.Id = a.Id
           LEFT JOIN Mineral m ON Ids.Id = m.Id
    

    SQLFiddle demo

    【讨论】:

    • @Notulysses 当我发布它时你不在页面上,可能是因为所有那些删除/取消删除...
    【解决方案2】:
    SELECT COALESCE(t.id, a.id, m.id) AS [ID]
         , MAX(t.name) AS Plant     
         , MAX(a.name) AS Animal
         , MAX(m.name) AS Mineral
    FROM plant t FULL OUTER JOIN mineral m ON m.id = t.id
                 FULL OUTER JOIN animal a ON a.id = t.id
    GROUP BY COALESCE(t.id, a.id, m.id)
    ORDER BY [ID]
    

    Example

    【讨论】:

    • 我在问了几分钟后偶然发现了……这是“正确的方法”吗?构建行然后将它们折叠在一起并重复 coalesce 语句似乎很奇怪,但后来我在黑暗中摸索着学习了所有的 SQL,所以我知道什么。
    • @Gilles :嗯,我对使用MAX 有点沮丧(而且任何其他聚合函数对我来说似乎也很混乱)。
    • 此查询不允许动物和矿物具有相同 id 而植物在该位置有间隙的情况。
    • @shawnt00 :是的,检查示例id = 2。这就是为什么,实际上,我使用FULL OUTER JOIN
    • FULL OUTER JOIN animal a ON a.id = t.id 当 t.id 为 null 时将不匹配,因为 id 等于 2。
    【解决方案3】:

    或者你也可以有一个锚定表:

    select anchor.Id, A.name, B.Name, C.Name
    from
        (
            select distinct Id
            from (select Id from A union all select Id from B union all select Id from C) as T
        ) as anchor
        left outer join A on A.Id = anchor.Id
        left outer join B on B.Id = anchor.Id
        left outer join C on C.Id = anchor.Id
    

    我忘记在锚表中区分 id,但这非常重要。

    【讨论】:

    • 我不是想偷你的答案。 Geez,我的第一个答案满足了这个问题,我只是因为投反对票才添加了这个。我已经这样做了 20 年了,比起担心这些废话,我还有更好的事情要做。
    • 我忘记在锚表中使 id 不同,但这非常重要。而且由于我很少使用 UNION(而不是 UNION ALL),我忘记了它很容易消除锚表中的重复 ID。这个答案显然等同于 Serpiton 的答案。
    【解决方案4】:
    select coalesce(A.Id, B.Id, C.Id) as Id, ...
    from
        A
        full outer join B on B.Id = A.Id
        full outer join C on C.Id = A.Id or C.Id = B.Id
    

    键在 ORed 连接条件中。也可以写成 C.Id = coalesce(A.Id, B.Id)

    如果您的 id 值是各个表中的主键,则不需要其他答案中使用的分组。

    【讨论】:

    • 正如我在问题中所写,我想避免列出所有可以相等的 Id 对。有 3 张桌子是可以管理的,但有 8 张桌子并且可能会增加,就不是了。
    • 合并消除了大部分重复,但我不能为你改变现实。
    • 对于精心设计的数据库,这确实不是惯例,因此没有直接的方法。由于您似乎不知道完整的外部联接,因此我将该声明作为您的思维过程中发生的事情的暗示,同时尝试仅使用其他类型的联接来解决它。你没有提到任何关于使用这种方法将越来越多的表格,我会说拒绝一个有效的答案是非常不公平的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多