Ошибка, связанная с only_full_group_by при выполнении запроса в MySql

443

Я обновил свою систему и установил MySql 5.7.9 с php для веб-приложения, над которым я работаю. У меня есть запрос, который создается динамически, и при запуске в более старых версиях MySql он работает нормально. После обновления до 5.7 я получаю эту ошибку:

Выражение №1 списка SELECT отсутствует в предложении GROUP BY и содержит неагрегированный столбец 'support_desk.mod_users_groups.group_id', который функционально не зависит от столбцов в предложении GROUP BY; это несовместимо с sql_mode = only_full_group_by

Обратите внимание на страницу руководства для Mysql 5.7 по теме « Режимы SQL сервера» .

Это вопрос, который доставляет мне проблемы:

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

Я немного погуглил по этому вопросу, но не понимаю only_full_group_byдостаточно, чтобы понять, что мне нужно сделать, чтобы исправить запрос. Могу ли я просто отключить эту only_full_group_byопцию, или мне нужно что-то еще сделать?

Дайте мне знать, если вам нужна дополнительная информация.

Дэн Бемовски
источник
здесь я нашел решение stackoverflow.com/a/7588442/612987
Васим А.
21
Это должно быть самое запутанное сообщение об ошибке, которое я когда-либо встречал.
Рольф
2
@Rolf Вы видели ошибку для того же типа проблемы в Oracle? " not a GROUP BY expressionВот и все. Они могли бы просто иметь числовой код ошибки и вообще никакого сообщения.
Билл Карвин

Ответы:

360

Я бы просто добавил group_idк GROUP BY.

При SELECTсоздании столбца, который не является частью GROUP BYэтого, в группах может быть несколько значений для этого столбца, но в результатах будет место только для одного значения. Таким образом, базе данных обычно нужно точно сказать, как превратить эти несколько значений в одно значение. Обычно это делается с помощью агрегатной функции, такой как COUNT(), SUM()и MAX()т. Д. Я обычно говорю, потому что большинство других популярных систем баз данных настаивают на этом. Однако в MySQL до версии 5.7 поведение по умолчанию было более щадящим, потому что он не будет жаловаться и затем произвольно выберет любое значение ! Он также имеетANY_VALUE()функция, которая может быть использована в качестве другого решения этого вопроса, если вам действительно нужно то же поведение, что и раньше. Такая гибкость обходится дорого, потому что она не является детерминированной, поэтому я бы не рекомендовал ее, если у вас нет веских причин для ее использования. MySQL теперь включает only_full_group_byнастройку по умолчанию по уважительным причинам, поэтому лучше привыкнуть к ней и заставить ваши запросы соответствовать ей.

Так почему мой простой ответ выше? Я сделал пару предположений:

1) group_idуникален Кажется разумным, в конце концов, это «удостоверение личности».

2) group_nameтоже уникален. Это не может быть таким разумным предположением. Если это не так, и у вас есть какой-то дубликат, group_namesи вы затем следуете моему совету для добавления group_idв GROUP BY, вы можете обнаружить, что теперь вы получаете больше результатов, чем раньше, потому что группы с одинаковым именем теперь будут иметь отдельные строки в результатах. Для меня это было бы лучше, чем скрывать дублирующиеся группы, потому что база данных незаметно выбрала значение произвольно!

Хорошей практикой также является квалификация всех столбцов по имени таблицы или псевдониму, если задействовано более одной таблицы ...

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
davmos
источник
Спасибо, это прекрасно работает. Я также согласен, что мне нужно квалифицировать столбцы. Это устраняет любую возможность двусмысленности. Я сейчас нахожусь в процессе восстановления кода. Спасибо миллион за помощь.
Дэн Бемовски
У меня даже нет GROUP BYпредложения в моем запросе, но я получаю эту ошибку.
Aitchkhan
@HaroonKhan, это звучит как новый вопрос. Дайте мне знать, если вы спросите, и я посмотрю.
Давмос
2
В MySQL 5.7 они устанавливают свойство, которое требует, чтобы все неагрегированные поля в запросе были GROUP BY. Таким образом, запрос типа SELECT a, SUM (b) FROM table; означает, что поле "a" должно быть в GROUP BY. Так что если у вас нет GROUP BY, вы должны добавить его в запрос. Это все о том, если у вас есть хотя бы одно агрегированное поле в части SELECT вашего запроса.
user1567291
Мне пришлось исправить «предупреждение», добавив элемент в группу по предложению, как предложено в этом ответе, потому что ни один из ответов, предлагающих исправления sql_mode, не работал. (мой MySQL находился в хранимой процедуре, но не знаю, было ли это актуально)
zzapper
355

