我有这种情况:
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会为每家公司带来多条记录。
begin
和end
字段永远不会为NULL。找到最新地址的解决方案是使用ORDER BY being DESC
,删除旧地址的解决方案是LIMIT 1
。
如果查询只能带来一家公司,那该工作正常。但是我需要一个查询,它带来所有Table1记录,并加上它们当前的Table2地址。因此,必须在LEFT JOIN的ON子句中删除过时的数据(AFAIK)。
任何想法如何构建条款不创建重复的Table1公司并带来最新地址?
答案 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
答案 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
内部表的索引在外部不起作用。