J'utilise PostgreSQL 9.1 avec une application Ruby on Rails.
J'essaie de lister la dernière version de chaque "charge" (dans ma table d'historique: hist_version_charges) appartenant au même identifiant de projet (proj_sous_projet_id = 2).
Cela me fait utiliser la fonction d'agrégation max () et appliquer le résultat à une fonction JOIN sur la même table que PostgreSQL n'autorise pas à utiliser les colonnes de la clause SELECT si elles n'apparaissent pas dans la clause GROUP BY, ALTHOUGH utilisant un max () signifie évidemment que je suis intéressé par la ligne contenant les valeurs maximales!
Voici ma requête:
SELECT h_v_charges.*,
max(last_v.version) as lv
FROM hist_versions_charges h_v_charges
JOIN hist_versions_charges last_v
ON h_v_charges.version = lv
AND h_v_charges.proj_charge_id = last_v.proj_charge_id
GROUP BY last_v.proj_sous_projet_id,
last_v.proj_charge_id
HAVING last_v.proj_sous_projet_id = 2
ORDER BY h_v_charges.proj_charge_id ASC;
Le message d'erreur que j'ai reçu:
ERROR: column "lv" does not exist
LINE 1: ..._versions_charges last_v ON h_v_charges.version = lv AND h_v...
^
********** Error **********
ERROR: column "lv" does not exist
SQL state: 42703
Character: 147
J'ai également essayé avec "last_v.lv" mais l'erreur reste la même.
Si quelqu'un a une idée de ce qui ne va pas, elle est plus que bienvenue.
=== MISE À JOUR ===
Selon * a_horse_with_no_name * et les réponses de Colin 't Hart , je me suis finalement retrouvé avec la requête suivante:
SELECT *
FROM (
SELECT *, max(version) OVER (PARTITION BY proj_charge_id) AS lv
FROM hist_versions_charges
WHERE proj_sous_projet_id = 2) AS hv
WHERE hv.lv = hv.version
ORDER BY hv.proj_charge_id ASC;
C'est un peu plus rapide avec un seul ORDER BY.
J'ai également essayé la requête avec une clause WITH. Bien que "plus agréable", il crée des frais de traitement supplémentaires. Comme je sais que je ne réutiliserai pas à l'avenir la sous-requête deux fois ou plus dans la même requête principale, je suis d'accord pour utiliser une simple sous-requête.
Merci quand même à * a_horse_with_no_name * et Colin 't Hart . J'ai appris beaucoup de choses!
Vous voulez probablement quelque chose comme ça:
SELECT h_v_charges.*,
last_v.last_version
FROM hist_versions_charges h_v_charges
JOIN (select proj_charge_id,
max(version) as last_version
from hist_versions_charges
where proj_sous_projet_id = 2
group by proj_charge_id
) last_v
ON h_v_charges.version = last_v.last_version
AND h_v_charges.proj_charge_id = last_v.proj_charge_id
ORDER BY h_v_charges.proj_charge_id ASC;
Une solution éventuellement (car aucune jointure n'est requise) plus rapide serait:
select *
from (
select hvc.*,
row_number() over (partition by proj_charge_id order by version desc) as rn
from hist_versions_charges as hvc
where proj_sous_projet_id = 2
) as hv
where rn = 1
order by hv.proj_charge_id ASC;
Comme l'a souligné Colin, cela peut également s'écrire:
with hv as (
select hvc.*,
row_number() over (partition by proj_charge_id order by version desc) as rn
from hist_versions_charges as hvc
where proj_sous_projet_id = 2
)
select *
from hv
where rn = 1
order by hv.proj_charge_id ASC;