Postgres:左连接,order by和limit 1

Posted

技术标签:

【中文标题】Postgres:左连接,order by和limit 1【英文标题】:Postgres: left join with order by and limit 1 【发布时间】:2014-03-17 06:01:52 【问题描述】:

我有这种情况:

Table1 has a list of companies.
Table2 has a list of addresses.
Table3 is a N relationship of Table1 and Table2, with fields 'begin' and 'end'.

由于公司可能会随着时间的推移而移动,因此它们之间的 LEFT JOIN 会导致每个公司有多个记录。

beginend 字段永远不会为 NULL。找到最新地址的解决方案是使用ORDER BY being DESC,删除旧地址是LIMIT 1

如果查询只能带来 1 家公司,那就可以了。但我需要一个将所有 Table1 记录与它们当前的 Table2 地址连接起来的查询。因此,必须在 LEFT JOIN 的 ON 子句中删除过时数据(AFAIK)。

知道如何建立条款以不创建重复的 Table1 公司并带来最新地址吗?

【问题讨论】:

【参考方案1】:

在连接条件中使用带有 max() 函数的依赖子查询。 类似这个例子:

SELECT *
FROM companies c
LEFT JOIN relationship r
ON c.company_id = r.company_id
   AND r."begin" = (
        SELECT max("begin")
        FROM relationship r1
        WHERE c.company_id = r1.company_id
     )
INNER JOIN addresses a
ON a.address_id = r.address_id 

演示:http://sqlfiddle.com/#!15/f80c6/2

【讨论】:

在 sqlfiddle 上很好 - 我直到现在才看到 jsfiddle,但 sqlfiddle 似乎超级有用! 如果 r.begin 在某些记录中可以相等,它似乎不起作用【参考方案2】:

我设法使用 Windows 功能解决了它:

WITH ranked_relationship AS(
    SELECT
        *
        ,row_number() OVER (PARTITION BY fk_company ORDER BY dt_start DESC) as dt_last_addr
    FROM relationship
)

SELECT
    company.*
    address.*,
    dt_last_addr as dt_relationship
FROM
    company
    LEFT JOIN ranked_relationship as relationship
            ON relationship.fk_company = company.pk_company AND dt_last_addr = 1
    LEFT JOIN address ON address.pk_address = relationship.fk_address

row_number() 在基于 fk_company 的每个窗口内为每个记录创建一个 int 计数器。对于每个窗口,日期最新的记录排在第一位,排名为 1,然后dt_last_addr = 1 确保每个fk_company 只发生一次 JOIN,地址最新的记录。

窗口函数非常强大,很少有人使用它们,它们避免了许多复杂的连接和子查询!

【讨论】:

【参考方案3】:

从 PostgreSQL 9.3 开始,JOIN LATERAL (https://www.postgresql.org/docs/9.4/queries-table-expressions.html) 允许进行子查询加入,因此它以优雅的方式解决了您的问题:

SELECT * FROM companies c
JOIN LATERAL (
    SELECT * FROM relationship r
    WHERE c.company_id = r.company_id
    ORDER BY r."begin" DESC LIMIT 1
) r ON TRUE
JOIN addresses a ON a.address_id = r.address_id

这种方法的缺点是LATERAL里面的表的索引在外面不起作用。

【讨论】:

您能否详细说明您的评论“这种方法的缺点是 LATERAL 内部表的索引在外部不起作用。”在您的示例中,如果r.company_id 上有索引,这是否意味着索引 用于子查询,但如果在外部查询中的 WHERE 子句? @EricDauenhauer 是的,确实如此。

以上是关于Postgres:左连接,order by和limit 1的主要内容,如果未能解决你的问题,请参考以下文章

使用具有不同 order by 子句的 postgres 窗口函数

ORDER BY 中的最后一个案例执行不正确 postgres

使用 LIMIT / ORDER BY 和 pg Postgres NodeJS 作为参数

MySQL调优--05---多表查询优化子查询优化 ORDER BY优化GROUP BY优化分页查询优化

Mysql之order by|group by 排序优化

在 Postgres 中更新左连接表的 NULL 值