2009-07-28 10 views
3

Je suis aux prises avec la création d'une requête SQL impliquant des agrégats utilisant PostgreSQL. Considérez les tableaux suivants:Récupère la clé primaire de la requête groupée par SQL en utilisant PostgreSQL

CREATE TABLE thing (
    id INT NOT NULL PRIMARY KEY, 
    price NUMERIC(10,2) NOT NULL, 
    description VARCHAR(255) NOT NULL, 
    url VARCHAR(255) NOT NULL, 
    location_id INT NOT NULL REFERENCES location(id) 
) 

CREATE TABLE location (
    id INT NOT NULL PRIMARY KEY, 
    type INT NOT NULL, 
    name VARCHAR(255) NOT NULL 
) 

Maintenant, je voudrais obtenir tous les dossiers chose pour chaque emplacement avec location.type = xxx qui ont le plus bas prix.

Quelque chose comme:

SELECT min(price) FROM thing 
INNER JOIN location ON (thing.location_id = location.id) 
WHERE type = xxx 
GROUP BY location_id 

Cela me liste le prix le plus bas pour chaque emplacement de type xxx, mais comment puis-je obtenir les lignes (ou leurs clés primaires) de ces colonnes de chose de la table?

Répondre

5

Utilisez cette extension PostgreSQL:

SELECT DISTINCT ON (location.id) thing.* 
FROM location 
JOIN thing 
ON  thing.location_id = location_id 
WHERE type = 1 
ORDER BY 
     location.id ASC, price ASC 

Cela permet de sélectionner uniquement la première ligne pour chaque location.id.

Puisque vos lignes sont triées par location.id puis par price, ce sera la ligne avec le prix minimal.

Dans la nouvelle PostgreSQL 8.4, vous pouvez également utiliser les fonctions de la fenêtre:

SELECT * 
FROM (
     SELECT thing.*, ROW_NUMBER() OVER (PARTITION BY location_id ORDER BY price) AS rn 
     FROM location 
     JOIN thing 
     ON  thing.location_id = location_id 
     WHERE type = 1 
     ) q 
WHERE rn = 1 
+0

parfait, c'est exactement ce que je cherchais. – Haes

0

peut-être utiliser une sous requête

SELECT t.id,t.description,t.price FROM 
    (SELECT location_id, min(price) FROM thing 
     INNER JOIN location ON (thing.location_id = location.id) 
     WHERE type = xxx 
     GROUP BY location_id 
    ) AS lowest 
    INNER JOIN thing AS t 
    ON t. location_id = lowest.location_id; 
0

Je suis un gars SQL Server, mais les éléments suivants doivent être SQL-92 conforme et devrait fonctionner:

select th.* 
from thing th 
    inner join (select lo.id, min(th.price) minPrice 
       from location lo 
       inner join thing th 
       on th.location_id = lo.id 
       where lo.type = xxx 
       group by lo.id) minSet 
    on minSet.id = th.location_id 
    and th.price = minSet.minPrice 

Notez également que je n'ai pas mis en place la table pour tester avec, donc t ici peut-être une faute de frappe ou deux là-dedans.

Alors que cela fonctionne, il semble vraiment gênant. Si Postgres a quelque chose comme les fonctions de classement de SQL, ils le rendraient un peu plus simple.

+0

C'est aussi ce que j'ai imaginé, à l'origine. Le problème avec cette requête est qu'elle retournera plusieurs colonnes pour un emplacement si le prix le plus bas n'est pas unique. – Haes

+0

Basé sur la description, j'ai pensé que c'était ce que vous cherchiez. S'il y a des choses en double basées sur le prix minimum, que voudriez-vous sélectionner? (Question rhétorique, puisque vous avez déjà une réponse - row_number() est une extension très utile.) –

3

Essayez cette requête

select thing.id,thing.description,thing.url,low.location_id,low.minimum from 
(select thing.location_id,min(price) as minimum from thing 
join location on thing.location_id=location.id 
    where location.type = 13 group by location_id) as low 
    inner join thing on thing.location_id = low.location_id 
Questions connexes