Создание столбца совокупной суммы в MySQL

У меня есть таблица, которая выглядит так:

id   count
1    100
2    50
3    10

Я хочу добавить новый столбец с именем cumulative_sum, чтобы таблица выглядела так:

id   count  cumulative_sum
1    100    100
2    50     150
3    10     160

Есть ли оператор обновления MySQL, с помощью которого это можно легко сделать? Как лучше всего этого добиться?


person Kirk Ouimet    schedule 01.04.2010    source источник


Ответы (9)


Если производительность является проблемой, вы можете использовать переменную MySQL:

set @csum := 0;
update YourTable
set cumulative_sum = (@csum := @csum + count)
order by id;

В качестве альтернативы вы можете удалить столбец cumulative_sum и вычислять его для каждого запроса:

set @csum := 0;
select id, count, (@csum := @csum + count) as cumulative_sum
from YourTable
order by id;

Это вычисляет текущую сумму в разбеге :)

person Andomar    schedule 01.04.2010
comment
Используйте перекрестное соединение для определения переменной без использования SET. - person OMG Ponies; 02.04.2010
comment
В моей таблице 36 миллионов записей, так что это действительно помогло ускорить процесс! - person Kirk Ouimet; 02.04.2010
comment
Обратите внимание, что упорядочение по кумулятивной_сумме может привести к полному сканированию таблицы. - person matt; 25.09.2012
comment
Это действительно работает и кажется довольно быстрым; какие-либо предложения, как это можно расширить, чтобы сделать кумулятивную сумму в группе? например сгруппировать по Name или аналогично, а затем выполнить кумулятивную сумму только для записей с тем же именем - person zaitsman; 31.08.2017
comment
@zaitsman Вы можете использовать его как подзапрос; во внешнем запросе сгруппируйте все по своему усмотрению, а затем используйте функцию MySQL MAX (), чтобы получить правильную совокупную сводку (последнюю сводку), которая была рассчитана для записей внутри группы. - person Pascal; 12.07.2018
comment
Это не работает с предложением order, которое отличается от порядка, в котором записи встречаются в MySQL. Кажется, что совокупная сумма вычисляется в исходном порядке, а затем скремблируется, когда упорядочивание выполняется, что делает ее больше не совокупной в желаемом смысле. - person Brick; 04.09.2018
comment
Предпочитайте ответ функции OLAP в MySQL 8.0+, как указано в stackoverflow.com/a/52278657/3090068 - person Yuki Inoue; 19.09.2018

Используя коррелированный запрос:


  SELECT t.id,
         t.count,
         (SELECT SUM(x.count)
            FROM TABLE x
           WHERE x.id <= t.id) AS cumulative_sum
    FROM TABLE t
ORDER BY t.id

Использование переменных MySQL:


  SELECT t.id,
         t.count,
         @running_total := @running_total + t.count AS cumulative_sum
    FROM TABLE t
    JOIN (SELECT @running_total := 0) r
ORDER BY t.id

Примечание:

  • JOIN (SELECT @running_total := 0) r - это перекрестное соединение, которое позволяет объявлять переменные, не требуя отдельной команды SET.
  • Псевдоним таблицы r требуется MySQL для любого подзапроса / производной таблицы / встроенного представления.

Предостережения:

  • Специфичный для MySQL; не переносится в другие базы данных
  • ORDER BY важен; он обеспечивает соответствие порядка OP и может иметь более серьезные последствия для более сложного использования переменных (IE: функция psuedo ROW_NUMBER / RANK, которой нет в MySQL)
person OMG Ponies    schedule 01.04.2010
comment
Я бы добавил ORDER BY t.id ASC к основному запросу, чтобы убедиться, что он всегда будет работать - person Wacek; 02.04.2010
comment
Моей первой мыслью было добавить ORDER BY. Но это неважно. Пока сложение не превратится хотя бы в неассоциативное :) - person Dercsár; 02.04.2010
comment
@OMG Poines: Я думаю, вам нужно использовать SELECT в JOIN (SELECT @running_total := 0) части примера переменных. - person Daniel Vassallo; 28.04.2010
comment
для использования коррелированного запроса откуда берется ваша таблица x? - person allan.simon; 12.09.2016
comment
Если не выполняется внутренняя оптимизация, коррелированный подзапрос является эквивалентом треугольного соединения, выполняемого за время O (N ^ 2), которое не масштабируется. - person Marc L.; 02.12.2016
comment
@MarcL. не могли бы вы объяснить, что вы имеете в виду? - person Meglio; 15.12.2016
comment
Для каждой строки в таблице выполняется полный запрос той же таблицы. Количество строк в подзапросе увеличивается линейно, вплоть до полного количества строк в таблице (возможно, -1). Общий объем этого образует большой треугольник. Из-за задействованного повторения (он не добавляет к предыдущему результату итеративно, а вместо этого воссоздает для каждой строки) это масштабируется вместе с площадью треугольника. Масштаб не учитывает линейные множители, поэтому (N ^ 2) / 2 равно O (N ^ 2). - person Marc L.; 28.12.2016
comment
Кажется, не работает, @running_total кажется 0 для каждой строки. Может ли это быть версией? (работает MariaDB) - person Ivan Peevski; 22.06.2017
comment
если вы хотите, чтобы ваш комулятивный счетчик из таблицы, тогда таблица x такая же, как таблица t (вы подсчитываете общее количество элементов, предшествующих текущему, поэтому вы повторяете исходную таблицу для каждой строки в исходной таблице ) - person Evhz; 21.01.2021

