MySQL - выбор столбца не в группе по
Вопрос
Я пытаюсь добавить функции в уже существующее приложение, и я натолкнулся на представление MySQL примерно так:
SELECT
AVG(table_name.col1),
AVG(table_name.col2),
AVG(table_name.col3),
table_name.personID,
table_name.col4
FROM table_name
GROUP BY table_name.personID;
Хорошо, есть несколько агрегатных функций. Вы можете выбрать personID, потому что вы группируете по нему. Но он также выбирает столбец, который не входит в составную функцию и не является частью предложения GROUP BY. Как это возможно??? Это просто выбирает случайное значение, потому что значения определенно не уникальны для группы?
Откуда я (MSSQL Server), это ошибка. Может кто-нибудь объяснить мне это поведение и почему это разрешено в MySQL?
Решение
Это правда, что эта функция разрешает некоторые неоднозначные запросы и молча возвращает набор результатов с произвольным значением, выбранным из этого столбца. На практике это, как правило, значение из строки в группе, которое физически сохраняется первым.
Эти запросы не являются неоднозначными, если вы выбираете только те столбцы, которые функционально зависят от столбцов в критериях GROUP BY. Другими словами, если может быть только одно отличное значение «неоднозначного»; столбец на значение, которое определяет группу, нет проблем. Этот запрос будет недопустимым в Microsoft SQL Server (и ANSI SQL), даже если он не может привести к неоднозначности:
SELECT AVG(table1.col1), table1.personID, persons.col4
FROM table1 JOIN persons ON (table1.personID = persons.id)
GROUP BY table1.personID;
Кроме того, MySQL имеет режим SQL, чтобы заставить его вести себя в соответствии со стандартом: <код> ONLY_FULL_GROUP_BY код> р>
FWIW, SQLite также разрешает эти неоднозначные предложения GROUP BY, но выбирает значение из последней строки в группе. & # 8224;
<Ч>& # 8224; По крайней мере в версии, которую я тестировал. Быть произвольным означает, что MySQL или SQLite могут изменить свою реализацию в будущем и вести себя по-другому. Поэтому вам не следует полагаться на то, что поведение остается таким, каким оно является в настоящее время в неоднозначных случаях, подобных этому. Лучше переписать ваши запросы, чтобы они были детерминированными, а не двусмысленными. Вот почему MySQL 5.7 теперь по умолчанию включает ONLY_FULL_GROUP_BY.
Другие советы
Я должен был погуглить немного дольше ... Кажется, я нашел мой ответ .
MySQL расширяет использование GROUP BY так что вы можете использовать неагрегированные столбцы или расчеты в списке SELECT которые не отображаются в GROUP BY пункт. Вы можете использовать эту функцию для получить лучшую производительность, избегая ненужная сортировка столбцов и группировка. Например, вам не нужно группировать по customer.name в следующий запрос
В стандартном SQL вы должны добавить customer.name к предложению GROUP BY. В MySQL имя является избыточным.
Тем не менее, это только кажется ... неправильно.
select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)
Допустим, у вас есть такой запрос:
SELECT g, v
FROM t
GROUP BY g;
В этом случае для каждого возможного значения для g
mysql выбирает одно из соответствующих значений v
.
Однако, какой из них выбран, зависит от некоторых обстоятельств. Р>
Я где-то читал, что для каждой группы g первое значение v
сохраняется в том порядке, как записи были вставлены в таблицу t
. Р>
Это довольно уродливо, потому что записи в таблице должны рассматриваться как набор , где порядок элементов не должен иметь значения. Это так "mysql-ish" ...
Если вы хотите определить, какое значение для v
оставить, вам нужно применить подвыбор для t
следующим образом:
SELECT g, v
FROM (
SELECT *
FROM t
ORDER BY g, v DESC
) q
GROUP BY g;
Таким образом, вы определяете, в каком порядке записи подзапроса обрабатываются внешним запросом, поэтому вы можете доверять, какое значение Однако, если вам нужны условия WHERE, будьте очень осторожны. Если вы добавите условие WHERE в подзапрос, то оно сохранит поведение, оно всегда вернет ожидаемое вами значение: Это то, что вы ожидаете, вложенный выбор фильтрует и упорядочивает таблицу. Он хранит записи, в которых Однако, если вы добавите такое же условие WHERE к внешнему запросу, вы получите недетерминированный результат: Удивительно, но вы можете получить разные значения для Он выбирает значение для Если вместо добавления внешнего WHERE вы указываете условие HAVING, например: Затем вы снова получаете последовательное поведение. Р>
ЗАКЛЮЧЕНИЕ: я бы предложил вообще не полагаться на эту технику. Если вы действительно хотите / должны избегать условий WHERE во внешнем запросе. Используйте его во внутреннем запросе, если можете, или в предложении HAVING во внешнем запросе. Р>
Я проверил это с этими данными: в MySQL 5.6.41. Р>
Может быть, это просто ошибка, которая исправляется / исправляется в более новых версиях, пожалуйста, оставьте отзыв, если у вас есть опыт работы с более новыми версиями. Р> v
будет выбрано для отдельных значений g код>. Р>
SELECT g, v
FROM (
SELECT *
FROM t
WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
ORDER BY g, v DESC
) q
GROUP BY g;
g
имеет заданное значение, а внешний запрос возвращает это g
и первое значение для v
. Р>
SELECT g, v
FROM (
SELECT *
FROM t
-- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
ORDER BY g, v DESC
) q
WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g;
v
при повторном выполнении одного и того же запроса, что ... странно. Ожидаемое поведение - получить все записи в соответствующем порядке из подзапроса, отфильтровав их во внешнем запросе, а затем выбрав их так же, как в предыдущем примере. Но это не так. Р>
v
, казалось бы, случайно. Тот же запрос возвращал разные значения для v
, если я выполнял больше (~ 20) раз, но распределение было неравномерным. Р>
SELECT g, v
FROM (
SELECT *
FROM t1
-- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
ORDER BY g, v DESC
) q
-- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g
HAVING g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9';
CREATE TABLE t1 (
v INT,
g VARCHAR(36)
);
INSERT INTO t1 VALUES (1, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');
INSERT INTO t1 VALUES (2, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');