【问题标题】:Join one to many and retrieve single result加入一对多并检索单个结果
【发布时间】:2012-09-10 05:19:45
【问题描述】:

我有两个表,如果重要的话,在 PostgreSQL 中,有一对多的关系。我需要加入他们,以便对于每个“一个”,我只能从“许多”表中获得单个结果。不仅如此,我还需要从“多”表中挑选出具体的结果。

表_A 身份证 |姓名 |日期 |更多科尔斯.... 1 |约翰 | 2012-01-10 | …… 2 |丽莎 | 2012-01-10 | …… 3 |安妮 | 2012-01-10 | …… 4 |詹姆斯 | 2012-01-10 | …… ... 表_B 身份证 |代码1 |代码2 |种类 1 | 04020 | 85003 | 1 1 | 04030 | 85002 | 4 2 | 81000 | 80703 | 1 3 | 87010 | 80102 | 4 3 | 87010 | 84701 | 5 4 | 04810 | 85003 | 1 4 | 04030 | 85002 | 4 4 | 04020 | 85003 | 1 ... 查询结果 身份证 |姓名 |日期 |代码1 |代码2 1 |约翰 | 2012-01-10 | 04020 | 85003 2 |丽莎 | 2012-01-10 | 81000 | 80703 3 |安妮 | 2012-01-10 | 87010 | 80102 4 |詹姆斯 | 2012-01-10 | 04810 | 85003 ...

TABLE_B 中的 SORT 列实际上是重新排序的 CODE2 中的最后一个字符。 CODE2 可以以 1-9 结尾,但 3 最重要,然后是 5、7、4、2、1、0、6、8、9,因此 3-->1、5-->2、7-->3 等等第四次。

我面临的问题是我需要 TABLE_B 中排序是最小数字的行。在某些情况下,存在多个最小大小写(参见 TABLE_B 中的 ID=4),那么选择哪一个具有最低 ID 的行并不重要,只要该 ID 有一个结果即可。

【问题讨论】:

  • 欢迎来到 StackOverflow!感谢您显示数据并写出清晰的问题。但是,下一次,如果您以CREATE TABLE 语句和INSERTCOPY 语句来加载数据来显示您的数据,那么您将让那些想要帮助您的人更轻松。这样,人们可以在发布之前轻松测试候选答案,以确保他们没有语法错误并获得您想要的结果。
  • 感谢您的评论。从现在开始我会这样做。

标签: sql postgresql select join one-to-many


【解决方案1】:

PostgreSQL 支持 window function。试试这个,

SELECT d.ID, d.NAME, d.DATE, d.CODE1, d.CODE2
FROM
(
  SELECT  a.ID, a.NAME, a.DATE,
          b.CODE1, b.CODE2,
          ROW_NUMBER() OVER(PARTITION BY a.ID ORDER BY b.SORT ASC, b.CODE2 DESC) AS ROWNUM
  FROM    TableA a
          INNER JOIN TableB b
            ON a.ID = b.ID
) d
WHERE d.RowNum = 1

SQLFiddle Demo

【讨论】:

  • 谢谢,但这给了我相同的结果,就好像我将所有从 TABLE_B 加入到 TABLE_A 一样,多对一。
  • @user1678791 因为您添加了postgresql 标签,所以我使用window function 更新了答案
  • 尤里卡!非常感谢,这是我问题的解决方案。为 SQLFiddle 演示 +1,太棒了。
  • 小提琴不再给出正确的结果
【解决方案2】:

这是我在 SQL Server 上要做的事情。

SELECT a.ID,
    a.NAME,
    a.DATE,
    b.CODE1,
    b.CODE2
FROM TABLE_A a
JOIN TABLE_B b
    on a.ID = b.ID
WHERE b.SORT = (SELECT MIN(SORT) 
    FROM TABLE_B
    WHERE ID = b.ID)

【讨论】:

    【解决方案3】:

    使用 PostgreSQL 的DISTINCT ON 更简单、更短、更快:

    SELECT DISTINCT ON (a.id)
           a.id, a.name, a.date, b.code1, b.code2
    FROM   table_a a
    LEFT   JOIN table_b b USING (id)
    ORDER  BY a.id, b.sort
    

    this closely related answer 中的详细信息、说明、基准和链接。
    我使用LEFT JOIN,因此table_a 中没有table_b 中任何匹配行的行不会被删除。

    旁注:

    虽然在 PostgreSQL 中被允许,但使用 date 作为列名是不明智的。它是每个 SQL 标准中的 reserved word 和 PsotgreSQL 中的类型名称。

    将 ID 列命名为 id 也是一种反模式。没有描述性,也没有帮助。一种(许多)可能的命​​名约定是在它是主键的表之后命名它:table_a_id。引用它的外键名称相同(如果没有其他自然名称优先)。

    【讨论】:

    • 谢谢,这个解决方案也有效。列的命名仅出于本示例的明确描述性原因。
    • @thorgilsv:我明白了。这个版本应该会快一些。您可以使用EXPLAIN ANALYZE 进行测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-11
    • 1970-01-01
    • 2019-03-26
    • 2016-02-17
    • 2015-08-12
    • 2017-03-27
    相关资源
    最近更新 更多