Não é possível cumulativamente soma `COUNT(*)`
-
22-12-2019 - |
Pergunta
A segunda seção do esta resposta usa variáveis para criar uma soma cumulativa da outra coluna.Eu estou fazendo a mesma coisa, exceto que eu estou usando um GROUP BY
instrução, e somando COUNT(*)
em vez de uma coluna.Aqui é o meu código para criar um mínimo de tabela e inserir valores:
CREATE TABLE `test_group_cumulative` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`group_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `test_group_cumulative` (`id`, `group_id`)
VALUES
(1, 1),
(2, 2),
(3, 3);
E aqui está o código que está a falhar:
SELECT
`group_id`,
COUNT(*) AS `count`,
@count_cumulative := @count_cumulative + COUNT(*) AS `count_cumulative`
FROM `test_group_cumulative` AS `tgc`
JOIN (SELECT @count_cumulative := 0) AS `_count_cumulative`
GROUP BY `group_id`
ORDER BY `id`;
Aqui está o resultado:
group_id count count_cumulative
1 1 1
2 1 1
3 1 1
Como você pode ver, count_cumulative
NÃO é de soma corretamente.No entanto, aqui é a parte estranha.Se eu substituir a COUNT(*)
no count_cumulative
com esse valor, 1
, a consulta funciona corretamente.
@count_cumulative := @count_cumulative + 1 AS `count_cumulative`
Aqui está o resultado correto:
group_id count count_cumulative
1 1 1
2 1 2
3 1 3
Obviamente, no meu app, haverá mais de um item em cada grupo, a fim de COUNT(*)
não vai ser sempre 1
.Eu sei que existem maneiras de fazer isso com associações ou subconsultas, e eu vou fazer o que se eu tenho, mas na minha mente, isso DEVE funcionar.Então por que não COUNT(*)
trabalhar dentro de uma soma cumulativa?
Solução
Este é um problema que muitas vezes enfrentam ao fazer uma análise de série temporal.A minha forma preferida para combater este é envolvê-la em um segundo seleccionar e introduzir o contador na última camada.E você pode adaptar essa técnica para mais complicado fluxos de dados, utilizando tabelas temporárias, se reqiured.
Eu fiz este pequeno sqlfiddle usando o esquema de presente: http://sqlfiddle.com/#!2/cc97e/21
E aqui é a consulta para obter a contagem cumulativa:
SELECT
tgc.group_id, @count_cumulative := @count_cumulative + cnt as cum_cnt
FROM (
SELECT
group_id, COUNT(*) AS cnt
FROM `test_group_cumulative`
group by group_id
order by id) AS `tgc`,
(SELECT @count_cumulative := 0) AS `temp_var`;
Este é o resultado que eu recebo:
GROUP_ID CUM_CNT
1 1
2 2
3 3
O motivo da sua tentativa não funcionou:
Quando você fizer um grupo com a variável temporária, mysql executa grupos individuais de forma independente, e no momento em que cada grupo é atribuído a variável temporária valor de corrente, que neste caso é 0.
Se você executou esta consulta:
SELECT @count_cumulative;
imediatamente após
SELECT
`group_id`,
COUNT(*) AS `count`,
@count_cumulative := @count_cumulative + COUNT(*) AS `count_cumulative`
FROM `test_group_cumulative` AS `tgc`
JOIN (SELECT @count_cumulative := 0) AS `_count_cumulative`
GROUP BY `group_id`
ORDER BY `id`;
você teria o valor de 1.Para cada um dos grupos, o @count_cumulative está sendo redefinido para 0.
Daí que, na minha proposta de solução, eu contornar este problema através da geração do 'grupo-conta" primeiro e, em seguida, fazendo a acumulação.
Outras dicas
Eu concordo com o @Ashalynd, o valor de count(*) não avaliado ainda.Aqui está um pequeno experimento que eu fiz :
1.
SELECT
GROUP_ID,
@COUNTER := @COUNTER + COUNT(*) GROUPCOUNT,
@COUNTER COUNTER
FROM
TEST_GROUP_CUMULATIVE,
(SELECT @COUNTER := 0) R
GROUP BY
GROUP_ID;
-- RESULT
============
GROUP_ID GROUPCOUNT COUNTER
------------------------------------
1 1 0
2 1 0
3 1 0
2.
SELECT @COUNTER;
-- RESULT
=============
@COUNTER
--------
1
Para cada grupo de variável está sendo inicializado como 0.Isso significa COUNT(*) não foi avaliado ainda.
Além disso, quando você fizer:
1.
SELECT
GROUP_ID,
@COUNTER := @COUNTER + 1 GROUPCOUNT,
@COUNTER COUNTER
FROM
TEST_GROUP_CUMULATIVE,
(SELECT @COUNTER := 0) R
GROUP BY
GROUP_ID;
-- RESULT
============
GROUP_ID GROUPCOUNT COUNTER
------------------------------------
1 1 1
2 1 2
3 1 3
2.
SELECT @COUNTER;
-- RESULT
=============
@COUNTER
--------
3
Ele não tem como avaliar 1.Diretamente resume e dá-lhe a soma cumulativa.