web-dev-qa-db-fra.com

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

J'obtiens cette erreur dans le mode de production de pg, mais son fonctionnement très bien dans le mode de développement sqlite3.

 ActiveRecord::StatementInvalid in ManagementController#index

PG::Error: ERROR:  column "estates.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT "estates".* FROM "estates"  WHERE "estates"."Mgmt" = ...
               ^
: SELECT "estates".* FROM "estates"  WHERE "estates"."Mgmt" = 'Mazzey' GROUP BY user_id

@myestate = Estate.where(:Mgmt => current_user.Company).group(:user_id).all
26
Hrishikesh Sardar
@myestate1 = Estate.where(:Mgmt => current_user.Company)
@myestate = @myestate1.select("DISTINCT(user_id)")

c'est ce que j'ai fait.

3
Hrishikesh Sardar

Si user_id est la CLÉ PRIMAIRE alors vous devez mettre à jour PostgreSQL; les versions plus récentes gèreront correctement le regroupement par la clé primaire.

Si user_id n'est ni unique ni la clé primaire de la relation 'successions' en question, alors cette requête ne fait pas beaucoup de sens, car PostgreSQL n'a aucun moyen de savoir quelle valeur renvoyer pour chaque colonne de estates où plusieurs lignes partagent la même user_id. Vous devez utiliser une fonction d'agrégation qui exprime ce que vous voulez, comme min, max, avg, string_agg, array_agg, etc. ou ajoutez la ou les colonnes qui vous intéressent dans le GROUP BY.

Vous pouvez également reformuler la requête pour utiliser DISTINCT ON Et un ORDER BY si vous voulez vraiment choisir une ligne quelque peu arbitraire, bien que je doute vraiment qu'il soit possible de l'exprimer via ActiveRecord.

Certaines bases de données - y compris SQLite et MySQL - choisiront simplement une ligne arbitraire. Ceci est considéré comme incorrect et dangereux par l'équipe de PostgreSQL, donc PostgreSQL suit le standard SQL et considère ces requêtes comme des erreurs.

Si tu as:

col1    col2
fred    42
bob     9
fred    44
fred    99

et vous faites:

SELECT col1, col2 FROM mytable GROUP BY col1;

alors il est évident que vous devriez obtenir la ligne:

bob     9

mais qu'en est-il du résultat pour fred? Il n'y a pas de réponse correcte unique à choisir, donc la base de données refusera d'exécuter de telles requêtes dangereuses. Si vous vouliez le plus grand col2 pour toute col1 vous utiliseriez l'agrégat max:

SELECT col1, max(col2) AS max_col2 FROM mytable GROUP BY col1;
43
Craig Ringer

J'ai récemment migré de MySQL vers PostgreSQL et rencontré le même problème. Juste pour référence, la meilleure approche que j'ai trouvée est d'utiliser DISTINCT ON comme suggéré dans cette réponse SO:

Groupe PostgreSQL élégant par pour Ruby on Rails/ActiveRecord

Cela vous permettra d'obtenir un enregistrement pour chaque valeur unique dans votre colonne choisie qui correspond aux autres conditions de requête:

MyModel.where(:some_col => value).select("DISTINCT ON (unique_col) *") 

Je préfère DISTINCT ON car je peux toujours obtenir toutes les autres valeurs de colonne de la ligne. DISTINCT seul ne renverra que la valeur de cette colonne spécifique.

15
Jin Hian Lee

Après avoir souvent reçu l'erreur moi-même, je me suis rendu compte que Rails (J'utilise Rails 4) ajoute automatiquement un 'ordre par id' à la fin de votre requête de regroupement) . Cela entraîne souvent l'erreur ci-dessus. Assurez-vous donc d'ajouter votre propre .order (: group_by_column) à la fin de votre requête Rails. Par conséquent, vous aurez quelque chose comme ceci:

@problems = Problem.select('problems.username, sum(problems.weight) as weight_sum').group('problems.username').order('problems.username')
6
hec