Postgres:左边加入订单依据和限制1

时间:2014-02-17 19:23:29

标签: postgresql sql-order-by left-join foreign-key-relationship duplicate-removal

我有这种情况:

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

如果查询只能带来一家公司,那该工作正常。但是我需要一个查询,它带来所有Table1记录,并加上它们当前的Table2地址。因此,必须在LEFT JOIN的ON子句中删除过时的数据(AFAIK)。

任何想法如何构建条款不创建重复的Table1公司并带来最新地址?

3 个答案:

答案 0 :(得分:15)

在连接条件中使用带有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

答案 1 :(得分:5)

我设法使用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确保JOIN仅针对每个fk_company发生一次,并且记录具有最新地址。

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

答案 2 :(得分:1)

从 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 内部表的索引在外部不起作用。