MySQL 8.0 / MariaDB поддерживает оконные SUM(col) OVER():

SELECT *, SUM(cnt) OVER(ORDER BY id) AS cumulative_sum
FROM tab;

Выход:

┌─────┬──────┬────────────────┐
│ id  │ cnt  │ cumulative_sum │
├─────┼──────┼────────────────┤
│  1  │ 100  │            100 │
│  2  │  50  │            150 │
│  3  │  10  │            160 │
└─────┴──────┴────────────────┘

db ‹> fiddle

person Lukasz Szozda    schedule 11.09.2018
comment
Я ищу кумулятивную сумму с помощью функции Windows.Спасибо. - person DatabaseCoder; 30.08.2019

Образец запроса

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
person Jazz    schedule 04.07.2011

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

delimiter |

CREATE TRIGGER calCumluativeSum  BEFORE INSERT ON someTable
  FOR EACH ROW BEGIN

  SET cumulative_sum = (
     SELECT SUM(x.count)
        FROM someTable x
        WHERE x.id <= NEW.id
    )

    set  NEW.cumulative_sum = cumulative_sum;
  END;
|

Я не тестировал это

person Greg    schedule 01.04.2010

выберите id, count, sum (count) over (по убыванию count) как cumulative_sum из tableName;

Я использовал агрегатную функцию суммы в столбце count, а затем использовал предложение over. Он суммирует каждую строку отдельно. Первая строка будет просто 100. Вторая строка будет 100 + 50. Третий ряд - 100 + 50 + 10 и так далее. Таким образом, в основном каждая строка - это ее сумма, а все предыдущие строки и самая последняя - это сумма всех строк. Таким образом, каждая строка представляет собой сумму суммы, в которой идентификатор меньше или равен самому себе.

person Pavan Bashetty    schedule 22.02.2019
comment
Хотя это может решить проблему, лучше немного объяснить, чтобы это принесло пользу другим :) - person Tiw; 22.02.2019
comment
это не связанный подзапрос или подзапрос, если на то пошло ... связанный подзапрос следует за SELECT ...., (SELECT .... FROM table2 WHERE table2.id = table1.id ) FROM table1 то, что у вас есть, это оконный запрос .. - person Raymond Nijland; 24.02.2019

  select t1.id, t1.count, SUM(t2.count) cumulative_sum
    from table t1 
        join table t2 on t1.id >= t2.id
    group by t1.id, t1.count

Пошагово:

1- Учитывая следующую таблицу:

select *
from table t1 
order by t1.id;

id  | count
 1  |  11
 2  |  12   
 3  |  13

2 - Получение информации по группам

select *
from table t1 
    join table t2 on t1.id >= t2.id
order by t1.id, t2.id;

id  | count | id | count
 1  | 11    | 1  |  11

 2  | 12    | 1  |  11
 2  | 12    | 2  |  12

 3  | 13    | 1  |  11
 3  | 13    | 2  |  12
 3  | 13    | 3  |  13

3- Шаг 3: Суммируйте все подсчеты по группе t1.id

select t1.id, t1.count, SUM(t2.count) cumulative_sum
from table t1 
    join table t2 on t1.id >= t2.id
group by t1.id, t1.count;


id  | count | cumulative_sum
 1  |  11   |    11
 2  |  12   |    23
 3  |  13   |    36
person Flavio_cava    schedule 20.05.2020
comment
Добавлен шаг за шагом, чтобы понять окончательный запрос - person Flavio_cava; 20.05.2020

person    schedule
comment
Хотя OP действительно запросил обновление, это денормализовано и, вероятно, будет неудобно поддерживать правильно. - person Matthew Flaschen; 02.04.2010

person    schedule
comment
Пожалуйста, объясните свой ответ - person Rohit Gupta; 27.10.2015
comment
Ответ работает, и это один лайнер. Он также инициализирует / сбрасывает переменную до нуля в начале выбора. - person raisercostin; 13.02.2017