Comment grouper par semaine dans MySQL?

92

Serveur de table Oracle offre une fonction de intégrée, TRUNC(timestamp,'DY'). Cette fonction convertit n'importe quel horodatage en minuit le dimanche précédent. Quelle est la meilleure façon de faire cela dans MySQL?

Oracle propose également TRUNC(timestamp,'MM')de convertir un horodatage en minuit le premier jour du mois au cours duquel il se produit. Dans MySQL, celui-ci est simple:

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

Mais cette DATE_FORMATastuce ne fonctionnera pas pendant des semaines. Je connais la WEEK(timestamp)fonction, mais je ne veux vraiment pas de numéro de semaine dans l'année; ce truc est pour un travail pluriannuel.

O. Jones
la source
vous voulez dire trunc (sysdate, 'W'), pas trunc (sysdate, 'DY')
David Aldridge
TRUNC(date, 'DY' )obtient des semaines avec un début dimanche. 'W'commence le lundi, du moins dans mon système.
O. Jones

Réponses:

123

Vous pouvez utiliser à la fois YEAR(timestamp)et WEEK(timestamp)et utiliser ces deux expressions dans SELECTla GROUP BYclause et.

Pas trop élégant, mais fonctionnel ...

Et bien sûr, vous pouvez également combiner ces deux parties de date dans une seule expression, c'est-à-dire quelque chose comme

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

Edit : Comme le souligne Martin , vous pouvez également utiliser la YEARWEEK(mysqldatefield)fonction, bien que sa sortie ne soit pas aussi conviviale que la formule plus longue ci-dessus.


Edit 2 [3 1/2 ans plus tard!]:
YEARWEEK(mysqldatefield)Avec le deuxième argument optionnel ( mode) mis à 0 ou 2 est probablement le meilleur moyen d'agréger par semaines complètes (c'est-à-dire y compris pour les semaines qui chevauchent le 1er janvier), si c'est ce qui est souhaité. L' YEAR() / WEEK()approche initialement proposée dans cette réponse a pour effet de diviser les données agrégées pour ces semaines «chevauchantes» en deux: une avec l'année précédente, une avec la nouvelle année.
Une coupe nette chaque année, au prix d'avoir jusqu'à deux semaines partielles, une à chaque extrémité, est souvent souhaitée en comptabilité, etc. et pour cela l' YEAR() / WEEK()approche est meilleure.

mjv
la source
12
YEARWEEK()résultats dans un INT(6)qui, je crois, sera plus rapide à trier / rejoindre / grouper avec
KCD
@mjv: que se passe-t-il lorsque deux ans se partagent la même semaine? comme pour ce 31-12-2012 au 6-1-2013 (du lundi au dimanche). Y a-t-il une solution à cela ?
hardik
@hardik: voir Edit 2. Selon ses besoins, YEARWEEK () peut être une meilleure clé pour agréger, si l'on préfère travailler avec des semaines complètes plutôt que de diviser la dernière / première semaine partielle de l'année.
mjv
@mjv voici une autre solution pour ce lien
hardik
assurez-vous que le "timestamp" n'est pas un entier, comme je l'avais dans ma table. WEEK (timestamp) prend le type de colonne date / horodatage comme argument valide
Usman Shaukat
37

La réponse acceptée ci-dessus n'a pas fonctionné pour moi, car elle a ordonné les semaines par ordre alphabétique, pas par ordre chronologique:

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

Voici ma solution pour compter et regrouper par semaine:

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

Génère:

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 Sept
la source
5
commander par date ( ORDER BY date ASC) devrait faire l'affaire aussi
Fender
36

Je l'ai compris ... c'est un peu encombrant, mais le voici.

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

Et, si les règles de votre entreprise indiquent que vos semaines commencent le lundi, remplacez le -1par -2.


Éditer

Les années ont passé et j'ai enfin réussi à écrire ceci. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

O. Jones
la source
@Ollie - J'envisagerais d'utiliser DAYOFWEEK () ou WEEKDAY () pour faciliter la lisibilité.
martin clayton
1
Martin, j'ai considéré cela et j'ai découvert que ces fonctions fonctionnent de 0 à 6 du lundi au dimanche. Mes règles commerciales disent que les semaines commencent le dimanche à 00h00, donc les trucs du WEEKDAY sont devenus un peu moche. Vous avez raison sur la lisibilité.
O. Jones
@OllieJones parfait! Merci beaucoup!
Joe Cheng
1
Excellente solution et article. Merci!
Jeremy Wiggins
Il existe un moyen de définir "lundi" comme premier jour?
Pedro Joaquín
25

Vous pouvez obtenir l'année concaténée et le numéro de semaine (200945) à l'aide de la fonction YEARWEEK () . Si je comprends bien votre objectif, cela devrait vous permettre de regrouper vos données pluriannuelles.

Si vous avez besoin de l'horodatage réel pour le début de la semaine, c'est moins agréable:

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

Pour la commande mensuelle, vous pouvez envisager la fonction LAST_DAY () - le tri serait effectué par le dernier jour du mois, mais cela devrait être équivalent au tri par premier jour du mois ... n'est-ce pas?

Martin Clayton
la source
1
+1 Martin. Duh pour moi, j'ai oublié YEARWEEK () ... Je ne l'utilise pas beaucoup.
mjv
4

Ajoutez simplement ceci dans la sélection:

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

Et

group_by(week);
Xavier
la source
comment grouper par mois
Ravi Teja
Il ne
récupère
2

Si vous avez besoin de la date de "fin de semaine", cela fonctionnera également. Cela comptera le nombre d'enregistrements pour chaque semaine. Exemple: Si trois bons de travail ont été créés entre (inclus) 1/2/2010 et 1/8/2010 et 5 ont été créés entre (inclus) 1/9/2010 et 1/16/2010, cela renverrait:

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

J'ai dû utiliser la fonction supplémentaire DATE () pour tronquer mon champ datetime.

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;
Excité
la source