Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

MySQL - contagem por mês (incluindo registros ausentes)


Você precisa gerar todas as datas desejadas e, em seguida, juntar seus dados às datas. Observe também que é importante colocar alguns predicados no ON da junção esquerda cláusula e outros no WHERE cláusula:
SELECT
  CONCAT(y, '-', LPAD(m, 2, '0')) as byMonth,
  COUNT(`created`) AS Total 
FROM (
  SELECT year(now())     AS y UNION ALL
  SELECT year(now()) - 1 AS y 
) `years`
CROSS JOIN (
  SELECT  1 AS m UNION ALL
  SELECT  2 AS m UNION ALL
  SELECT  3 AS m UNION ALL
  SELECT  4 AS m UNION ALL
  SELECT  5 AS m UNION ALL
  SELECT  6 AS m UNION ALL
  SELECT  7 AS m UNION ALL
  SELECT  8 AS m UNION ALL
  SELECT  9 AS m UNION ALL
  SELECT 10 AS m UNION ALL
  SELECT 11 AS m UNION ALL
  SELECT 12 AS m
) `months`
LEFT JOIN `qualitaet` q
ON YEAR(`created`) = y 
  AND MONTH(`created`) = m
  AND `status` = 1
WHERE STR_TO_DATE(CONCAT(y, '-', m, '-01'), '%Y-%m-%d') 
    >= MAKEDATE(year(now()-interval 1 year),1) + interval 5 month
  AND STR_TO_DATE(CONCAT(y, '-', m, '-01'), '%Y-%m-%d') 
    <= now()
GROUP BY y, m
ORDER BY y, m

Como funciona o acima?

  • CROSS JOIN cria um produto cartesiano entre todos os anos disponíveis e todos os meses disponíveis. Isso é o que você quer, você quer todas as combinações ano-mês sem lacunas.
  • LEFT JOIN adiciona todos os qualitaet registros ao resultado (se existirem) e os une ao produto cartesiano ano-mês anterior. É importante colocar predicados como o status = 1 predicado aqui.
  • COUNT(created) conta apenas valores não NULL de created , ou seja, quando o LEFT JOIN não produz linhas para nenhum ano-mês, queremos 0 como resultado, não 1 , ou seja, não queremos contar o NULL valor.

Uma observação sobre o desempenho


O acima faz uso pesado de operações de string e aritmética de data e hora em seu ON e WHERE predicados. Isso não vai funcionar para muitos dados. Nesse caso, você deve pré-truncar e indexar melhor seus anos-meses no qualitaet tabela, e operar apenas nesses valores.