PostgreSQL - doit apparaître dans le groupe par clause OU être utilisé dans une fonction agrégée

j'obtiens cette erreur dans le mode de production pg, mais son fonctionnement parfait 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
demandé sur Craig Ringer 2013-08-05 18:55:53

4 réponses

si user_id est la clé primaire alors vous devez mettre à jour PostgreSQL; les versions plus récentes seront correctement gérer le groupement par la clé primaire.

si user_id n'est ni unique ni la clé primaire pour la relation 'estates' en question, alors cette question n'a pas beaucoup de sens, car PostgreSQL n'a aucun moyen de savoir quelle valeur retourner pour chaque colonne de estates où plusieurs lignes partagent le même user_id . Vous devez utiliser une fonction agrégée qui exprime ce que vous voulez, comme min , max , avg , string_agg , array_agg , etc ou ajouter la (Les) colonne (s) d'intérêt au GROUP BY .

alternativement vous pouvez reformuler la requête pour utiliser DISTINCT ON et un ORDER BY si vous voulez vraiment choisir une rangée quelque peu arbitraire, bien que je doute vraiment qu'il soit possible d'exprimer cela via ActiveRecord.

certaines bases de données - dont SQLite et MySQL - ne sélectionneront qu'une ligne arbitraire. Cela est considéré comme incorrect et dangereux par L'équipe de PostgreSQL, donc PostgreSQL suit la norme SQL et considère ces requêtes comme des erreurs.

si vous avez:

col1    col2
fred    42
bob     9
fred    44
fred    99

et vous n':

SELECT col1, col2 FROM mytable GROUP BY col1;

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

bob     9

mais qu'résultat pour fred ? Il n'y a pas une seule réponse correcte à choisir, donc la base de données refusera d'exécuter ces requêtes dangereuses. Si vous voulez le le plus grand col2 pour n'importe quel col1 vous utiliserez le max agrégat:

SELECT col1, max(col2) AS max_col2 FROM mytable GROUP BY col1;
42
répondu Craig Ringer 2015-08-13 22:14:44

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

Elegant PostgreSQL Group by for Ruby on Rails / ActiveRecord

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

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

I je préfère DISTINCT sur parce que je peux encore obtenir toutes les autres valeurs de colonne dans la rangée. Seul DISTINCT retournera seulement la valeur de cette colonne spécifique.

15
répondu Jin Hian Lee 2017-05-23 12:26:32

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 groupement. De ce fait souvent l'erreur ci-dessus. Donc, assurez-vous d'ajouter vos propres .ordre (: 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
répondu hec 2015-01-02 06:58:02
@myestate1 = Estate.where(:Mgmt => current_user.Company)
@myestate = @myestate1.select("DISTINCT(user_id)")

c'est ce que j'ai fait.

3
répondu Hrishikesh Sardar 2013-08-05 15:54:38