Вы можете попытаться отключить only_full_group_byнастройку, выполнив следующее:

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 не принимает, NO_AUTO_CREATE_USERпоэтому необходимо удалить.

Weiyuan
источник
85
Это
обходной путь
1
Обновление /etc/mysql/my.cnf (как показано ниже) является более устойчивым решением, поскольку настройки по умолчанию возвращаются после перезапуска. А для тех, кто говорит, что вы должны исправить запрос, иногда это бывает непросто, когда вам нужен быстрый отладочный запрос и вы выполняете что-то вроде: у SELECT COUNT(*), t.* FROM my_table t GROUP BY colвас есть 20-50 столбцов, хотите ли вы тратить столько времени на добавление каждого столбца в группа по?
Shadoweb
3
Это «решение» довольно опасно. Вы можете активировать режимы, которые вы отключили ранее, слепо меняя их, вместо того, чтобы снимать только один флаг настроек. Это может привести к непредвиденному поведению и в худшем случае привести к неверным результатам или помешать работе ваших приложений. Таким образом, я считаю этот ответ неправильным.
Крис С.
1
Я сделал это постоянным путем обновления, выяснив мой текущий sql_mode через SELECT @@sql_mode;и затем добавив результат оттуда в my.cnf:sql_mode=[list of modes from query, minus ONLY_FULL_GROUP_BY]
wbharding
323

Вы можете отключить предупреждающее сообщение, как описано в других ответах, или вы можете понять, что происходит, и исправить это.

Начиная с MySQL 5.7.5, режим SQL по умолчанию включает ONLY_FULL_GROUP_BY, что означает, что когда вы группируете строки и затем выбираете что-то из этих групп, вам нужно явно указать, из какой строки должен быть сделан этот выбор. Mysql должен знать, какую строку в группе вы ищете, что дает вам два варианта

Mysql должен знать, какую строку в группе вы ищете, что дает вам два варианта

  • Вы также можете добавить столбец, который вы хотите, к оператору группы, group by rect.color, rect.valueкоторый в некоторых случаях может быть тем, что вам нужно, в противном случае вы получите повторяющиеся результаты с тем же цветом, который вам может не понадобиться
  • Вы также можете использовать агрегатные функции mysql, чтобы указать, какую строку вы ищете в группах, например AVG() MIN() MAX() полный список
  • И, наконец, вы можете использовать, ANY_VALUE()если вы уверены, что все результаты внутри группы одинаковы. доктор
azerafati
источник
25
Я не думаю, что MySQL поддерживает FIRST()метод / функцию?
Бен Гильдия
3
Я считаю, что MySQL (по крайней мере до 5.7) по умолчанию принимает первое значение, которое он находит в группе. Таким образом, FIRST () не существует, поскольку раньше он был синонимом самой функции GROUP BY.
DrDamnit
2
@DrDamnit, я считаю, что это было больше похоже на случайный отбор в таких случаях, что приводило к путанице и, таким образом ONLY_FULL_GROUP_BY,
включало
6
Я уже знал ответ, но пример с цветным прямоугольником так легко понять, что я буду использовать его в следующий раз, когда кто-нибудь спросит меня об этом.
user327961
@azerafati - это что-то вроде FIRST () или какой-то запрос, который можно использовать для получения первого значения, как в случае со мной.
Харис
169

