GRUPA Zapytań MySQL według dnia / miesiąca / roku


649

Czy można wykonać proste zapytanie, aby policzyć, ile rekordów mam w określonym przedziale czasowym, takim jak rok, miesiąc lub dzień, mając TIMESTAMPpole takie jak:

SELECT COUNT(id)
FROM stats
WHERE record_date.YEAR = 2009
GROUP BY record_date.YEAR

Lub nawet:

SELECT COUNT(id)
FROM stats
GROUP BY record_date.YEAR, record_date.MONTH

Aby mieć miesięczne statystyki.

Dzięki!


1
Myślę, że to powinno być GROUP BY record_date.MONTHw twoim pierwszym fragmencie kodu?
chiccodoro

Odpowiedzi:


1012
GROUP BY YEAR(record_date), MONTH(record_date)

Sprawdź funkcje daty i godziny w MySQL.


27
W niektórych przypadkach możesz chcieć dodać dodatkową kolumnę dla większej przejrzystości, na przykład w przypadku, gdy rekordy obejmują kilka lat. SELECT COUNT (event_id), DATE_FORMAT (event_start, '% Y /% m')
Ric

Prosty kompletny przykład: SELECT count(*), record_date FROM anytable WHERE anytable.anycolumn = 'anycondition' GROUP BY YEAR(record_date), month(record_date);uwaga: data_rejestru jest datą typu TIMESTAMP
renedet

Prawdopodobnie warto wspomnieć, że nie działało to na MySQL 5.7 z kolumną z aliasingiem COUNT (bez błędu, mam zero wyników). Kiedy zmieniłem, aby wybrać pola z aliasem, mogłem następnie pogrupować według aliasu. Jest to standardowy obraz dokera MySQL 5.7 działający w środowisku lokalnym, więc nie mam pojęcia, dlaczego nie popełnił błędu ani nie zwrócił wyników.
MrMesees,

3
O Boże, gdybym to wiedział wcześniej ... tak wiele wierszy PHP do zrobienia czegoś, co mysql może zrobić w jednym wierszu.
noce

230
GROUP BY DATE_FORMAT(record_date, '%Y%m')

Uwaga (przede wszystkim do potencjalnych downvoters). Obecnie może to nie być tak skuteczne, jak inne sugestie. Mimo to pozostawiam to jako alternatywę, która może pomóc w sprawdzeniu szybkości innych rozwiązań. (Bo tak naprawdę nie można szybko odróżnić od spowolnienia, dopóki nie zobaczysz różnicy.) Z biegiem czasu można również wprowadzić zmiany w silniku MySQL w zakresie optymalizacji, aby w pewnym stopniu wprowadzić takie rozwiązanie (być może nie tak odległe) punkt w przyszłości, aby stać się dość porównywalnym pod względem wydajności z większością innych.


3
Mam wrażenie, że nie działałoby to dobrze, ponieważ funkcja formatowania nie byłaby w stanie użyć indeksu w kolumnie daty.
Sonny,

@Stv: Możesz wtedy rozważyć odpowiedź @ fu-chi . O ile mogę stwierdzić, wyrażenia grupujące zarówno w tej odpowiedzi, jak i mojej, oceniają to samo, ale EXTRACT()mogą być bardziej wydajne niż DATE_FORMAT(). (Nie mam jednak MySQL do prawidłowego testowania.)
Andriy M

45

Spróbuj tego

SELECT COUNT(id)
FROM stats
GROUP BY EXTRACT(YEAR_MONTH FROM record_date)

Funkcja EKSTRAKT (jednostka od daty) jest lepsza, ponieważ stosuje się mniejsze grupowanie, a funkcja zwraca wartość liczbową.

Warunek porównania podczas grupowania będzie szybszy niż funkcja DATE_FORMAT (która zwraca wartość ciągu). Spróbuj użyć pola funkcji |, które zwracają wartość nieciągową dla warunku porównania SQL (GDZIE, POSIADANIE, ORDER BY, GROUP BY).


43

Próbowałem użyć powyższego wyrażenia „GDZIE”, myślałem, że jest poprawny, ponieważ nikt go nie poprawił, ale się myliłem; po kilku wyszukiwaniach dowiedziałem się, że jest to właściwa formuła dla instrukcji WHERE, więc kod wygląda następująco:

SELECT COUNT(id)  
FROM stats  
WHERE YEAR(record_date) = 2009  
GROUP BY MONTH(record_date)

30

Jeśli Twoje wyszukiwanie trwa kilka lat i nadal chcesz grupować co miesiąc, sugeruję:

wersja 1:

SELECT SQL_NO_CACHE YEAR(record_date), MONTH(record_date), COUNT(*)
FROM stats
GROUP BY DATE_FORMAT(record_date, '%Y%m')

wersja # 2 (bardziej wydajna) :

SELECT SQL_NO_CACHE YEAR(record_date), MONTH(record_date), COUNT(*)
FROM stats
GROUP BY YEAR(record_date)*100 + MONTH(record_date)

Porównałem te wersje na dużym stole z 1357 918 wierszami (), a druga wersja wydaje się mieć lepsze wyniki.

wersja1 (średnio 10 wykonań) : 1,404 sekundy
wersja2 (średnio 10 wykonań) : 0,780 sekund

( SQL_NO_CACHEdodano klucz, aby zapobiec buforowaniu MySQL do zapytań).


