MySQL 8.0 GROUP BY / FULL_GROUP_BY - PullRequest
       11

MySQL 8.0 GROUP BY / FULL_GROUP_BY

0 голосов
/ 08 февраля 2019

После обновления до MySQL 8 у меня появилось много запросов, которые не соответствуют новой настройке MySQL для полной группы.Ниже приведена упрощенная версия одного из запросов.У меня их много, и прежде чем пройтись по всему коду, я хочу полностью разобраться в проблеме.

У меня есть следующие записи в базе данных:

[prod_id] => 1
[prod_name] => Product 1
[prod_new] => 50.00
[prod_size] => L

[prod_id] => 2
[prod_name] => Product 1
[prod_new] => 45.00
[prod_size] => M

[prod_id] => 3
[prod_name] => Product 1
[prod_new] => 40.00
[prod_size] => S

[prod_id] => 4
[prod_name] => Product 4
[prod_new] => 100.00
[prod_size] => M

[prod_id] => 5
[prod_name] => Product 5
[prod_new] => 200.00
[prod_size] => M

Когда я запустилСледующий запрос в MySQL 5.x, я получил 3 результата.Содержит продукты 1, 4, 5. С соответствующим названием, ценой и размером.

SELECT prod_id, prod_name, prod_price, prod_size 
FROM prod_product
GROUP BY prod_name

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

SELECT MAX(prod_id), prod_name, ANY_VALUE(prod_price), ANY_VALUE(prod_size)
FROM prod_product
GROUP BY prod_name

Это даст мне идентификаторы продукта 3, 4, 5. Но с идентификатором продукта 3 это даст мне цену и размер.идентификатора продукта 1.

Очевидно, что это нежелательное поведение.Я бы предположил, что, поскольку prod_id является первичным ключом, база данных знает, какие значения показывать с соответствующим идентификатором.Когда я говорю MAX(prod_id), это уже указывает на одну запись в этой группе, зачем мне давать значения других записей из этой группы?

Я предполагаю, что упускаю что-то важное здесь.=)

Спасибо!

Ответы [ 2 ]

0 голосов
/ 08 февраля 2019

Я бы предположил, что, поскольку prod_id является первичным ключом, база данных знает, какие значения показывать с соответствующим идентификатором.

С чего бы это знать?

Рассмотрим следующие запросы:

SELECT prod_name, prod_id, MIN(prod_price), MAX(prod_price)
FROM prod_product
GROUP BY prod_name

Какое значение должно возвращаться для prod_id здесь?Продукт, который соответствует минимальной цене?Или продукт, который соответствует максимальной цене?

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

SELECT prod_name, prod_id, AVG(prod_price)
FROM prod_product
GROUP BY prod_name

Теперь, какой prod_id он должен вывести?Совокупный расчет AVG() может вернуть значение, которое не соответствует ни одному из продуктов.

То же самое происходит с совокупностью SUM().

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

0 голосов
/ 08 февраля 2019

Если вы хотите, чтобы первая запись в каждой группе строк имела одинаковый prod_name, упорядоченный по prod_id, вы можете использовать оконную функцию ROW_NUMBER(), которая доступна в MySQL 8:

SELECT x.prod_id, x.prod_name, x.prod_new, x.prod_size
FROM (
    SELECT 
        p.prod_id, p.prod_name, p.prod_new, p.prod_size, 
        ROW_NUMBER() OVER(PARTITION BY p.prod_name ORDER BY p.prod_id) rn
    FROM prod_product p
) x WHERE x.rn = 1

Внутренний запрос присваивает номер каждой записи в каждой группе, а внешний запрос фильтрует первую запись в каждой группе.

Демонстрация на DB Fiddle :

WITH prod_product AS (
    SELECT 1 prod_id, 'Product 1' prod_name, 50 prod_new, 'L' prod_size
    UNION ALL SELECT 2, 'Product 1', 45, 'M'
    UNION ALL SELECT 3, 'Product 1', 40, 'S'
    UNION ALL SELECT 4, 'Product 4', 100, 'M'
    UNION ALL SELECT 5, 'Product 5', 200, 'M' 
)
SELECT x.prod_id, x.prod_name, x.prod_new, x.prod_size
FROM (
    SELECT 
        p.prod_id, p.prod_name, p.prod_new, p.prod_size, 
        ROW_NUMBER() OVER(PARTITION BY p.prod_name ORDER BY p.prod_id) rn
    FROM prod_product p
) x WHERE x.rn = 1;
| prod_id | prod_name | prod_new | prod_size |
| ------- | --------- | -------- | --------- |
| 1       | Product 1 | 50       | L         |
| 4       | Product 4 | 100      | M         |
| 5       | Product 5 | 200      | M         |
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...