Wie gruppiere ich in MySQL nach Wochen?

90

Der Tabellenserver von Oracle bietet eine integrierte Funktion TRUNC(timestamp,'DY'). Diese Funktion konvertiert jeden Zeitstempel am vorherigen Sonntag in Mitternacht. Was ist der beste Weg, dies in MySQL zu tun?

Oracle bietet außerdem TRUNC(timestamp,'MM')an, einen Zeitstempel am ersten Tag des Monats, in dem er auftritt, auf Mitternacht umzustellen. In MySQL ist dies unkompliziert:

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

Aber dieser DATE_FORMATTrick wird wochenlang nicht funktionieren. Ich bin mir der WEEK(timestamp)Funktion bewusst , aber ich möchte wirklich keine Wochennummer innerhalb des Jahres. Dieses Zeug ist für mehrjährige Arbeit.

O. Jones
quelle
du meinst trunc (sysdate, 'W'), nicht trunc (sysdate, 'DY')
David Aldridge
TRUNC(date, 'DY' )bekommt Wochen mit einem Sonntagsstart. 'W'bekommt einen Montagstart, zumindest in meinem System.
O. Jones

Antworten:

121

Sie können beide YEAR(timestamp)und WEEK(timestamp)und beide dieser Ausdrücke in der SELECTund der GROUP BYKlausel verwenden.

Nicht übermäßig elegant, aber funktional ...

Und natürlich können Sie diese beiden Datumsteile auch in einem einzigen Ausdruck kombinieren, z

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

Bearbeiten : Wie Martin betont, können Sie die YEARWEEK(mysqldatefield)Funktion auch verwenden , obwohl ihre Ausgabe nicht so augenfreundlich ist wie die längere Formel oben.


Bearbeiten Sie 2 [3 1/2 Jahre später!]: Wenn
YEARWEEK(mysqldatefield)das optionale zweite Argument ( mode) auf 0 oder 2 gesetzt ist, ist dies wahrscheinlich der beste Weg, um nach vollständigen Wochen zu aggregieren (dh auch nach Wochen, die sich über den 1. Januar erstrecken), wenn dies der Fall ist was ist erwünscht. Der YEAR() / WEEK()ursprünglich in dieser Antwort vorgeschlagene Ansatz hat zur Folge, dass die aggregierten Daten für solche "überspannenden" Wochen in zwei geteilt werden: eine mit dem früheren Jahr, eine mit dem neuen Jahr.
Ein sauberer Schnitt jedes Jahr auf Kosten von bis zu zwei Teilwochen, eine an jedem Ende, ist häufig in der Buchhaltung usw. erwünscht, und dafür ist der YEAR() / WEEK()Ansatz besser.

mjv
quelle
12
YEARWEEK()Ergebnisse in einem, von INT(6)dem ich glaube, dass es schneller zu sortieren / beizutreten / zu gruppieren sein wird
KCD
@mjv: Was passiert, wenn zwei Jahre dieselbe Woche teilen? wie für diesen 31-12-2012 bis 6-1-2013 (Mo bis So). Gibt es eine Lösung dafür?
Hardik
@hardik: siehe Bearbeiten 2. Je nach Bedarf kann YEARWEEK () ein besserer Schlüssel zum Aggregieren sein, wenn man lieber mit vollständigen Wochen arbeiten möchte, als die letzte / erste Teilwoche des Jahres aufzuteilen.
mjv
@mjv hier ist eine andere Lösung für diesen Link
Hardik
Stellen Sie sicher, dass der "Zeitstempel" keine Ganzzahl ist, wie ich sie in meiner Tabelle hatte. WOCHE (Zeitstempel) nimmt Datums- / Zeitstempelspaltentyp als gültiges Argument
Usman Shaukat
36

Die oben akzeptierte Antwort hat bei mir nicht funktioniert, da die Wochen in alphabetischer Reihenfolge und nicht in chronologischer Reihenfolge sortiert wurden:

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

Hier ist meine Lösung, um nach Wochen zu zählen und zu gruppieren:

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

Erzeugt:

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 Sieben
quelle
4
Bestellung nach Datum ( ORDER BY date ASC) sollte auch den Trick tun
Fender
36

Ich habe es herausgefunden ... es ist etwas umständlich, aber hier ist es.

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

Und wenn Ihre Geschäftsregeln besagen, dass Ihre Wochen montags beginnen, ändern Sie die -1in -2.


Bearbeiten

Jahre sind vergangen und ich bin endlich dazu gekommen, dies aufzuschreiben. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

O. Jones
quelle
@Ollie - Ich würde in Betracht ziehen, DAYOFWEEK () oder WEEKDAY () zu verwenden, um die Lesbarkeit zu verbessern.
Martin Clayton
1
Martin, ich habe darüber nachgedacht und festgestellt, dass diese Funktionen von Montag bis Sonntag zwischen 0 und 6 ausgeführt werden. Meine Geschäftsregeln besagen, dass die Wochen am Sonntag um 00:00 Uhr beginnen, sodass das WOCHENTAG-Zeug etwas hässlich wurde. Sie haben Recht mit der Lesbarkeit.
O. Jones
@ OllieJones perfekt! Ich danke dir sehr!
Joe Cheng
1
Tolle Lösung und Artikel. Danke dir!
Jeremy Wiggins
Gibt es eine Möglichkeit, "Montag" als ersten Tag festzulegen?
Pedro Joaquín
24

Sie können die verkettete Jahres- und Wochennummer (200945) mit der Funktion YEARWEEK () abrufen . Wenn ich Ihr Ziel richtig verstehe, sollten Sie Ihre mehrjährigen Daten gruppieren können.

Wenn Sie den tatsächlichen Zeitstempel für den Wochenbeginn benötigen, ist es weniger schön:

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

Bei monatlichen Bestellungen können Sie die Funktion LAST_DAY () in Betracht ziehen. Die Sortierung erfolgt nach dem letzten Tag des Monats. Dies sollte jedoch der Sortierung nach dem ersten Tag des Monats entsprechen.

Martin Clayton
quelle
1
+1 Martin. Duh für mich habe ich YEARWEEK () vergessen ... ich benutze es einfach nicht viel.
MJV
3

Fügen Sie dies einfach in die Auswahl ein:

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

Und

group_by(week);
Xavier
quelle
wie man nach Monat gruppiert
Ravi Teja
Die Daten werden nicht kontinuierlich
erfasst,
2

Wenn Sie das "Wochenenddatum" benötigen, funktioniert dies ebenfalls. Dies zählt die Anzahl der Datensätze für jede Woche. Beispiel: Wenn zwischen (einschließlich) 1/2/2010 und 1/8/2010 drei Arbeitsaufträge erstellt wurden und zwischen (einschließlich) 1/9/2010 und 1/16/2010 5 erstellt wurden, würde dies Folgendes zurückgeben:

3 08.01.2010
5 16.01.2010

Ich musste die zusätzliche Funktion DATE () verwenden, um mein Datum / Uhrzeit-Feld abzuschneiden.

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;
Geil
quelle