web-dev-qa-db-ja.com

Postgres:order byおよびlimitを使用した左結合1

私は状況があります:

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

クエリで1つの会社しか持てない場合は、これで問題ありません。しかし、現在のTable2アドレスと結合されたすべてのTable1レコードを取得するクエリが必要です。したがって、古いデータの削除はLEFT JOINのON句で行う必要があります(AFAIK)。

複製されたTable1企業を作成せず、最新の住所を持ち込まないようにするための条項をどのように構築できるかについてのアイデアはありますか?

19
Hikari

私はなんとか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が1回だけ発生することを確認しますfk_company、最新の住所を持つレコード。

ウィンドウ関数は非常に強力で、それらを使用するpplはほとんどありません。それらは、多くの複雑な結合とサブクエリを回避します!

6
Hikari

結合条件で依存サブクエリを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

19
krokodilko