Почему MySQL позволяет группировать по запросам БЕЗ агрегатных функций?

Сюрприз - это вполне корректный запрос в MySQL:

select X, Y from someTable group by X

Если вы попробуете этот запрос в Oracle или SQL Server, вы получите естественное сообщение об ошибке:

Column 'Y' is invalid in the select list because it is not contained in 
either an aggregate function or the GROUP BY clause.

Итак, как MySQL определяет, какой Y отображать для каждого X? Он просто выбирает одного. Насколько я могу судить, он просто выбирает первую Y, которую находит. Обоснование состоит в том, что если Y не является ни агрегатной функцией, ни предложением group by, то указание «выбрать Y» в вашем запросе не имеет смысла для начала. Следовательно, я как движок базы данных верну все, что захочу, и вам это понравится.

Есть даже параметр конфигурации MySQL, чтобы отключить эту «неплотность». http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_only_full_group_by

В этой статье даже упоминается, как MySQL подвергался критике за несовместимость с ANSI-SQL в этом отношении. http://www.oreillynet.com/databases/blog/2007/05/debunking_group_by_myths.html

У меня вопрос: Почему был разработан MySQL именно таким образом? Что послужило причиной отказа от ANSI-SQL?


person Aaron Fi    schedule 03.08.2009    source источник
comment
Позвольте мне сказать это так. Я считаю этот выбор дизайна эквивалентом языка программирования, который разрешает и игнорирует, например, разрешение null быть левым значением. например null = 3. Нет причин позволять этому случиться. Это ошибка, которая всегда опасно ошибочна.   -  person Aaron Fi    schedule 04.08.2009
comment
@lumpynose, чушь, это могло быть правдой до 5.x   -  person Johan    schedule 31.08.2011
comment
@lumpynose Можете ли вы дать ссылку на ваше утверждение?   -  person Barranka    schedule 16.02.2013
comment
О, боже, я так хотел чего-то похожего в SQL Server. Я всегда представлял себе это, поскольку мне нужна функция агрегации PICKANY (col) или, что еще лучше, функция UNIQUEVALUE (col), которая приведет к ошибке, если окажется, что она НЕ будет постоянной во всей группе. Я обычно использую MAX (col), поскольку все значения у меня одинаковы, дайте мне эту функцию агрегирования значений   -  person Brondahl    schedule 01.05.2019
comment
Извините, приведенный выше комментарий был бессмысленным выражением ИМО, ответ будет заключаться в том, что эта функция действительно полезна. Но у меня нет цитаты, кроме «потому что я этого хочу».   -  person Brondahl    schedule 01.05.2019


Ответы (6)


Я считаю, что это было для случая, когда группировка по одному полю будет означать, что другие поля также группируются:

SELECT user.id, user.name, COUNT(post.*) AS posts 
FROM user 
  LEFT OUTER JOIN post ON post.owner_id=user.id 
GROUP BY user.id

В этом случае user.name всегда будет уникальным для каждого user.id, поэтому удобно не требовать user.name в предложении GROUP BY (хотя, как вы говорите, существует определенная область для проблем)

person Cebjyre    schedule 04.08.2009
comment
Значит, это было просто для экономии набора текста (как при вводе с клавиатуры)? Хех. - person Aaron Fi; 04.08.2009
comment
Меньше столбцов в предложении GROUP BY означает более быстрое время выполнения, поэтому это оптимизирующий прием. Я постоянно использую MAX(user.name) AS name в аналогичных запросах на реализациях ANSI SQL. - person wqw; 04.08.2009
comment
@wqw: чушь. user.name находится либо в группе по, либо в совокупности. В лучшем случае это неоднозначно. См. Комментарии к stackoverflow.com/questions/6060241/ Только MySQL допускает такие болтовни, например stackoverflow.com/q/6642241/27535 - person gbn; 10.07.2011
comment
@gbn: Мусор к чему? Использование чего-то вроде SELECT user.id, ANY(user.name) .. GROUP BY user.id не является недетерминированным, если в user.id есть ПК. Очевидно, что корзины user.name будут содержать одно уникальное значение - имя пользователя, чей идентификатор сгруппирован. Использование MAX(user.name) AS name или любого другого агграгейта в лучшем случае неудобно (хотя намерение более четко задокументировано). Может быть, это зависит снова .. - person wqw; 18.07.2011
comment
Postgresql обеспечивает такую ​​функциональность контролируемым образом: ienablemuch.com/2010/ 08 / postgresql -ising-financial.html. - person Michael Buen; 25.08.2012
comment
AFAIK последний стандарт SQL требует, чтобы все выбранные поля были либо полями группы, либо агрегированными полями или полями, определяемыми для группы, например user.name в приведенном выше примере. Таким образом, MySQL соответствует последнему стандарту, поскольку они позволяют вам выбирать поля, которые не являются ни групповыми полями, ни агрегированными полями, но являются определяющими для группы - то, что многие другие СУБД не допускают. Однако MySQL нарушает стандартное соответствие, не проверяя, действительно ли поле определено, что позволяет также допускать неопределенные поля. [продолжение следует] - person Thorsten Kettner; 28.05.2014
comment
Вероятно, СУБД сложно надежно определить, будет ли выбранное поле определенным или нет. Postgres в этом вопросе слишком строг, MySQL слишком послушен. - person Thorsten Kettner; 28.05.2014

