Почему MySQL разрешает запросы "group by" без агрегатных функций?

Сюрприз - это абсолютно корректный запрос в 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 не является ни агрегатной функцией, ни в предложении group by, то указание "выбрать Y" в вашем запросе не имеет смысла начинать. Поэтому я, как механизм базы данных, вернулю все, что захочу, и вам понравится.

Theres даже параметр конфигурации 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?

Ответ 1

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

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.id, поэтому есть удобство в том, чтобы не требовать имя пользователя в предложении GROUP BY (хотя, как вы говорите, есть определенные возможности для проблем)

Ответ 2

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

Ответ 3

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

Звучит так, как будто они предполагали, что к нему относятся как к функции "FIRST (Y)", которую имеют многие другие системы.

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

Rob

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

Таблица

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

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