MySQL: возможно ли отобразить агрегированные данные1 и агрегированные данные2 в одной итоговой таблице? - PullRequest
2 голосов
/ 23 марта 2019

Предположим, у нас есть эта таблица mytable:

+--------+-------+-----+
| City   | User  | Amt |
+--------+-------+-----+
| London | John  | 100 |
| London | John  | 200 |
| London | James | 300 |
| London | James |  50 |
| Paris  | Jean  | 100 |
+--------+-------+-----+

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

+--------+-------+------------+------------+
| City   | User  | AmtPerUser | AmtPerCity |
+--------+-------+------------+------------+
| London | John  |        300 |        650 |
| London | James |        350 |        650 |
| Paris  | Jean  |        100 |        100 |
+--------+-------+------------+------------+

Это можно сделать с помощью следующего запроса:

SELECT t1.City, User, AmtPerUser, AmtPerCity
FROM
    (SELECT City, User, SUM(Amt) as AmtPerUser FROM mytable GROUP BY City, User) t1
  JOIN
     (SELECT City, SUM(Amt) as AmtPerCity FROM mytable GROUP BY City ) t2
  USING (City);

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

Так что мне интересно, есть ли более эффективный способ выполнить эту задачу.

03/25/2019 ОБНОВЛЕНИЕ

Спасибо за предоставленные решения. В настоящее время я все еще использую старый MySQL 5.1.
Полезно знать о оконных функциях в MySQL 8.

Я проверил запросы на большем наборе (данные, перечисленные здесь, скопированы 3600 раз, чтобы получить 18К строк). Это лучший на сегодняшний день:

SELECT City, User, SUM(Amt) as AmtPerUser,
       SUM(SUM(Amt)) OVER (PARTITION BY City) as AmtPerCity
FROM mytable
GROUP BY City, User;

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

@ GordonLinoff: 43мс
Мой оригинальный запрос: 70мс
@GMB: 135 мс

Также оказалось, что наличие или отсутствие индексов не способствует.

Ответы [ 3 ]

1 голос
/ 23 марта 2019

В MySQL 8.0 вы можете использовать оконные функции вместе с SELECT DISTINCT для получения того же результата:

SELECT DISTINCT
    city, 
    user, 
    SUM(amt) OVER(PARTITION BY City, User) AmtPerUser,
    SUM(amt) OVER(PARTITION BY City) AmtPerCity
FROM mytable

Оконные функции обычно работают лучше, чем эквивалентные агрегированные запросы.Но если вы действительно беспокоитесь о производительности, то вам все равно нужно создавать индексы (возможно, составной индекс на (city, user)).

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

| city   | user  | AmtPerUser | AmtPerCity |
| ------ | ----- | ---------- | ---------- |
| London | James | 350        | 650        |
| London | John  | 300        | 650        |
| Paris  | Jean  | 100        | 100        |
1 голос
/ 23 марта 2019

Использовать оконные функции:

SELECT City, User, SUM(Amt) as AmtPerUser,
       SUM(SUM(Amt)) OVER (PARTITION BY City) as AmtPerCity
FROM mytable
GROUP BY City, User;

Примечание. Предполагается, что MySQL 8+.

В более ранних версиях ваша версия с JOIN и двумя GROUP BY s, вероятно, является лучшим подходом.

0 голосов
/ 23 марта 2019

Вы можете использовать запрос GROUP BY ... WITH ROLLUP для получения итоговых значений City в другой строке в ваших выходных данных (и в целом в итоговой строке):

SELECT City, User, SUM(Amt)
FROM mytable
GROUP BY City, User WITH ROLLUP

Выход:

City    User    SUM(Amt)
London  James   350
London  John    300
London  null    650
Paris   Jean    100
Paris   null    100
null    null    750

Вы можете распознать строки ROLLUP по значениям NULL - итоговое значение для каждого города имеет название города для City и NULL для User, в то время как в строке общего итога есть NULL для оба City и User. Хотя это не совсем тот формат, который вы ищете, он будет более эффективным.

Демонстрация на dbfiddle

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...