2009-02-03 15 views
494

Czy to możliwe, że wykonuję proste zapytanie, aby policzyć, ile rekordów mam w określonym okresie czasu, jak rok, miesiąc lub dzień, mając pole TIMESTAMP, takie jak:Grupa zapytań MySQL Wg dnia/miesiąca/roku

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

Albo nawet:

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

Aby mieć miesięczną statystykę.

Dzięki!

+1

myślę, że to powinno być 'GROUP BY record_date.MONTH' w twój pierwszy fragment kodu? – chiccodoro

Odpowiedz

789

Zapoznaj się z date and time functions w MySQL.

+14

Możesz dodać dodatkową kolumnę w celu zwiększenia przejrzystości w niektórych przypadkach, na przykład w przypadku rekordów obejmujących kilka lat. SELECT COUNT (event_id), DATE_FORMAT (event_start, '% Y /% m') –

+0

Prosty kompletny przykład: 'SELECT count (*), data_dokumentu FROM anyable WHERE countable.anycolumn = 'anycondition' GROUP BY YEAR (data_nagrywania), month (record_date); 'note: record_date jest typem daty TIMESTAMP – renedet

+0

Warto wspomnieć, że to nie działało na moim MySQL 5.7 z COUNT kolumną aliasową (brak błędu, otrzymałem zero wyników). Gdy zmieniłem, aby wybrać te pola z alisem, mogłem pogrupować według aliasu. Jest to standardowy obraz dokowania MySQL 5.7 działający w środowisku lokalnym, więc nie mam pojęcia, dlaczego nie spowodował błędu ani nie zwrócił wyników. – MrMesees

36

Próbowałem użyć powyższego wyrażenia "WHERE", myślałem, że jest poprawne, ponieważ nikt go nie poprawił, ale myliłem się; po pewnych poszukiwań okazało się, że jest to odpowiednia formuła rachunku gdzie tak kod staje się tak:

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

W rzeczywistości można pominąć "ROK (data_dania)" w GROUP BY, ponieważ ograniczasz dane do jednego roku. –

170
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ę, a także jedną, która może służyć do sprawdzenia, jak szybsze są inne rozwiązania. (Nie można tak naprawdę mówić szybko, dopóki nie zauważy się różnicy.) Z upływem czasu można również wprowadzić zmiany w silniku MySQL pod kątem optymalizacji, aby niektóre z nich (być może nie tak) odległy) punkt w przyszłości, aby osiągnąć porównywalną wydajność z większością innych.

+1

Mam wrażenie, że nie będzie to działać dobrze, ponieważ funkcja formatu nie będzie mogła używać indeksu w kolumnie daty. – Sonny

+0

@Stv: Możesz wtedy rozważyć odpowiedź [@ fu-chi] (http://stackoverflow.com/a/7721169/297408). O ile mogę powiedzieć, wyrażenia grupujące zarówno w tej odpowiedzi, jak i mojej, oceniają to samo, ale "EXTRACT()" mogą być bardziej wydajne niż 'DATE_FORMAT()'. (Jednak nie mam MySQL do poprawnego testowania.) –

14

Jeśli chcesz grupy według daty w MySQL następnie użyć poniższy kod:

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

Nadzieja Oszczędza to trochę czasu dla tych, którzy mają zamiar znaleźć tego wątku.

+5

Należy pamiętać, że trzeba również pogrupować według 'MONTH (record_date)', aby uwzględnić kilka miesięcy. – Webnet

32

próbować ten jeden

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

EXTRACT(unit FROM date) funkcja jest lepiej jako mniej grupowanie jest używany, a funkcja zwraca wartość liczbową.

Warunek porównania w przypadku grupowania będzie szybszy niż funkcja DATE_FORMAT (która zwraca wartość ciągu). Spróbuj użyć funkcji | pole, które zwraca wartość nie będącą ciągiem dla warunku porównania SQL (GDZIE, MAJĄC, ORDER BY, GROUP BY).

18

Jeśli wyszukiwanie jest w ciągu kilku lat, a nadal chcesz grupy miesięcznie, proponuję:

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 wydajne) :

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 o n duży stół z 1,357,918 rzędami (), , a druga wersja wydaje się mieć lepsze wyniki.

version1(średnio 10 wykonuje): 1,404 sekund
Version2(średnio 10 wykonuje): 0,780 sekund

(SQL_NO_CACHE klucz by zapobiec MySQL z buforowania zapytań .)

+1

Weź pod uwagę sugestię @ fu-chi do swoich testów, może okazać się jeszcze bardziej skuteczna. Ponadto przetestowałeś opcję "GROUP BY YEAR (data_dostępu) * 100 + MIESIĄC (data_udziału)", ale dlaczego nie przetestować również "GROUP BY YEAR (data_dania), MONTH (data_dania)"? –

+2

Jeśli użyjesz COUNT (1) zamiast COUNT (*), będzie to jeszcze szybsze, a dane wynikowe będą takie same. – Pa0l0

+0

Co to jest '* 100' na versión # 2? Z góry dziękuję. –

6

Aby uzyskać miesięczną statystykę z liczbą wierszy przypadającą na miesiąc każdego roku uporządkowaną 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 
9

Jeśli chcesz filtrować rekordy dla określonego roku (np. 2000), a następnie optymalizacji klauzuli WHERE tak:

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 generowane na tablicy zawierającej 300K wierszy i indeks kolumny dat.

Jeśli chodzi o klauzulę GROUP BY, przetestowałem trzy warianty w odniesieniu 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. 

Ten ostatni jest zwycięzcą.

1

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

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

ten sposób można powiązać tylko raz w roku, na przykład '2009', o podanym parametrze i nie musisz się martwić dodaniem '-01-01' lub przekazaniem osobnym '2010'.

Ponadto, prawdopodobnie po prostu liczymy wiersze, a id nigdy nie jest NULL, wolę COUNT(*) od COUNT(id).

4

Następująca kwerenda pracował 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

.... 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

+0

To byłoby bardzo powolne. – earl3s

2

Możesz to zrobić po prostu Mysql DATE_FORMAT() Funkcja w GROUP BY. Możesz dodać dodatkową kolumnę dla większej przejrzystości w niektórych przypadkach, na przykład gdy rekordy trwają kilka lat, a ten sam miesiąc ma miejsce w różnych latach. Tyle opcji można dostosować. Przeczytaj to przed rozpoczęciem. Mam nadzieję, że to powinno być dla ciebie bardzo pomocne.Oto próbka zapytań o wyrozumiałość

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 '); 
1

Kompletna i proste rozwiązanie z podobnie wykonując jeszcze krótsza i bardziej elastyczna alternatywa aktualnie aktywny:

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