Если вы не хотите вносить какие-либо изменения в текущий запрос, выполните следующие действия:

  1. бродяги ssh в вашу коробку
  2. Тип: sudo vim /etc/mysql/my.cnf
  3. Прокрутите до конца файла и введите, Aчтобы войти в режим вставки.
  4. Скопировать и вставить

    [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. Тип escдля выхода из режима ввода

  6. Введите, :wqчтобы сохранить и закрыть Vim.
  7. Введите, sudo service mysql restartчтобы перезапустить MySQL.
Аджай
источник
Просто на голову всем пользователям Vagrant, пытающимся использовать это с AdonisJS, это необходимо для запуска paginateфункции.
Майкл Дж. Калкинс
Это очевидно не только для пользователей Vagrant, но и для любой системы Unix. Обратите внимание, что для меня расположение my.cnfфайла было /etc. Также обратите внимание на новый синтаксис с тех порMySQL 5.7.8: sql-mode="STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
Валентин Грегуар
Работает для меня в Mint 19.3 (Ubuntu 18.04), спасибо
Марко Лопес
71

Используйте ANY_VALUE()для ссылки на неагрегированный столбец.

SELECT name,           address , MAX(age) FROM t GROUP BY name; -- fails
SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name; -- works

Из MySQL 5.7 документов :

Вы можете добиться того же эффекта без отключения ONLY_FULL_GROUP_BY , используя ANY_VALUE()ссылку на неагрегированный столбец.

...

Этот запрос может быть недопустимым с ONLY_FULL_GROUP_BYвключенным, поскольку столбец неагрегированного адреса в списке выбора не указан в GROUP BYпредложении:

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

...

Если вы знаете, что для данного набора данных каждое значение имени фактически однозначно определяет значение адреса, адрес эффективно функционально зависит от имени. Чтобы указать MySQL принять запрос, вы можете использовать ANY_VALUE()функцию:

SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;
Итало Борссатто
источник
1
Как человек, который уже давно сталкивался с этой проблемой - мне особенно нравится это решение, потому что оно не требует изменения каких-либо файлов или настроек в настройках MySQL. Интересно, что я ANY_VALUE нигде не упоминал о подобных обсуждениях - у меня отлично работает с моей GROUP_BY contains nonaggregated columnошибкой.
adstwlearn
50

Я постараюсь объяснить вам, о чем эта ошибка.
Начиная с MySQL 5.7.5, опция ONLY_FULL_GROUP_BYвключена по умолчанию.
Таким образом, согласно стандарту SQL92 и ранее:

не разрешает запросы, для которых список выбора, условие HAVING или список ORDER BY ссылаются на неагрегированные столбцы, которые не названы ни в предложении GROUP BY, ни функционально зависят (однозначно определяются) столбцами GROUP BY

( подробнее в документации )

Так, например:

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

Вы получите сообщение об ошибке после выполнения запроса выше.

# 1055 - Выражение # 1 списка SELECT отсутствует в предложении GROUP BY и содержит неагрегированный столбец «testsite.user.id», который функционально не зависит от столбцов в предложении GROUP BY; это несовместимо с sql_mode = only_full_group_by

Почему?
Потому что MySQL точно не понимает, какие определенные значения из сгруппированных записей нужно извлекать, и в этом суть.

IE скажем, у вас есть эти записи в вашей usersтаблице:
Эй

И вы выполните неверный запрос, показанный выше.
И вы получите ошибку, показанную выше, потому что есть 3 записи с именем John, и это хорошо, но все они имеют разные emailзначения полей.
Итак, MySQL просто не понимает, какие из них возвращать в результирующей сгруппированной записи.

Вы можете решить эту проблему, просто изменив свой запрос следующим образом:

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

Кроме того, вы можете добавить дополнительные поля в раздел SELECT, но вы не можете сделать это, если они не агрегированы, но есть костыль, который вы можете использовать (но настоятельно не рекомендуется):

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

введите описание изображения здесь

Теперь вы можете спросить, почему использование ANY_VALUEне рекомендуется?
Поскольку MySQL точно не знает, какое значение сгруппированных записей нужно извлечь, и с помощью этой функции вы просите ее извлечь любую из них (в этом случае было получено письмо первой записи с именем = Джон).
Точно не могу придумать какие-либо идеи о том, почему вы хотели бы, чтобы это поведение существовало.

Пожалуйста, если вы меня не понимаете, узнайте больше о том, как работает группировка в MySQL, это очень просто.

И, наконец, вот еще один простой, но правильный запрос.
Если вы хотите запросить общее количество пользователей в соответствии с доступным возрастом, вы можете записать этот запрос

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

Который полностью действителен, согласно правилам MySQL.
И так далее.

Важно понять, в чем именно проблема, и только потом записать решение.

Авраам Тугалов
источник
Спасибо за этот хороший ответ. Что если я тоже хочу получать электронные письма в виде массива в группе результатов?
Иосия
1
Вы всегда можете GROUP_CONCAT обязательное поле, чтобы получить каждую запись там.
Авраам
Ой! так что есть способ! Пожалуйста, помогите мне в этом вопросе
Иосия
Спасибо за этот отличный ответ. Что если я захочу сделать GROUP BY DAY(created_date)? Похоже, это не работает, если DAY()добавить.
эскимос
41

Я использую Laravel 5.3, MySQL 5.7.12, на усадьбе Laravel (я думаю, 0.5.0)

Даже после явной настройки редактирования /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

Я все еще получал ошибку.

Мне пришлось изменить config/database.phpс trueна false:

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

Дальнейшее чтение:

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

Райан
источник
Dang! Это было полезно для пользователей Laravel. +1 за это.
Okafor T Kosiso
20

Если вы используете wamp 3.0.6 или более раннюю версию, кроме стабильной версии 2.5, вы можете столкнуться с этой проблемой, во-первых, проблема с sql. Вы должны назвать поля соответственно. но есть другой способ, которым вы можете решить это. нажмите на зеленый значок Wamp. mysql-> mysql settings-> sql_mode-> none. или из консоли вы можете изменить значения по умолчанию.

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';
PiyaChakraborty
источник
фантастика, show variables like '%sql_mode%';основанная на том, что он сказал, вставьте команду в командной строке mysql, настройка sql_mode будет выставлена
Jade Han
19

Добавление строк (упомянуто ниже) в файле: /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

Работай нормально для меня. Версия сервера: 5.7.18-0ubuntu0.16.04.1 - (Ubuntu)

Джагдип Сингх
источник
1
вы используете RDS?
Мубасшир Пол
@MubasshirPawle Нет
Джагдип Сингх
16

Перейдите в mysql или phpmyadmin и выберите базу данных, затем просто выполните этот запрос, и он будет работать. Это работает нормально для меня.

SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
Анил Гупта
источник
Отлично .. Спасибо :)
mohitesachin217
Мое удовольствие :) @mohit
Анил Гупта
Спасло меня кучу времени, спасибо.
JoeRaid
1
Добро пожаловать, брат @TharinduEranga
Анил Гупта
1
Но проблема с решением заключается в том, что каждый раз, когда я перезапускаю mysql, мне нужно снова выполнить запрос. Это означает, что решение не является постоянным или постоянным.
Мушфикур Рахман
10

