MySQL - выбор столбца не в группе по - PullRequest
45 голосов
/ 21 июня 2009

Я пытаюсь добавить функции в ранее существовавшее приложение, и я наткнулся на представление 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?

Ответы [ 4 ]

47 голосов
/ 21 июня 2009

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

Эти запросы не являются неоднозначными, если вы выбираете только те столбцы, которые функционально зависят от столбцов в критериях 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, но выбирает значение из последней строки в группе.


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

11 голосов
/ 21 июня 2009

Я должен был погуглить немного дольше ... Кажется, я нашел мой ответ .

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

В стандартном SQL вы должны добавить customer.name к предложению GROUP BY. В MySQL имя избыточно.

Тем не менее, это только кажется ... неправильным.

0 голосов
/ 05 декабря 2018

Допустим, у вас есть такой запрос:

SELECT g, v 
FROM t
GROUP BY g;

В этом случае для каждого возможного значения g mysql выбирает одно из соответствующих значений v.

Однако, какой из них выбран, зависит от некоторых обстоятельств.

Я где-то читал, что для каждой группы g сохраняется первое значение v в том порядке, как записи были вставлены в таблицу t.

Это довольно уродливо, потому что записи в таблице должны рассматриваться как набор , где порядок элементов не должен иметь значения. Это так "mysql-иш" ...

Если вы хотите определить, какое значение для v оставить, вам нужно применить подвыбор для t, например:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        ORDER BY g, v DESC
) q
GROUP BY g;

Таким образом, вы определяете, в каком порядке записи подзапроса обрабатываются внешним запросом, поэтому вы можете доверять, какое значение v будет выбрано для отдельных значений g.

Однако, если вам нужны условия WHERE, будьте очень осторожны. Если вы добавите условие WHERE в подзапрос, то оно сохранит поведение, оно всегда вернет ожидаемое вами значение:

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.

Однако, если вы добавите такое же условие WHERE к внешнему запросу, вы получите недетерминированный результат:

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) раз, но распределение было неравномерным.

Если вместо добавления внешнего WHERE вы указываете условие HAVING следующим образом:

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';

Тогда вы снова получаете последовательное поведение.

ЗАКЛЮЧЕНИЕ: Я бы предложил вообще не полагаться на эту технику. Если вы действительно хотите / должны избегать условий WHERE во внешнем запросе. Используйте его во внутреннем запросе, если можете, или в предложении HAVING во внешнем запросе.

Я проверил это с этими данными:

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');

в mysql 5.6.41.

Может быть, это просто ошибка, которая исправляется / исправляется в более новых версиях, пожалуйста, оставьте отзыв, если у вас есть опыт работы с более новыми версиями.

0 голосов
/ 15 апреля 2013
select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...