Como agrupar por semana no MySQL?


94

O servidor de mesa da Oracle oferece uma função integrada TRUNC(timestamp,'DY'),. Esta função converte qualquer carimbo de data / hora em meia-noite do domingo anterior. Qual é a melhor maneira de fazer isso no MySQL?

A Oracle também oferece TRUNC(timestamp,'MM')a conversão de um carimbo de data / hora em meia-noite do primeiro dia do mês em que ocorre. No MySQL, este é direto:

TIMESTAMP(DATE_FORMAT(timestamp, '%Y-%m-01'))

Mas esse DATE_FORMATtruque não funcionará por semanas. Estou ciente da WEEK(timestamp)função, mas realmente não quero o número da semana dentro do ano; esse material é para trabalho de vários anos.


você quer dizer trunc (sysdate, 'W'), não trunc (sysdate, 'DY')
David Aldridge

TRUNC(date, 'DY' )obtém semanas com início no domingo. 'W'começa uma segunda-feira, pelo menos no meu sistema.
O. Jones

Respostas:


124

Você pode usar ambos YEAR(timestamp)e WEEK(timestamp), e usar ambas as estas expressões na SELECTea GROUP BYcláusula.

Não excessivamente elegante, mas funcional ...

E, claro, você pode combinar essas duas partes de data em uma única expressão também, ou seja, algo como

SELECT CONCAT(YEAR(timestamp), '/', WEEK(timestamp)), etc...
FROM ...
WHERE ..
GROUP BY CONCAT(YEAR(timestamp), '/', WEEK(timestamp))

Edit : Como Martin aponta, você também pode usar a YEARWEEK(mysqldatefield)função, embora sua saída não seja tão amigável quanto a fórmula mais longa acima.


Editar 2 [3 1/2 anos depois!]:
YEARWEEK(mysqldatefield)Com o segundo argumento opcional ( mode) definido como 0 ou 2 é provavelmente a melhor maneira de agregar por semanas completas (ou seja, incluindo semanas que duram mais de 1º de janeiro), se for o que é desejado. A YEAR() / WEEK()abordagem proposta inicialmente nesta resposta tem o efeito de dividir os dados agregados para essas semanas "abrangentes" em duas: uma com o ano anterior e outra com o ano novo.
Um corte limpo todo ano, ao custo de ter até duas semanas parciais, uma em cada extremidade, é frequentemente desejado na contabilidade, etc. e para isso a YEAR() / WEEK()abordagem é melhor.


12
YEARWEEK()resulta em um INT(6)que acredito será mais rápido para classificar / ingressar / agrupar com
KCD

1
@mjv: o que acontece quando dois anos compartilham a mesma semana? como para este 31-12-2012 a 6-1-2013 (seg a dom). Existe alguma solução para isso ?
hardik de

@hardik: veja Edit 2. Dependendo das necessidades de cada um, YEARWEEK () pode ser uma chave melhor para agregar, se alguém preferir trabalhar com semanas completas em vez de dividir a última / primeira semana parcial do ano.
mjv de

@mjv aqui está outra solução para esse link
hardik

certifique-se de que o "carimbo de data / hora" não seja um número inteiro, como eu tinha na minha tabela. WEEK (carimbo de data / hora) usa o tipo de coluna de data / hora como um argumento válido
Usman Shaukat

40

A resposta aceita acima não funcionou para mim, porque ordenou as semanas por ordem alfabética, não cronológica:

2012/1
2012/10
2012/11
...
2012/19
2012/2

Esta é minha solução para contar e agrupar por semana:

SELECT CONCAT(YEAR(date), '/', WEEK(date)) AS week_name, 
       YEAR(date), WEEK(date), COUNT(*)
FROM column_name
GROUP BY week_name
ORDER BY YEAR(DATE) ASC, WEEK(date) ASC

Gera:

YEAR/WEEK   YEAR   WEEK   COUNT
2011/51     2011    51      15
2011/52     2011    52      14
2012/1      2012    1       20
2012/2      2012    2       14
2012/3      2012    3       19
2012/4      2012    4       19

5
ordenar por data ( ORDER BY date ASC) deve funcionar também
Fender

37

Descobri ... é um pouco complicado, mas aqui está.

FROM_DAYS(TO_DAYS(TIMESTAMP) -MOD(TO_DAYS(TIMESTAMP) -1, 7))

E, se suas regras de negócios dizem que suas semanas começam às segundas-feiras, altere -1para -2.


Editar

Anos se passaram e eu finalmente consegui escrever isso. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/


@Ollie - Eu consideraria usar DAYOFWEEK () ou WEEKDAY () para facilitar a leitura.
Martin Clayton

1
Martin, eu considerei isso e descobri que essas funções funcionam de 0 a 6 de segunda a domingo. Minhas regras de negócios dizem que as semanas começam no domingo às 00:00, então as coisas do WEEKDAY ficaram um pouco feias. Você está certo sobre a legibilidade.
O. Jones

@OllieJones perfeito! Muito obrigado!
Joe Cheng,

1
Ótima solução e artigo. Obrigado!
Jeremy Wiggins

Existe uma maneira de definir "segunda-feira" como o primeiro dia?
Pedro Joaquín

25

Você pode obter o ano concatenado e o número da semana (200945) usando a função YEARWEEK () . Se eu entendi sua meta corretamente, isso deve permitir que você agrupe seus dados plurianuais.

Se você precisa do timestamp real para o início da semana, é menos agradável:

DATE_SUB( field, INTERVAL DAYOFWEEK( field ) - 1 DAY )

Para ordenação mensal, você pode considerar a função LAST_DAY () - ordenar seria pelo último dia do mês, mas isso deveria ser equivalente a ordenar pelo primeiro dia do mês ... não deveria?


1
+1 Martin. Duh pra mim eu esqueci YEARWEEK () ... Eu só não uso muito.
mjv

4

Basta adicionar isso no select:

DATE_FORMAT($yourDate, \'%X %V\') as week

E

group_by(week);

como agrupar por mês
Ravi Teja

Ele não
coleta

2

Se você precisar da data de "término da semana", isso também funcionará. Isso contará o número de registros para cada semana. Exemplo: Se três ordens de serviço foram criadas entre (inclusive) 1/2/2010 e 1/8/2010 e 5 foram criadas entre (inclusive) 1/9/2010 e 1/16/2010, isso retornaria:

3 1/8/2010
5 1/16/2010

Tive que usar a função DATE () extra para truncar meu campo de data e hora.

SELECT COUNT(*), DATE_ADD( DATE(wo.date_created), INTERVAL (7 - DAYOFWEEK( wo.date_created )) DAY) week_ending
FROM work_order wo
GROUP BY week_ending;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.