Рассчитать промежуточную сумму в MySQL - PullRequest
55 голосов
/ 20 марта 2009

У меня есть запрос MySQL:

SELECT DAYOFYEAR(`date`)  AS d, COUNT(*) 
FROM  `orders` 
WHERE  `hasPaid` > 0
GROUP  BY d
ORDER  BY d

Что возвращает что-то вроде этого:

d  | COUNT(*) |
20 |  5       |
21 |  7       |
22 | 12       |
23 |  4       |

Что мне действительно нужно, так это еще один столбец в конце, показывающий итоговую сумму:

d  | COUNT(*) | ??? |
20 |  5       |   5 |
21 |  7       |  12 |
22 | 12       |  24 |
23 |  4       |  28 |

Возможно ли это?

Ответы [ 7 ]

104 голосов
/ 18 августа 2009

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

SET @runtot:=0;
SELECT
   q1.d,
   q1.c,
   (@runtot := @runtot + q1.c) AS rt
FROM
   (SELECT
       DAYOFYEAR(`date`) AS d,
       COUNT(*) AS c
    FROM  `orders`
    WHERE  `hasPaid` > 0
    GROUP  BY d
    ORDER  BY d) AS q1

Это даст вам дополнительный столбец RT (промежуточный итог). Не пропустите инструкцию SET вверху, чтобы сначала инициализировать переменную промежуточного итога, иначе вы просто получите столбец со значениями NULL.

10 голосов
/ 20 марта 2009
SELECT 
   DAYOFYEAR(O.`date`)  AS d, 
   COUNT(*),
   (select count(*) from `orders` 
       where  DAYOFYEAR(`date`) <= d and   `hasPaid` > 0)
FROM  
  `orders` as O
WHERE  
  O.`hasPaid` > 0
GROUP  BY d
ORDER  BY d

Это потребует некоторой синтаксической настройки (у меня нет MySQL для тестирования), но это показывает вам идею. Подзапрос должен просто вернуться назад и сложить все свежее, что вы уже включили во внешний запрос, и он должен делать это для каждой строки.

Посмотрите на этот вопрос о том, как использовать объединения для достижения того же.

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

2 голосов
/ 18 сентября 2018

Начиная с MySQL 8, вы будете использовать оконные функции для этого вида запроса:

SELECT dayofyear(`date`) AS d, count(*), sum(count(*)) OVER (ORDER BY dayofyear(`date`))
FROM `orders`
WHERE `hasPaid` > 0
GROUP BY d
ORDER BY d

В приведенном выше запросе агрегатная функция count(*) вложена в оконную функцию sum(..) OVER (..), что возможно из-за логического порядка операций в SQL . Если это слишком запутанно, вы можете легко использовать производную таблицу или WITH предложение , чтобы лучше структурировать свой запрос:

WITH daily (d, c) AS (
  SELECT dayofyear(`date`) AS d, count(*)
  FROM `orders`
  WHERE `hasPaid` > 0
  GROUP BY d
)
SELECT d, c, sum(c) OVER (ORDER BY d)
ORDER BY d
1 голос
/ 20 марта 2009

Если у вас нет другого выбора, кроме как сделать это в SQL, я бы суммировал результаты на языке программирования, который выполняет запрос. Подобное вложение будет очень медленным по мере роста стола.

1 голос
/ 20 марта 2009

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

0 голосов
/ 20 марта 2009

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

  • Сделайте это вне MySql или
  • Использовать 5 курсоров MySql
0 голосов
/ 20 марта 2009

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

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