Група запитів MySQL за днями / місяцями / роками


649

Чи можна зробити простий запит, щоб підрахувати, скільки записів у мене за певний проміжок часу, як рік, місяць чи день, маючи TIMESTAMPполе, як:

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

Або навіть:

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

Мати щомісячну статистику.

Дякую!


1
Я думаю, це має бути GROUP BY record_date.MONTHу вашому першому фрагменті коду?
чікодоро

Відповіді:


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

Перевірте функції дати та часу в MySQL.


27
Ви можете додати додатковий стовпець для додаткової ясності в деяких випадках, наприклад, коли записи тривають кілька років. SELECT COUNT (event_id), DATE_FORMAT (event_start, '% Y /% m')
Рік

Простий повний приклад: SELECT count(*), record_date FROM anytable WHERE anytable.anycolumn = 'anycondition' GROUP BY YEAR(record_date), month(record_date);примітка: record_date - це дата дати TIMESTAMP
renedet

Напевно, варто згадати, що це не запускалось на моєму MySQL 5.7 зі стовпчиком з псевдонімом COUNT (помилки немає, я отримав нульові результати). Коли я змінив вибір цих полів з псевдонімом, я міг згрупувати псевдонім. Це стандартне докерське зображення MySQL 5.7, що працює в локальному середовищі, тому я не знаю, чому він не помилився або не повернув результати.
MrMesees

3
О боже, якби я знав це раніше ... так багато рядків PHP, щоб зробити що-небудь mysql, можна зробити в одному рядку.
ночі

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

Зверніть увагу (насамперед, на потенційні потоки). Зараз це може бути не настільки ефективно, як інші пропозиції. Але я залишаю це як альтернативу, і те, що може послужити бачити, наскільки швидшими є інші рішення. (Тому що ви не можете реально швидко сказати від повільного, поки не побачите різницю.) Крім того, з плином часу можна змінити двигун MySQL щодо оптимізації, щоб зробити це рішення в деяких випадках (можливо, не так віддалена) точка в майбутньому, щоб стати досить порівнянною за ефективністю з більшістю інших.


3
У мене є відчуття, що це не буде добре, оскільки функція формату не зможе використовувати індекс у стовпці дати.
Сонні

@Stv: Тоді ви можете розглянути відповідь @ fu-chi . Наскільки я можу сказати, вирази групування як у цій відповіді, так і в моїй оцінюють одне і те ж, але EXTRACT()можуть бути ефективнішими, ніж DATE_FORMAT(). (Я не маю MySQL для належного тестування.)
Андрій М

45

спробуйте це

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

ЕКСТРАКТ (одиниця від дати)Функція краще, оскільки використовується менше групування, а функція повертає числове значення.

Умова порівняння при групуванні буде швидше, ніж функція DATE_FORMAT (яка повертає рядкове значення). Спробуйте використовувати функцію | поле, яке повертає нерядкове значення для умови порівняння SQL (ДЕРЖАВИ, ЯКІСТЬ, ЗАМОВЛЕННЯ, ГРУППУ).


43

Я спробував використати вищезазначене твердження "WHERE", я вважав його правильним, оскільки його ніхто не виправляв, але я помилявся; після деяких пошуків я виявив, що це правильна формула для оператора WHERE, так що код стає таким:

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

30

Якщо ваш пошук триває кілька років, і ви все ще хочете групуватися щомісяця, пропоную:

версія №1:

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

версія №2 (більш ефективна) :

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

Я порівняв ці версії на великій таблиці з 1 357 918 рядками (), а 2-й варіант, схоже, має кращі результати.

версія1 ( в середньому 10 викон) : 1.404 секунди
Version2 ( в середньому 10 виконує) : 0.780 секунди

( SQL_NO_CACHEключ доданий для запобігання MySQL CACHING у запитах.)


1
Якщо врахувати, що включення пропозиції @ fu-chi у ваші тести, це може виявитися ще ефективнішим. Також ви пройшли тестування GROUP BY YEAR(record_date)*100 + MONTH(record_date), але чому б не зробити тест GROUP BY YEAR(record_date), MONTH(record_date)?
Андрій М

2
Якщо ви використовуватимете COUNT (1) COUNT (*), то це буде ще швидше, і результати результатів однакові.
Pa0l0

2
Що це *100за версія №2? Заздалегідь спасибі.
Авіон

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

17

Якщо ви хочете згрупуватись за датою в MySQL, тоді використовуйте код нижче:

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

Сподіваюся, це заощадить час для тих, хто збирається знайти цю нитку.


6
Важливо зауважити, що вам також доведеться групуватися, MONTH(record_date)а також обліковувати протягом кількох місяців.
Webnet

14

Якщо ви хочете відфільтрувати записи за певний рік (наприклад, 2000), тоді оптимізуйте WHEREпункт так:

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.

Замість:

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

Результати були сформовані у таблиці, що містить 300k рядків та стовпчик індексу на дату.

Щодо GROUP BYпункту, я перевірив три варіанти щодо вищезгаданої таблиці; ось результати:

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.

Останній - переможець.


10

Повне і просте рішення з активно виконаною, але коротшою та гнучкішою альтернативою, що діє зараз:

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

Якщо ви хочете отримувати щомісячну статистику з підрахунками рядків за місяць кожного року, упорядкованими до останнього місяця, то спробуйте це:

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

Зробити це можна просто функцією Mysql DATE_FORMAT () в GROUP BY. Ви можете додати додатковий стовпець для додаткової чіткості в деяких випадках, наприклад, коли записи тривають кілька років, а в різних роках відбувається той самий місяць. Ось стільки варіантів, які можна налаштувати. Будь ласка, прочитайте це для початку. Сподіваюся, це може бути дуже корисним для вас. Ось зразок запиту для вашого розуміння

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

Наступний запит працював для мене в 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

Я вважаю за краще оптимізувати однорічний вибір групи так:

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

Таким чином, ви можете просто зв’язати рік за один раз, наприклад '2009', з іменованим параметром і не потрібно турбуватися про додавання '-01-01'або передачу '2010'окремо.

Крім того, як вважаємо, ми просто підраховуємо рядки, і idце ніколи NULL, я вважаю COUNT(*)за краще 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') ---> МОЖ

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


Це було б дуже повільно.
Earl3s
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.