Для Mac:

1. Скопируйте файл по умолчанию my-default.cnf в /etc/my.cnf

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

2. Измените sql_mode в my.cnf, используя ваш любимый редактор, и установите его в этом

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. Перезапустите сервер MySQL.

mysql.server restart
Танвир Хасан Аник
источник
Спасибо, сработало и у меня. Но я использую mysql по умолчанию для Mac, поэтому у меня нет пути варки. Просто sudo cp my-default.cnf /etc/my.cnf
Майк Нгуен
2
@Mike Nguyen sudo cp /usr/local/mysql-5.7.17-macos10.12-x86_64/support-files/my-default.cnf /etc/my.cnf sudo vi /etc/my.cnf set sql_model 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.
Ён Гао
7

Вот что помогло мне понять всю проблему:

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

И в следующем другом примере проблемного запроса.

Проблематично:

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 = #

Решено, добавив это в конец:

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

Примечание: смотрите сообщение об ошибке в PHP, оно говорит вам, в чем проблема.

Пример:

Ошибка запроса MySQL 1140: В агрегированном запросе без GROUP BY выражение № 4 списка SELECT содержит неагрегированный столбец 'db.gameplay.timestamp'; это несовместимо с sql_mode = only_full_group_by - Запрос: SELECT COUNT (*) как попытки, SUM (истекшее) как elapsedtotal, userid, timestamp, questionid, answerid, SUM (правильное) как правильное, elapsed, ipaddress FROM gameplay WHERE timeSmp>> DATE (СЕЙЧАС (), ИНТЕРВАЛ 1 ДЕНЬ) И ИДЕНТИФИКАТОР = 1

В этом случае выражение № 4 отсутствовало в GROUP BY.

Кай Ноак
источник
1
Спасибо за указание stackoverflow.com/questions/20074562/...
cwhsu
Отличный ответ, чтобы решить эту проблему. Большое спасибо
Hansana Athukorala
7

Для localhost / wampserver 3 мы можем установить sql-mode = user_mode, чтобы удалить эту ошибку:

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

затем перезагрузите wamp или apache

Брахам Дев Ядав
источник
1
Я знаю, что это не может быть долгосрочным решением этой проблемы, но это, безусловно, помогло на данный момент. Моя хостинговая компания Go Daddy использует MySQL V5.6.33, а WampServer 3 использует MySQL V5.7.14. Спасибо за это быстрое решение проблемы
Алан Н
4

Вы можете добавить unique indexк group_id; если вы уверены, что group_idэто уникально.

Это может решить ваш случай без изменения запроса .

Поздний ответ, но он еще не упоминался в ответах. Возможно, это должно завершить уже исчерпывающие доступные ответы. По крайней мере, это решило мой случай, когда мне пришлось разделить таблицу со слишком большим количеством полей.

micaball
источник
2

Если у вас есть эта ошибка с Symfony, использующим конструктор запросов doctrine , и если эта ошибка вызвана orderBy :

Обратите внимание selectна столбец, который вы хотите groupBy, и используйте addGroupByвместо groupBy:

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

Работает на Symfony3 -

Гангай Иоганн
источник
1

Извините за то, что не используете ваш точный SQL

Я использовал этот запрос, чтобы преодолеть предупреждение Mysql.

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

обратите внимание на ключ для меня

count(*) AS cnt
Гарри Бош
источник