GRUPO MySQL Query por dia / mês / ano

649

É possível fazer uma consulta simples para contar quantos registros eu tenho em um determinado período de tempo, como um ano, mês ou dia, com um TIMESTAMPcampo, como:

SELECT COUNT(id)
FROM stats
WHERE record_date.YEAR = 2009
GROUP BY record_date.YEAR

Ou até:

SELECT COUNT(id)
FROM stats
GROUP BY record_date.YEAR, record_date.MONTH

Ter uma estatística mensal.

Obrigado!

Fernando Barrocal
fonte
1
Acho que deveria estar GROUP BY record_date.MONTHno seu primeiro trecho de código?
Chiccodoro 6/10/10

Respostas:

1012
GROUP BY YEAR(record_date), MONTH(record_date)

Confira as funções de data e hora no MySQL.

codelogic
fonte
27
Convém adicionar uma coluna extra para maior clareza em alguns casos, como onde os registros abrangem vários anos. SELECT COUNT (event_id), DATE_FORMAT (event_start, '% Y /% m')
Ric
Exemplo completo simples: SELECT count(*), record_date FROM anytable WHERE anytable.anycolumn = 'anycondition' GROUP BY YEAR(record_date), month(record_date);note: record_date é um tipo de data TIMESTAMP
renedet
Provavelmente vale a pena mencionar que isso não foi executado no MySQL 5.7 com uma coluna com alias COUNT (sem erros, obtive zero resultados). Quando mudei para selecionar esses campos com alias, pude agrupar pelo alias. Esta é a imagem padrão do docker do MySQL 5.7 em execução em um ambiente local, portanto não faço ideia do porquê de não ter errado ou retornar resultados.
MrMesees
3
Oh Deus, se eu soubesse disso antes ... tantas linhas de PHP para fazer algo que o mysql pode fazer em uma linha.
noites
230
GROUP BY DATE_FORMAT(record_date, '%Y%m')

Nota (principalmente, para potenciais downvoters). Atualmente, isso pode não ser tão eficiente quanto outras sugestões. Ainda assim, deixo isso como uma alternativa, e também uma que pode servir para ver a rapidez com que outras soluções são. (Pois você não pode realmente dizer rápido de devagar até perceber a diferença.) Além disso, com o passar do tempo, podem ser feitas alterações no mecanismo do MySQL no que diz respeito à otimização, a fim de tornar essa solução, em alguns (talvez, não tão distante) no futuro, para se tornar bastante comparável em eficiência com a maioria dos outros.

Andriy M
fonte
3
Sinto que isso não funcionaria bem porque uma função de formato não seria capaz de usar um índice na coluna da data.
Sonny
@Stv: Você pode considerar a resposta de @ fu-chi então. Tanto quanto posso dizer, as expressões de agrupamento nas respostas e nas minhas avaliam a mesma coisa, mas EXTRACT()podem ser mais eficientes que DATE_FORMAT(). (Eu não tenho um MySQL para um teste adequado, no entanto.)
Andriy M
45

tente este

SELECT COUNT(id)
FROM stats
GROUP BY EXTRACT(YEAR_MONTH FROM record_date)

A função EXTRACT (unit FROM date) é melhor, pois menos agrupamento é usado e a função retorna um valor numérico.

A condição de comparação ao agrupar será mais rápida que a função DATE_FORMAT (que retorna um valor de sequência). Tente usar o campo function | que retorne valor não-string para a condição de comparação SQL (WHERE, HAVING, ORDER BY, GROUP BY).

fu-chi
fonte
43

Tentei usar a declaração 'WHERE' acima, achei correta, pois ninguém a corrigiu, mas estava errado; depois de algumas pesquisas, descobri que essa é a fórmula correta para a instrução WHERE, para que o código fique assim:

SELECT COUNT(id)  
FROM stats  
WHERE YEAR(record_date) = 2009  
GROUP BY MONTH(record_date)
dimazaid
fonte
30

Se sua pesquisa durar vários anos e você ainda desejar agrupar mensalmente, sugiro:

versão 1:

SELECT SQL_NO_CACHE YEAR(record_date), MONTH(record_date), COUNT(*)
FROM stats
GROUP BY DATE_FORMAT(record_date, '%Y%m')

versão 2 (mais eficiente) :

SELECT SQL_NO_CACHE YEAR(record_date), MONTH(record_date), COUNT(*)
FROM stats
GROUP BY YEAR(record_date)*100 + MONTH(record_date)

Comparei essas versões em uma grande mesa com 1.357.918 linhas () e a segunda versão parece ter melhores resultados.

version1 (média de 10 execuções) : 1.404 segundos
version2 (média de 10 execuções) : 0.780 segundos

( SQL_NO_CACHEchave adicionada para impedir o MySQL de CACHING para consultas.)

