web-dev-qa-db-fra.com

PostgreSQL - clause GROUP BY ou être utilisé dans une fonction d'agrégation

J'ai trouvé quelques sujets ici sur SO, mais je ne trouve toujours pas la bonne configuration pour ma requête.

C'est une requête qui me convient bien sur localhost:

@cars = Car.find_by_sql('SELECT cars.*, COUNT(cars.id) AS counter 
                         FROM cars 
                         LEFT JOIN users ON cars.id=users.car_id 
                         GROUP BY cars.id ORDER BY counter DESC')

Mais sur Heroku me donne l'erreur ci-dessus - clause GROUP BY ou être utilisé dans une fonction d'agrégation.

Ensuite, j'ai lu quelque part que je devrais spécifier toutes les colonnes du tableau, j'ai donc essayé ceci:

@cars = Car.find_by_sql('SELECT cars.id, cars.name, cars.created_at, 
                                cars.updated_at, COUNT(cars.id) AS counter 
                         FROM cars 
                         LEFT JOIN users ON cars.id=users.car_id 
                         GROUP BY (cars.id, cars.name, cars.created_at, cars.updated_at) 
                         ORDER BY counter DESC')

Mais cela ne fonctionne pas sur localhost et pas sur Heroku ...

Quelle devrait être la bonne configuration de la requête?

29
user984621

Je pense que vous essayez de regrouper et de regrouper sur la même colonne. Cela dépend des données que vous souhaitez. Ether fait ceci:

SELECT 
 cars.name, 
 cars.created_at, 
 cars.updated_at, 
 COUNT(cars.id) AS counter 
FROM cars 
LEFT JOIN users 
  ON cars.id=users.car_id 
GROUP BY cars.name, cars.created_at, cars.updated_at 
ORDER BY counter DESC

Ou vous voulez tout compter peut-être? Alors comme ça:

SELECT
 cars.id,
 cars.name, 
 cars.created_at, 
 cars.updated_at, 
 COUNT(*) AS counter 
FROM cars 
LEFT JOIN users 
  ON cars.id=users.car_id 
GROUP BY cars.id, cars.name, cars.created_at, cars.updated_at 
ORDER BY counter DESC
28
Arion

Une requête comme celle-ci (récupération de toutes les lignes ou de la plupart) est plus rapide si vous GROUP avant JOIN. Comme ça:

SELECT id, name, created_at, updated_at, u.ct
FROM   cars c
LEFT   JOIN (
    SELECT car_id, count(*) AS ct
    FROM   users
    GROUP  BY 1
    ) u ON u.car_id  = c.id
ORDER  BY u.ct DESC;

De cette façon, vous avez besoin de beaucoup moins d'opérations de jointure. Et les lignes de la table cars ne doivent pas d'abord être multipliées en se joignant à de nombreux utilisateurs chacune, puis regroupées pour être à nouveau uniques.
Seul le bon tableau doit être regroupé, ce qui simplifie également la logique.

31
Erwin Brandstetter

Vous pouvez utiliser l'astuce MAX() sur la colonne des voitures.

@cars = Car.find_by_sql('
SELECT cars.id, MAX(cars.name) as name, MAX(cars.created_at) AS 
created_at, MAX(cars.updated_at) as updated_at, COUNT(cars.id) AS counter 
FROM cars LEFT JOIN users ON cars.id=users.car_id 
GROUP BY cars.id ORDER BY counter DESC')
7
user673207