web-dev-qa-db-fra.com

Erreur liée à only_full_group_by lors de l'exécution d'une requête en MySql

J'ai mis à niveau mon système et ai installé MySql 5.7.9 avec php pour une application Web sur laquelle je travaille. J'ai une requête qui est créée dynamiquement, et lorsqu'elle est exécutée dans les anciennes versions de MySql, elle fonctionne bien. Depuis la mise à niveau vers la version 5.7, j'obtiens cette erreur:

L'expression n ° 1 de la liste SELECT n'est pas dans la clause GROUP BY et contient une colonne non agrégée 'support_desk.mod_users_groups.group_id' qui ne dépend pas fonctionnellement des colonnes de la clause GROUP BY; ceci est incompatible avec sql_mode = only_full_group_by

Notez la page de manuel de Mysql 5.7 sur le sujet de Modes SQL Server .

C'est la requête qui me pose problème:

SELECT mod_users_groups.group_id AS 'value', 
       group_name AS 'text' 
FROM mod_users_groups
LEFT JOIN mod_users_data ON mod_users_groups.group_id = mod_users_data.group_id 
WHERE  mod_users_groups.active = 1 
  AND mod_users_groups.department_id = 1 
  AND mod_users_groups.manage_work_orders = 1 
  AND group_name != 'root' 
  AND group_name != 'superuser' 
GROUP BY group_name 
HAVING COUNT(`user_id`) > 0 
ORDER BY group_name

J'ai fait quelques recherches sur le sujet, mais je ne comprends pas assez only_full_group_by pour comprendre ce que je dois faire pour résoudre la requête. Puis-je simplement désactiver l'option only_full_group_by ou dois-je faire autre chose?

Faites-moi savoir si vous avez besoin de plus d'informations.

371
Dan Bemowski

Je voudrais juste ajouter group_id au GROUP BY.

Lorsque SELECTligne une colonne qui ne fait pas partie du GROUP BY, il peut y avoir plusieurs valeurs pour cette colonne dans les groupes, mais il ne restera plus qu'un espace pour une seule valeur dans les résultats. Ainsi, la base de données généralement doit être expliquée exactement comment transformer ces valeurs multiples en une seule valeur. Généralement, cela se fait avec une fonction d'agrégation comme COUNT(), SUM(), MAX() etc ... Je dis généralement car la plupart des autres systèmes de base de données populaires insistent sur ce point . Cependant, dans MySQL avant la version 5.7, le comportement par défaut était plus tolérant, car il ne se plaindrait pas et choisirait alors arbitrairement toute valeur! Il a également une fonction ANY_VALUE() qui pourrait être utilisée comme une autre solution à cette question si vous aviez réellement besoin du même comportement qu'auparavant. Cette flexibilité a un coût, car elle n’est pas déterministe. Je ne le recommanderais donc que si vous avez une très bonne raison pour en avoir besoin. MySQL active maintenant le paramètre only_full_group_by par défaut pour de bonnes raisons; il est donc préférable de s'y habituer et de rendre vos requêtes conformes.

Alors pourquoi ma réponse simple ci-dessus? J'ai fait quelques hypothèses:

1) le group_id est unique. Cela semble raisonnable, il s'agit d'une "identité" après tout.

2) le group_name est également unique. Cette hypothèse peut ne pas être raisonnable. Si ce n'est pas le cas et que vous avez des doublons group_names et que vous suivez ensuite mon conseil d'ajouter group_id au GROUP BY, vous constaterez peut-être que vous obtenez maintenant plus de résultats qu'auparavant, car les groupes portant le même nom auront désormais des lignes distinctes dans les résultats. Pour moi, cela serait mieux que de masquer ces groupes de doublons car la base de données a discrètement choisi une valeur de manière arbitraire!

C'est également une bonne pratique de qualifier toutes les colonnes avec leur nom de table ou leur alias lorsqu'il y a plus d'une table impliquée ...

SELECT 
  g.group_id AS 'value', 
  g.group_name AS 'text' 
FROM mod_users_groups g
LEFT JOIN mod_users_data d ON g.group_id = d.group_id 
WHERE g.active = 1 
  AND g.department_id = 1 
  AND g.manage_work_orders = 1 
  AND g.group_name != 'root' 
  AND g.group_name != 'superuser' 
GROUP BY 
  g.group_name, 
  g.group_id 
HAVING COUNT(d.user_id) > 0 
ORDER BY g.group_name
303
davmos

Vous pouvez essayer de désactiver le paramètre only_full_group_by en procédant comme suit:

mysql> set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
mysql> set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

MySQL 8 n'accepte pas NO_AUTO_CREATE_USER et doit donc être supprimé.

318
WeiYuan

vous pouvez désactiver le message d'avertissement comme expliqué dans les autres réponses ou bien comprendre ce qui se passe et le corriger.

Depuis MySQL 5.7.5, le mode SQL par défaut inclut ONLY_FULL_GROUP_BY , ce qui signifie que lorsque vous regroupez des lignes et que vous sélectionnez un élément dans ce groupe, vous devez explicitement dire quelle ligne faut-il utiliser pour cette sélection? Mysql needs to know which row in the group you're looking for, which gives you two options

