Come raggruppare per settimana in MySQL?


92

Offerte Server Tabella di Oracle un built-in funzione TRUNC(timestamp,'DY'). Questa funzione converte qualsiasi timestamp alla mezzanotte della domenica precedente. Qual è il modo migliore per farlo in MySQL?

Oracle offre anche TRUNC(timestamp,'MM')di convertire un timestamp a mezzanotte del primo giorno del mese in cui si verifica. In MySQL, questo è semplice:

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

Ma questo DATE_FORMATtrucco non funzionerà per settimane. Sono a conoscenza della WEEK(timestamp)funzione, ma davvero non voglio il numero della settimana entro l'anno; questa roba è per lavori pluriennali.


intendi trunc (sysdate, 'W'), non trunc (sysdate, 'DY')
David Aldridge

TRUNC(date, 'DY' )ottiene settimane con un inizio di domenica. 'W'inizia il lunedì, almeno nel mio sistema.
O. Jones

Risposte:


123

È possibile utilizzare entrambi YEAR(timestamp)e WEEK(timestamp), e utilizzare entrambi i queste espressioni nella SELECTe della GROUP BYclausola.

Non eccessivamente elegante, ma funzionale ...

E ovviamente puoi anche combinare queste due parti della data in un'unica espressione, cioè qualcosa di simile

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

Modifica : come sottolinea Martin , puoi anche utilizzare la YEARWEEK(mysqldatefield)funzione, sebbene il suo output non sia così facile da vedere come la formula più lunga sopra.


Modifica 2 [3 anni e mezzo dopo!]:
YEARWEEK(mysqldatefield)Con il secondo argomento opzionale ( mode) impostato su 0 o 2 è probabilmente il modo migliore per aggregare per settimane complete (cioè includendo per settimane che si trovano a cavallo del 1 ° gennaio), se questo è ciò che si desidera. L' YEAR() / WEEK()approccio inizialmente proposto in questa risposta ha l'effetto di suddividere i dati aggregati per tali settimane "a cavallo tra" in due: uno con l'anno precedente, uno con il nuovo anno.
Un taglio netto ogni anno, a costo di avere fino a due settimane parziali, una per ciascuna estremità, è spesso richiesto in contabilità ecc. E per questo l' YEAR() / WEEK()approccio è migliore.


12
YEARWEEK()risultati in un INT(6)che credo sarà più veloce da ordinare / partecipare / raggruppare con
KCD

@mjv: cosa succede quando due anni condividono la stessa settimana? come per questo dal 31-12-2012 al 6-1-2013 (dal lunedì alla domenica). c'è qualche soluzione a questo ?
hardik

@hardik: vedere Modifica 2. A seconda delle proprie esigenze YEARWEEK () può essere una chiave migliore su cui aggregarsi, se si preferisce lavorare con settimane complete piuttosto che dividere l'ultima / prima settimana parziale dell'anno.
mjv

@mjv ecco un'altra soluzione per quel collegamento
hardik

assicurati che il "timestamp" non sia un numero intero, come avevo nella mia tabella. WEEK (timestamp) accetta il tipo di colonna data / timestamp come argomento valido
Usman Shaukat

37

La risposta accettata sopra non ha funzionato per me, perché ha ordinato le settimane in ordine alfabetico, non in ordine cronologico:

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

Ecco la mia soluzione per contare e raggruppare per settimana:

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

5
anche l'ordinazione per data ( ORDER BY date ASC) dovrebbe funzionare
Fender

36

Capito ... è un po 'ingombrante, ma eccolo qui.

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

E, se le regole della tua azienda dicono che le tue settimane iniziano il lunedì, cambia -1in -2.


modificare

Sono passati anni e finalmente sono riuscito a scriverlo. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/


@Ollie: prenderei in considerazione l'utilizzo di DAYOFWEEK () o WEEKDAY () per facilitare la leggibilità.
martin clayton

1
Martin, ci ho pensato e ho scoperto che quelle funzioni funzionano da 0 a 6 dal lunedì alla domenica. Le mie regole aziendali dicono che le settimane iniziano la domenica alle 00:00, quindi le cose WEEKDAY sono diventate un po 'brutte. Hai ragione sulla leggibilità.
O. Jones,

@OllieJones perfetto! Grazie mille!
Joe Cheng

1
Ottima soluzione e articolo. Grazie!
Jeremy Wiggins

C'è un modo per impostare "lunedì" come primo giorno?
Pedro Joaquín

25

È possibile ottenere l'anno e il numero della settimana concatenati (200945) utilizzando la funzione YEARWEEK () . Se comprendo correttamente il tuo obiettivo, ciò dovrebbe consentirti di raggruppare i tuoi dati pluriennali.

Se hai bisogno del timestamp effettivo per l'inizio della settimana, è meno carino:

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

Per l'ordinamento mensile, potresti prendere in considerazione la funzione LAST_DAY () - l'ordinamento sarebbe per l'ultimo giorno del mese, ma dovrebbe essere equivalente all'ordinamento per il primo giorno del mese ... non dovrebbe?


1
+1 Martin. Duh per me mi sono dimenticato di YEARWEEK () ... Non lo uso molto.
mjv

4

Basta aggiungere questo nella selezione:

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

E

group_by(week);

come raggruppare per mese
Ravi Teja

Non
acquisisce

2

Se hai bisogno della data di "fine settimana", anche questa funzionerà. Questo conterà il numero di record per ogni settimana. Esempio: se tre ordini di lavoro sono stati creati tra (incluso) 1/2/2010 e 1/8/2010 e 5 sono stati creati tra (incluso) 1/9/2010 e 1/16/2010, ciò restituirebbe:

3 1/8/2010
5

Ho dovuto usare la funzione DATE () extra per troncare il mio campo 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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.