J'ai deux tables appelées record
et record_history
. Pour chaque enregistrement, il peut y avoir plusieurs historiques. Ils peuvent être joints par id
et record_id
. Je veux obtenir toutes les entrées de record
avec les dernières record_history
Les données. J'ai créé la requête comme,
SELECT rec.id, rec.name, rech1.data AS last_history_data
FROM record rec
LEFT OUTER JOIN record_history rech1 ON (rec.id = rech1.record_id)
LEFT OUTER JOIN record_history rech2 ON (rec.id = rech2.record_id AND rech2.ts > rech1.ts)
WHERE rech2.id IS NULL
ORDER BY rec.id DESC
Ici, je reçois le dernier par ts
. Cela fonctionne tant qu'il n'y a pas d'entrées ts
en double. Si l'horodatage récent est répété dans record_history
, cette requête renvoie plus d'une ligne pour un enregistrement. Comment pouvons-nous appliquer la limite ici sur la jointure de gauche pour restreindre les lignes en double?
Sauf si vous êtes dans une version très ancienne de Postgres, vous n'avez pas besoin de la double jointure. Vous pouvez obtenir le même résultat en utilisant une LATERAL
join.
Les résultats en double peuvent être évités dans votre méthode en ajoutant une deuxième condition en plus de rec.id = rech2.record_id
. Avec la méthode de jointure LATERAL
, l'utilisation de LIMIT
l'évite de toute façon. Il ne peut y avoir qu'une seule ligne renvoyée par la sous-requête latérale. Nous pouvons ajouter une deuxième condition pour que le choix soit déterministe (à partir des deux lignes ou plus avec le même horodatage):
SELECT rec.id, rec.name, rech.data AS last_history_data
FROM record AS rec
LEFT OUTER JOIN LATERAL
( SELECT rech.data
FROM record_history AS rech
WHERE rec.id = rech.record_id
ORDER BY rech.ts DESC
-- ,rech.id DESC -- optional
LIMIT 1
) AS rech
ON TRUE
ORDER BY rec.id DESC ;
En ce qui concerne la façon de procéder avec la méthode d'origine (2 jointures et IS NULL
check), vous pouvez modifier la condition ON
- en supposant qu'il existe une colonne id
dans la table d'historique afin que (id)
ou au moins (ts, id)
est unique:
LEFT OUTER JOIN record_history rech2
ON rec.id = rech2.record_id
AND (rech2.ts > rech1.ts OR rech2.ts = rech1.ts AND rech2.id > rech1.id)
Soit dit en passant, vous pouvez remplacer cette deuxième jointure LEFT
et IS NULL
vérifier avec un NOT EXISTS
sous-requête avec les mêmes résultats et éventuellement une efficacité similaire (ou même avec un NOT IN
sous-requête bien que cela nécessite des précautions supplémentaires pour les colonnes nullables, non recommandé).