mr.baby123
fonte
1
Considere incluir a sugestão de @ fu-chi em seus testes; ela pode ser ainda mais eficiente. Além disso, você testou GROUP BY YEAR(record_date)*100 + MONTH(record_date), mas por que não testar GROUP BY YEAR(record_date), MONTH(record_date)também?
Andriy M
2
Se você usar COUNT (1) com COUNT inserido (*), será ainda mais rápido e os dados do resultado serão os mesmos.
Pa0l0 04/06/2015
2
O que é isso *100na versão # 2? Desde já, obrigado.
Avión
1
*100aYEAR(record_date)*100 + MONTH(record_date) == DATE_FORMAT(record_date, '%Y%m')
Phu Duy
17

Se você deseja agrupar por data no MySQL, use o código abaixo:

 SELECT COUNT(id)
 FROM stats
 GROUP BY DAYOFMONTH(record_date)

Espero que isso economize algum tempo para aqueles que encontrarão esse tópico.

Haijerome
fonte
6
É importante observar que você também precisará agrupar MONTH(record_date)e contabilizar vários meses.
Webnet 19/10/12
14

Se você deseja filtrar registros para um ano específico (por exemplo, 2000), otimize a WHEREcláusula assim:

SELECT MONTH(date_column), COUNT(*)
FROM date_table
WHERE date_column >= '2000-01-01' AND date_column < '2001-01-01'
GROUP BY MONTH(date_column)
-- average 0.016 sec.

Ao invés de:

WHERE YEAR(date_column) = 2000
-- average 0.132 sec.

Os resultados foram gerados em uma tabela contendo 300k linhas e índice na coluna de data.

Quanto à GROUP BYcláusula, testei as três variantes na tabela acima mencionada; aqui estão os resultados:

SELECT YEAR(date_column), MONTH(date_column), COUNT(*)
FROM date_table
GROUP BY YEAR(date_column), MONTH(date_column)
-- codelogic
-- average 0.250 sec.

SELECT YEAR(date_column), MONTH(date_column), COUNT(*)
FROM date_table
GROUP BY DATE_FORMAT(date_column, '%Y%m')
-- Andriy M
-- average 0.468 sec.

SELECT YEAR(date_column), MONTH(date_column), COUNT(*)
FROM date_table
GROUP BY EXTRACT(YEAR_MONTH FROM date_column)
-- fu-chi
-- average 0.203 sec.

O último é o vencedor.

Salman A
fonte
10

Solução completa e simples, com desempenho semelhante, porém mais curto e flexível, atualmente ativo:

SELECT COUNT(*) FROM stats
-- GROUP BY YEAR(record_date), MONTH(record_date), DAYOFMONTH(record_date)
GROUP BY DATE_FORMAT(record_date, '%Y-%m-%d')
Cees Timmerman
fonte
7

Se você deseja obter estatísticas mensais com contagens de linhas por mês de cada ano, ordenadas pelo último mês, tente o seguinte:

SELECT count(id),
      YEAR(record_date),
      MONTH(record_date) 
FROM `table` 
GROUP BY YEAR(record_date),
        MONTH(record_date) 
ORDER BY YEAR(record_date) DESC,
        MONTH(record_date) DESC
user3019799
fonte
7

Você pode fazer isso simplesmente na função Mysql DATE_FORMAT () no GROUP BY. Você pode adicionar uma coluna extra para maior clareza em alguns casos, como onde os registros abrangem vários anos e o mesmo mês ocorre em anos diferentes. Aqui há tantas opções que você pode personalizar isso. Por favor, leia isto antes de começar. Espero que seja muito útil para você. Aqui está um exemplo de consulta para sua compreensão

SELECT
    COUNT(id),
    DATE_FORMAT(record_date, '%Y-%m-%d') AS DAY,
    DATE_FORMAT(record_date, '%Y-%m') AS MONTH,
    DATE_FORMAT(record_date, '%Y') AS YEAR

FROM
    stats
WHERE
    YEAR = 2009
GROUP BY
    DATE_FORMAT(record_date, '%Y-%m-%d ');
Faisal
fonte
4

A consulta a seguir funcionou para mim no Oracle Database 12c Release 12.1.0.1.0

SELECT COUNT(*)
FROM stats
GROUP BY 
extract(MONTH FROM TIMESTAMP),
extract(MONTH FROM TIMESTAMP),
extract(YEAR  FROM TIMESTAMP);
Minisha
fonte
2

Prefiro otimizar a seleção do grupo de um ano da seguinte forma:

SELECT COUNT(*)
  FROM stats
 WHERE record_date >= :year 
   AND record_date <  :year + INTERVAL 1 YEAR;

Dessa forma, você pode vincular o ano apenas uma vez, por exemplo '2009', com um parâmetro nomeado e não precisa se preocupar em adicionar '-01-01'ou passar '2010'separadamente.

Além disso, como presumivelmente estamos apenas contando linhas e idnão é NULL, eu prefiro COUNT(*)a COUNT(id).

Arth
fonte
0

.... group by to_char(date, 'YYYY') -> 1989

.... group by to_char(date,'MM') -> 05

.... group by to_char(date,'DD') ---> 23

.... group by to_char(date,'MON') ---> MAIO

.... group by to_char(date,'YY') ---> 89

aromita sen
fonte
Isso seria muito, muito lento.
earl3s