2016-10-22 2 views
1

У меня есть таблица с каждой строкой, где хранятся поля с именем id (ключ), дата, знак, значение.Накопительная сумма Mysql, с условием сброса, группа за период

Знак = 0 является условием сброса. Он будет использоваться для установки начального кумулятивного значения

Знак = +1 является условием добавления. Он будет суммировать стоимость до суммарной суммы.

Знак = -1 является вычитанием, и, как вы догадаетесь, это уменьшит суммарное общее количество.

Порядок важен, поэтому их необходимо оценивать с помощью порядка 0, 1, -1.

Предположим, что я хочу получить суммарную сумму стоимости.

SELECT my_date, my_sign, my_value, @cum AS cum_before, 
(@cum := IF(my_sign !=0, @cum + my_sign * my_value, my_value)) AS cum_after 
FROM my_table, (SELECT @cum :=0) as t WHERE my_date LIKE '2016-05-%' 
ORDER BY my_date, my_sign + (my_sign =0) *2 DESC; 

будет корректно отображать:

my_date  my_sign my_value cum_before cum_after 
2016-05-02  0 10000.00   0 10000.00 
2016-05-02  1 1860.00  10000 11860.00 
2016-05-02  -1 1860.00  11860 10000.00 
2016-05-03  1 1780.00  10000 11780.00 
2016-05-06  1 4625.00  11780 16405.00 
2016-05-09  1 14200.00  16405 30605.00 

Теперь я хотел бы, чтобы группа его за неделю (или месяц), и, cum_before устанавливается в начальное значение перед обработкой строк в группе (кстати, должен быть cum_after предыдущей группы) и cum_after в качестве кумулятивного значения после обработки строк в группе. Все становится сложнее, поскольку предыдущая переменная состояния @cum, похоже, инициализируется первым значением набора групп.

Я создаю временную таблицу, чтобы сохранить правильный порядок, поскольку GROUP, похоже, не подчиняется какому-либо предложению ORDER BY (я предполагаю, что он принимает строки, как они появляются в БД).

CREATE TEMPORARY TABLE _t_ SELECT id FROM my_table 
ORDER BY my_date, my_sign + (my_sign =0) *2 DESC ; 

Я использую назначить вместо (@cum: = значение), внутри функции SUM, чтобы изменить соответствующим образом @cum при группировке, и умножить на 0, чтобы не мешать реальной суммы, которая будет подводить знака * в нормальном случае и вычитает значение @cum и добавит поле значения, когда будет найдено условие сброса.

SELECT min(my_date) AS MinDate, max(my_date) AS MaxDate, 
@cum AS cum_before, SUM( 
    0 * (@cum := IF(my_sign !=0, my_sign * my_value, my_value)) + 
    IF(my_sign !=0, my_sign * my_value, - @cum + my_value) 
) AS cum_after 
FROM my_table as F, _t_, (SELECT @cum :=0) AS t 
WHERE _t_.id = F.id AND my_date LIKE '2016-05-%' 
GROUP BY date_format(my_date, "%y%U"); 

даст следующее:

MinDate  MaxDate  cum_before cum_after 
2016-05-02 2016-05-06  10000 16405.00 
2016-05-09 2016-05-09  14200 14200.00 

что неправильно, потому что я ожидаю получить это:

MinDate  MaxDate  cum_before cum_after 
2016-05-02 2016-05-06   0 16405.00 
2016-05-09 2016-05-09  16405 30605.00 

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

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

ответ

0

Адаптация a previous answerHaleemur Ali Кажется, что это невозможно сделать за один шаг, поскольку кумулятивная переменная сбрасывается после каждой группы. Поэтому возможное решение состоит в следующем:

SELECT MinDate, MaxDate, @cum as cum_before, (@cum:[email protected]+tmp_cum) as cum_after 
FROM (SELECT min(my_date) AS MinDate, max(my_date) AS MaxDate, 
SUM( 
    0 * (@tmp := IF(my_sign !=0, my_sign * my_value, my_value)) + 
    IF(my_sign !=0, my_sign * my_value, - @tmp + my_value) 
) AS tmp_cum 
FROM my_table as F, _t_, (SELECT @tmp :=0) AS t 
WHERE _t_.id = F.id AND my_date LIKE '2016-05-%' 
GROUP BY date_format(my_date, "%y%U")) as SUBQ, (SELECT @cum:=0) as tmp_var; 

Не знаю, о производительности, или, если это может быть оптимизировано.