2009-03-20 4 views
47

У меня есть этот запрос MySQL:Расчет нарастающим итогом в 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 | 

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

+0

Возможный дубликат [Создать кумулятивную колонку суммы в MySQL] (http://stackoverflow.com/questions/2563918/create-a-cumulative-sum-column-in-mysql) – Ztyx

+1

@Ztyx Ваш связанный вопрос был задан более чем через год. Так было бы наоборот. –

ответ

87

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

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.

+1

, который работает блестяще! Глядя на 'EXPLAIN', это показывает, что это намного эффективнее, чем ранее принятый ответ. – nickf

+0

Ключевым моментом является использование подзапроса. Это делает его надежным в сложных запросах, которые включают несколько таблиц или агрегаций. –

+0

Для тех, кто хочет сделать что-то подобное с базовыми функциями MySQL MySQL, обязательно запустите первую строку отдельно (но еще до 2-го). – 2011-08-11 12:50:37

1

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

+0

Учитывая характер реляционной математики и тот факт, что вы используете группу, даже если у mysql есть хак, чтобы сделать это возможным, было бы менее запутанно просто делать это на языке программирования, как предлагает Сергей. –

+6

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

+0

+1 вы правы: это было бы проще и лучше всего в логике программирования - я пытался посмотреть, есть ли какая-то волшебная удивительная функция для этого. – nickf

9
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, чтобы проверить его), но он показывает вам идею. Подзапрос просто должен вернуться и добавить все свежее, что вы уже включили во внешний запрос, и это нужно сделать для каждой строки.

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

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

+0

спасибо - это работает отлично, как есть. – nickf

+1

Имейте в виду, что это будет очень медленным в больших, средних и некоторых небольших базах данных, потому что ему нужно сделать столько запросов, сколько будет в результате строк –

+0

Согласен. Я ответил на этот ответ, потому что он умный, и мы все использовали такие решения, когда это было необходимо, но мы также все знаем, что есть стоимость. Зависит от того, где вам нужно количество операций. Для бизнес-логики? Тогда, возможно, сделайте это в БД. Для просмотра? Сделайте это в коде. –

1

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

+0

Производительность будет расти с размером стола, но не агрессивно, поскольку значение вычисляется и сохраняется. Другие подходы, основанные на подвыборке, будут более дорогими. – Brendan

0

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

0

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

  • ли это за пределами MySql или
  • Использовать MySql 5 Cursors
Смежные вопросы