Mysql a besoin de savoir quelle ligne du groupe que vous recherchez, ce qui vous donne deux options

  • Vous pouvez également ajouter la colonne de votre choix à l'instruction de groupe group by rect.color, rect.value, ce qui peut être ce que vous voulez dans certains cas. Sinon, vous obtiendrez des résultats en double avec la même couleur que vous ne souhaitez peut-être pas.
  • vous pouvez aussi utiliser des fonctions d'agrégat de mysql pour indiquer la rangée recherchée dans les groupes, comme AVG()MIN()MAX()liste complète
  • ET enfin, vous pouvez utiliser ANY_VALUE() si vous êtes certain que tous les résultats du groupe sont identiques. doc
295
azerafati

Si vous ne souhaitez pas modifier votre requête actuelle, suivez les étapes ci-dessous -

  1. shant vagabond dans votre boîte
  2. Type: Sudo vim /etc/mysql/my.cnf
  3. Faites défiler jusqu'au bas du fichier et tapez A pour passer en mode insertion.
  4. Copier et coller

    [mysqld]
    sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
    
  5. Tapez esc pour quitter le mode de saisie

  6. Tapez :wq pour enregistrer et fermer vim.
  7. Tapez Sudo service mysql restart pour redémarrer MySQL.
160
Ajai

Utilisez ANY_VALUE() pour faire référence à la colonne non agrégée.

Depuis MySQL 5.7 docs :

Vous pouvez obtenir le même effet sans désactiver ONLY_FULL_GROUP_BY en utilisant ANY_VALUE() pour faire référence à la colonne non agrégée.

...

Cette requête peut ne pas être valide avec ONLY_FULL_GROUP_BY activé, car la colonne d'adresse non agrégée de la liste de sélection n'est pas nommée dans la clause GROUP BY:

SELECT name, address, MAX(age) FROM t GROUP BY name;

...

Si vous savez que pour un ensemble de données donné, chaque valeur de nom détermine de manière unique la valeur d'adresse, l'adresse dépend effectivement du nom de la fonction. Pour dire à MySQL d'accepter la requête, vous pouvez utiliser la fonction ANY_VALUE():

SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;
50
Italo Borssatto

Je vais essayer de vous expliquer en quoi consiste cette erreur.
Depuis MySQL 5.7.5, l’option ONLY_FULL_GROUP_BY est activée par défaut.
Ainsi, conformément à la norme SQL92 et aux versions antérieures:

n'autorise pas les requêtes pour lesquelles la liste de sélection, la condition HAVING ou la liste ORDER BY font référence à des colonnes non agrégées qui ne sont ni nommées dans la clause GROUP BY ni dépendent fonctionnellement de colonnes GROUP BY (déterminées uniquement par)

( en lire plus dans la documentation )

Donc, par exemple:

SELECT * FROM `users` GROUP BY `name`;

Vous obtiendrez un message d'erreur après avoir exécuté la requête ci-dessus.

# 1055 - L'expression n ° 1 de la liste SELECT n'est pas dans la clause GROUP BY et contient la colonne non agrégée 'testsite.user.id' qui ne dépend pas fonctionnellement des colonnes de la clause GROUP BY; ceci est incompatible avec sql_mode = only_full_group_by

Pourquoi?
Étant donné que MySQL ne comprend pas exactement quelles valeurs des enregistrements groupés doivent être extraites, c’est le point.

C'EST À DIRE. Disons que vous avez ces enregistrements dans votre table users:
Yo

Et vous exécuterez la requête invalide présentée ci-dessus.
Et vous obtiendrez l'erreur ci-dessus, car il y a 3 enregistrements portant le nom John, et c'est Nice, mais ils ont tous des valeurs de champs email différentes.
Ainsi, MySQL ne comprend tout simplement pas lequel de ces enregistrements doit être restitué.

Vous pouvez résoudre ce problème en modifiant simplement votre requête comme ceci:

SELECT `name` FROM `users` GROUP BY `name`

