¿Cómo agrupar por semana en MySQL?

92

Ofrece el servidor de la tabla de Oracle una función incorporada, TRUNC(timestamp,'DY'). Esta función convierte cualquier marca de tiempo a la medianoche del domingo anterior. ¿Cuál es la mejor manera de hacer esto en MySQL?

Oracle también ofrece TRUNC(timestamp,'MM')convertir una marca de tiempo a la medianoche del primer día del mes en que ocurre. En MySQL, este es sencillo:

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

Pero este DATE_FORMATtruco no funcionará durante semanas. Soy consciente de la WEEK(timestamp)función, pero realmente no quiero un número de semana dentro del año; este material es para trabajos de varios años.

O. Jones
fuente
te refieres a trunc (sysdate, 'W'), no trunc (sysdate, 'DY')
David Aldridge
TRUNC(date, 'DY' )obtiene semanas con un comienzo en domingo. 'W'comienza el lunes, al menos en mi sistema.
O. Jones

Respuestas:

123

Puede usar YEAR(timestamp)y WEEK(timestamp), y usar estas dos expresiones en SELECTla GROUP BYcláusula y.

No demasiado elegante, pero funcional ...

Y, por supuesto, también puede combinar estas dos partes de fecha en una sola expresión, es decir, algo como

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

Editar : como señala Martin , también puede usar la YEARWEEK(mysqldatefield)función, aunque su salida no es tan agradable a la vista como la fórmula más larga anterior.


Edición 2 [¡3 1/2 años después!]:
YEARWEEK(mysqldatefield)Con el segundo argumento opcional ( mode) establecido en 0 o 2 es probablemente la mejor manera de agregar por semanas completas (es decir, incluidas las semanas que van más allá del 1 de enero), si es lo que se desea. El YEAR() / WEEK()enfoque propuesto inicialmente en esta respuesta tiene el efecto de dividir los datos agregados para esas semanas "transzonales" en dos: uno con el año anterior y otro con el año nuevo.
Un corte limpio cada año, a costa de tener hasta dos semanas parciales, una en cada extremo, a menudo se desea en contabilidad, etc. y para eso el YEAR() / WEEK()enfoque es mejor.

mjv
fuente
12
YEARWEEK()da como resultado un INT(6)que creo que será más rápido para ordenar / unirse / agrupar con
KCD
@mjv: ¿que pasa cuando dos años comparten la misma semana? como para este 31-12-2012 al 6-1-2013 (de lunes a domingo). Hay alguna solución para esto ?
hardik
@hardik: ver Edición 2. Dependiendo de las necesidades de cada uno, YEARWEEK () puede ser una mejor clave para agregar, si uno prefiere trabajar con semanas completas en lugar de dividir la última / primera semana parcial del año.
mjv
@mjv aquí hay otra solución para ese enlace
hardik
asegúrese de que la "marca de tiempo" no sea un número entero, como lo tenía en mi tabla. WEEK (marca de tiempo) toma el tipo de columna de fecha / marca de tiempo como un argumento válido
Usman Shaukat
37

La respuesta aceptada anterior no me funcionó, porque ordenó las semanas por orden alfabético, no por orden cronológico:

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

Aquí está mi solución para contar y 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

Genera:

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
B siete
fuente
5
ordenar por fecha ( ORDER BY date ASC) también debería funcionar
Fender
36

Lo descubrí ... es un poco engorroso, pero aquí está.

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

Y, si las reglas de su negocio dicen que sus semanas comienzan los lunes, cambie -1a -2.


Editar

Han pasado los años y finalmente he podido escribir esto. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

O. Jones
fuente
@Ollie: consideraría usar DAYOFWEEK () o WEEKDAY () para mejorar la legibilidad.
Martin Clayton
1
Martin, lo consideré y descubrí que esas funciones se ejecutan de 0 a 6 de lunes a domingo. Mis reglas comerciales dicen que las semanas comienzan los domingos a las 00:00, por lo que las cosas del WEEKDAY se pusieron un poco feas. Tienes razón sobre la legibilidad.
O. Jones
@OllieJones perfecto! Muchas gracias!
Joe Cheng
1
Gran solución y artículo. ¡Gracias!
Jeremy Wiggins
¿Hay alguna forma de establecer el "lunes" como primer día?
Pedro Joaquín
25

Puede obtener el año y el número de semana concatenados (200945) utilizando la función YEARWEEK () . Si entiendo correctamente su objetivo, eso debería permitirle agrupar sus datos de varios años.

Si necesita la marca de tiempo real para el comienzo de la semana, es menos agradable:

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

Para los pedidos mensuales, podría considerar la función LAST_DAY () ; ordenar sería por el último día del mes, pero eso debería ser equivalente a ordenar por el primer día del mes ... ¿no es así?

martin clayton
fuente
1
+1 Martín. Duh para mí me olvidé de YEARWEEK () ... Simplemente no lo uso mucho.
mjv
4

Solo agregue esto en la selección:

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

Y

group_by(week);
Javier
fuente
cómo agrupar por mes
Ravi Teja
No captura los datos continuamente, sino solo para una fecha específica
Chique_Code
2

Si necesita la fecha de "fin de semana", también funcionará. Esto contará el número de registros de cada semana. Ejemplo: Si se crearon tres órdenes de trabajo entre (inclusive) 1/2/2010 y 1/8/2010 y se crearon 5 entre (inclusive) 1/9/2010 y 1/16/2010, esto devolvería:

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

Tuve que usar la función DATE () adicional para truncar mi campo de fecha y 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;
Cachondo
fuente