1
Zastanów się nad włączeniem sugestii @ fu-chi do swoich testów, może okazać się ona jeszcze bardziej wydajna. Testowałeś GROUP BY YEAR(record_date)*100 + MONTH(record_date), ale dlaczego nie przetestować GROUP BY YEAR(record_date), MONTH(record_date)?
Andriy M

2
Jeśli użyjesz COUNT (1), uruchom COUNT (*), będzie to jeszcze szybsze, a dane wyników będą takie same.
Pa0l0

2
Co to jest *100w wersji # 2? Z góry dziękuję.
Avión

1
*100doYEAR(record_date)*100 + MONTH(record_date) == DATE_FORMAT(record_date, '%Y%m')
Phu Duy,

17

Jeśli chcesz pogrupować według daty w MySQL, użyj poniższego kodu:

 SELECT COUNT(id)
 FROM stats
 GROUP BY DAYOFMONTH(record_date)

Mam nadzieję, że zaoszczędzi to trochę czasu tym, którzy znajdą ten wątek.


6
Ważne jest, aby pamiętać, że należy również pogrupować według, MONTH(record_date)aby rozliczać się przez wiele miesięcy.
Webnet,

14

Jeśli chcesz filtrować rekordy dla określonego roku (np. 2000), zoptymalizuj następującą WHEREklauzulę:

SELECT MONTH(date_column), COUNT(*)
FROM date_table
WHERE date_column >= '2000-01-01' AND date_column < '2001-01-01'
GROUP BY MONTH(date_column)
-- average 0.016 sec.

Zamiast:

WHERE YEAR(date_column) = 2000
-- average 0.132 sec.

Wyniki zostały wygenerowane na podstawie tabeli zawierającej 300 tys. Wierszy i kolumny indeksu według daty.

Jeśli chodzi o GROUP BYklauzulę, przetestowałem trzy warianty w stosunku do powyższej tabeli; Oto wyniki:

SELECT YEAR(date_column), MONTH(date_column), COUNT(*)
FROM date_table
GROUP BY YEAR(date_column), MONTH(date_column)
-- codelogic
-- average 0.250 sec.

SELECT YEAR(date_column), MONTH(date_column), COUNT(*)
FROM date_table
GROUP BY DATE_FORMAT(date_column, '%Y%m')
-- Andriy M
-- average 0.468 sec.

SELECT YEAR(date_column), MONTH(date_column), COUNT(*)
FROM date_table
GROUP BY EXTRACT(YEAR_MONTH FROM date_column)
-- fu-chi
-- average 0.203 sec.

Ostatni jest zwycięzcą.


10

Kompletne i proste rozwiązanie z podobnie działającą, ale krótszą i bardziej elastyczną alternatywą obecnie aktywną:

SELECT COUNT(*) FROM stats
-- GROUP BY YEAR(record_date), MONTH(record_date), DAYOFMONTH(record_date)
GROUP BY DATE_FORMAT(record_date, '%Y-%m-%d')

7

Jeśli chcesz otrzymywać miesięczne statystyki z liczbą wierszy na miesiąc każdego roku uporządkowane według ostatniego miesiąca, spróbuj tego:

SELECT count(id),
      YEAR(record_date),
      MONTH(record_date) 
FROM `table` 
GROUP BY YEAR(record_date),
        MONTH(record_date) 
ORDER BY YEAR(record_date) DESC,
        MONTH(record_date) DESC

7

Możesz to zrobić po prostu funkcję MYSql DATE_FORMAT () w GROUP BY. W niektórych przypadkach możesz chcieć dodać dodatkową kolumnę dla większej przejrzystości, na przykład gdy rekordy obejmują kilka lat, a następnie ten sam miesiąc w różnych latach. Tutaj jest tyle opcji, które możesz dostosować. Przeczytaj to przed rozpoczęciem. Mam nadzieję, że powinno to być dla ciebie bardzo pomocne. Oto przykładowe zapytanie do zrozumienia

SELECT
    COUNT(id),
    DATE_FORMAT(record_date, '%Y-%m-%d') AS DAY,
    DATE_FORMAT(record_date, '%Y-%m') AS MONTH,
    DATE_FORMAT(record_date, '%Y') AS YEAR

FROM
    stats
WHERE
    YEAR = 2009
GROUP BY
    DATE_FORMAT(record_date, '%Y-%m-%d ');

4

Następujące zapytanie działało dla mnie w Oracle Database 12c Release 12.1.0.1.0

SELECT COUNT(*)
FROM stats
GROUP BY 
extract(MONTH FROM TIMESTAMP),
extract(MONTH FROM TIMESTAMP),
extract(YEAR  FROM TIMESTAMP);

2

Wolę zoptymalizować wybór grupy na jeden rok tak:

SELECT COUNT(*)
  FROM stats
 WHERE record_date >= :year 
   AND record_date <  :year + INTERVAL 1 YEAR;

W ten sposób możesz po prostu powiązać rok za jednym razem, np. '2009'Z nazwanym parametrem i nie musisz martwić się o dodanie '-01-01'lub przekazanie '2010'osobno.

Ponadto, jak przypuszczalnie jesteśmy tylko liczenie wierszy i idnigdy nie jest NULL, wolę COUNT(*)się COUNT(id).


0

.... group by to_char(date, 'YYYY') -> 1989

.... group by to_char(date,'MM') -> 05

.... group by to_char(date,'DD') ---> 23

.... group by to_char(date,'MON') ---> MAJ

.... group by to_char(date,'YY') ---> 89


To byłoby bardzo, bardzo wolne.
Earl3s
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.