En outre, vous pouvez ajouter d'autres champs à la section SELECT, mais vous ne pouvez pas le faire, s'ils ne sont pas agrégés, mais vous pouvez utiliser une béquille (mais cela n'est pas recommandé):

SELECT ANY_VALUE(`id`), ANY_VALUE(`email`), `name` FROM `users` GROUP BY `name`

enter image description here

Maintenant, vous pouvez demander, pourquoi utiliser ANY_VALUE est fortement déconseillé?
Parce que MySQL ne sait pas exactement quelle valeur d'enregistrements groupés à récupérer, et en utilisant cette fonction, vous lui demandez d'en extraire un (dans ce cas, l'email du premier enregistrement avec le nom = John a été récupéré) .
Exactement, je ne peux pas vous dire pourquoi vous voudriez que ce comportement existe.

S'il vous plaît, si vous ne me comprenez pas, apprenez-en plus sur le fonctionnement des groupes dans MySQL, c'est très simple.

Et à la fin, voici une autre requête simple, mais valide.
Si vous souhaitez interroger le nombre total d'utilisateurs en fonction de l'âge disponible, vous pouvez écrire cette requête

SELECT `age`, COUNT(`age`) FROM `users` GROUP BY `age`;

Ce qui est entièrement valide, selon les règles de MySQL.
Etc.

Il est important de comprendre en quoi consiste exactement le problème et d’écrire ensuite la solution.

44
Abraham Tugalov

J'utilise Laravel 5.3, MySQL 5.7.12, sur laravel Homestead (0.5.0, je crois)

Même après avoir explicitement défini l'édition /etc/mysql/my.cnf afin de refléter:

[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Je recevais toujours l'erreur.

Je devais changer config/database.php de true à false:

    'mysql' => [
        'strict' => false, //behave like 5.6
        //'strict' => true //behave like 5.7
    ], 

Lectures complémentaires:

https://laracasts.com/discuss/channels/servers/set-set-sql-mode-on-Homesteadhttps://mattstauffer.co/blog/strict-mode- et-other-mysql-customizations-in-laravel-5-2

36
Ryan

Si vous utilisez wamp 3.0.6 ou une version supérieure autre que la version stable 2.5, vous pourriez être confronté à ce problème. Premièrement, le problème concerne SQL. vous devez nommer les champs en conséquence. mais il existe un autre moyen de le résoudre. cliquez sur l'icône verte de wamp. mysql-> mysql settings-> sql_mode-> aucun. ou depuis la console, vous pouvez modifier les valeurs par défaut.

mysql> set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
mysql> set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
18
PiyaChakraborty

Ajout de lignes (mention ci-dessous) dans le fichier: /etc/mysql/my.cnf

[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Travaille bien pour moi. Version du serveur: 5.7.18-0ubuntu0.16.04.1 - (Ubuntu)

14
Jagdeep Singh

Pour Mac:

1.Copiez le fichier my-default.cnf par défaut dans /etc/my.cnf.

Sudo cp $(brew --prefix mysql)/support-files/my-default.cnf /etc/my.cnf

2.Changez sql_mode dans my.cnf en utilisant votre éditeur favori et réglez-le à ceci

sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

3. Redémarrez le serveur MySQL.

mysql.server restart
9
Tanvir Hasan Anick

Pour localhost/wampserver 3, nous pouvons définir sql-mode = user_mode pour supprimer cette erreur:

click on wamp icon -> MySql -> MySql Setting -> sql-mode -> user_mode

puis redémarrez wamp ou apache

6
Braham Dev Yadav

C'est ce qui m'a aidé à comprendre l'ensemble du problème:

  1. https://stackoverflow.com/a/20074634/1066234
  2. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

Et dans ce qui suit, un autre exemple de requête problématique.

Problématique:

SELECT COUNT(*) as attempts, SUM(elapsed) as elapsedtotal, userid, timestamp, questionid, answerid, SUM(correct) as correct, elapsed, ipaddress FROM `gameplay`
                        WHERE timestamp >= DATE_SUB(NOW(), INTERVAL 1 DAY)
                        AND cookieid = #

résolu en ajoutant ceci à la fin:

  GROUP BY timestamp, userid, cookieid, questionid, answerid, elapsed, ipaddress

Note: Voir le message d'erreur en PHP, il vous indique où se situe le problème.

Exemple:

Erreur de requête MySQL 1140: Dans une requête agrégée sans GROUP BY, l'expression n ° 4 de la liste SELECT contient la colonne non agrégée 'db.gameplay.timestamp'; cela est incompatible avec sql_mode = only_full_group_by - Requête: SELECT COUNT (*) en tant que tentatives, SUM (écoulé) en tant que écoulé, total, userid, timestamp, questionid, answerid, SUM (correct), correct, écoulé, adresse IP du jeu WHERE timestamp> = DATE_SUB (NOW (), INTERVAL 1 JOUR) ET userid = 1

Dans ce cas, l'expression # 4 était manquante dans GROUP BY.

5
Kai Noack

Vous pouvez ajouter un unique index à group_id; si vous êtes sûr que group_id est unique.

Cela peut résoudre votre cas sans modifier la requête.

Une réponse tardive, mais cela n’a pas encore été mentionné dans les réponses. Peut-être faudrait-il compléter les réponses déjà complètes disponibles. Au moins, mon cas a été résolu lorsque j'ai dû diviser une table avec trop de champs.

3
micaball

Si vous rencontrez cette erreur avec Symfony avec constructeur de requêtes de doctrine, et si cette erreur est provoquée par un orderBy:

Faites attention à select la colonne que vous voulez groupBy et utilisez addGroupBy à la place de groupBy:

$query = $this->createQueryBuilder('smth')->addGroupBy('smth.mycolumn');

Fonctionne sur Symfony -

2
Gangai Johann

Toutes mes excuses pour ne pas utiliser votre code SQL exact

J'ai utilisé cette requête pour surmonter l'avertissement Mysql.

SELECT count(*) AS cnt, `regions_id`
FROM regionables 
WHERE `regionable_id` = '115' OR `regionable_id` = '714'
GROUP BY `regions_id`
HAVING cnt > 1

note la clé pour moi être

count(*) AS cnt
0
Harry Bosh