Согласно этой странице (интерактивное руководство по 5.0 ), это для лучшей производительности и удобства пользователя.

person Miroslav Genev    schedule 10.08.2011
comment
+1, за ответ MySQL :). Я использую это все время, чтобы избежать выполнения подзапроса, который выполняет ORDER BY ... LIMIT 1 ... вам просто нужно быть осторожным, зная, что данные, которые вы получаете в неагрегированных столбцах, будут случайными для всех строк, соответствующих вашим условиям. - person Kevin Nelson; 01.07.2014
comment
Линк мертв, можно его обновить? или еще лучше вставьте сюда документы на случай, если он снова сломается - person szx; 28.03.2017

К сожалению, почти все разновидности SQL имеют ситуации, когда они нарушают ANSI и приводят к непредсказуемым результатам.

Мне кажется, что они намеревались рассматривать ее как функцию FIRST (Y), которая есть во многих других системах.

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

person Rob Farley    schedule 04.08.2009
comment
Это определенно так. - person deadlock; 15.07.2020

MySQL рассматривает это как один столбец DISTINCT, когда вы используете GROUP BY без агрегатной функции. Используя другие параметры, вы либо получите отдельный результат, либо должны использовать подзапросы и т. Д. Вопрос в том, действительно ли результаты предсказуемы.

Кроме того, полезная информация находится в этой ветке.

person GL_Stephen    schedule 04.01.2012

Из того, что я прочитал на справочной странице mysql, говорится: «Вы можете использовать эту функцию, чтобы повысить производительность, избегая ненужной сортировки и группировки столбцов. Однако это полезно в первую очередь, когда все значения в каждом неагрегированном столбце не имеют имени в GROUP BY одинаковы для каждой группы ".

Предлагаю вам прочитать эту страницу (ссылка на справочное руководство mysql): http://dev.mysql.com/doc/refman/5.5/en//group-by-extensions.html

person Giancarlo Nebiolo Navidad    schedule 15.02.2013

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

Таблицы

USER
user_id | name

USER_LOGIN_HISTORY 
user_id | date_logged_in

USER_LOGIN_HISTORY имеет несколько строк для одного пользователя, поэтому, если я присоединю к нему пользователей, он вернет много строк. поскольку меня интересует только последняя запись, я бы сделал это

select 
  user_id,
  name,
  date_logged_in

from(

  select 
    u.user_id, 
    u.name, 
    ulh.date_logged_in

  from users as u

    join user_login_history as ulh
      on u.user_id = ulh.user_id

  where u.user_id = 1234

  order by ulh.date_logged_in desc 

)as table1

group by user_id

Это вернет одну строку с именем пользователя и временем последнего входа в систему.

person Nick Dennies    schedule 25.08.2012
comment
Мой пример выше используется только для того, чтобы показать, как вы можете манипулировать возвращаемым результатом. Я не говорю, что это самый простой способ вернуть эту информацию. Вы бы использовали функцию MAX. При гораздо более сложных запросах становится очень полезной возможность группировки без использования агрегатных функций во всех других полях. - person Nick Dennies; 25.08.2012
comment
Этот надуманный пример и длиннее, и медленнее, чем просто выполнение прямого максимума, и поэтому не поддерживает ваше утверждение о том, что это вообще очень полезный инструмент. Если вы не можете даже придумать пример его полезности, я серьезно сомневаюсь в его полезности. Я также не верю, что намеренное использование того, что часто является неопределенной функциональностью, принесет больше пользы в более сложных запросах. - person Tim Gautier; 23.04.2015