Как сгруппировать по неделям в MySQL?


92

Сервер таблиц Oracle предлагает встроенную функцию TRUNC(timestamp,'DY'). Эта функция преобразует любую метку времени в полночь в предыдущее воскресенье. Как лучше всего это сделать в MySQL?

Oracle также предлагает TRUNC(timestamp,'MM')преобразовать метку времени в полночь первого дня месяца, в котором это происходит. В MySQL это просто:

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

Но этот DATE_FORMATтрюк неделями не сработает. Я знаю об этой WEEK(timestamp)функции, но мне действительно не нужен номер недели в году; этот материал для многолетней работы.


вы имеете в виду trunc (sysdate, 'W'), а не trunc (sysdate, 'DY')
Дэвид Олдридж

TRUNC(date, 'DY' )получает недели с воскресным началом. 'W'получает начало понедельника, по крайней мере, в моей системе.
O. Jones

Ответы:


123

Вы можете использовать и YEAR(timestamp)и WEEK(timestamp), и оба этих выражения в предложении SELECTи GROUP BY.

Не слишком элегантно, но функционально ...

И, конечно же, вы можете объединить эти две части даты в одно выражение, то есть что-то вроде

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

Изменить : как указывает Мартин , вы также можете использовать эту YEARWEEK(mysqldatefield)функцию, хотя ее вывод не так удобен для глаз, как более длинная формула выше.


Редактировать 2 [3 1/2 года спустя!]:
YEARWEEK(mysqldatefield)С необязательным вторым аргументом ( mode), установленным либо на 0, либо на 2, вероятно, лучший способ агрегирования по полным неделям (то есть включая недели, которые охватывают период с 1 января), если это что желательно. YEAR() / WEEK()Подход первоначально предложенный в этом ответе есть эффект расщепления агрегированных данных для такого «ТРАНСЗОНАЛЬНЫЙ» недели в два: один с бывшим годом, один с новым годом.
Чистая срезка каждый год за счет наличия до двух неполных недель, по одной на каждом конце, часто желательна в бухгалтерском учете и т. Д., И для этого YEAR() / WEEK()подход лучше.


12
YEARWEEK()приводит к результату, INT(6)который, как я полагаю, будет быстрее сортировать / присоединяться / группироваться
KCD

@mjv: что происходит, когда два года делятся на одну неделю? как для этого 31-12-2012 по 6-1-2013 (с понедельника по воскресенье). есть ли решение этого?
Hardik

@hardik: см. Edit 2. В зависимости от потребностей YEARWEEK () может быть лучшим ключом для агрегирования, если вы предпочитаете работать с полными неделями, а не с разделением последней / первой неполной недели года.
mjv

@mjv вот еще одно решение для этой ссылки
hardik

убедитесь, что "отметка времени" не является целым числом, как в моей таблице. WEEK (временная метка) принимает тип столбца даты / времени в качестве допустимого аргумента
Усман Шаукат,

37

Принятый выше ответ не сработал для меня, потому что он упорядочил недели в алфавитном порядке, а не в хронологическом порядке:

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

Вот мое решение для подсчета и группировки по неделям:

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

Создает:

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
упорядочение по дате ( ORDER BY date ASC) тоже должно помочь
Fender

36

Разобрался ... громоздко, но вот оно.

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

И, если ваши бизнес-правила гласят, что ваши недели начинаются с понедельника, измените значение -1на -2.


редактировать

Прошли годы, и я наконец нашел время, чтобы это написать. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/


@Ollie - Я бы подумал об использовании DAYOFWEEK () или WEEKDAY () для облегчения чтения.
Мартин Клейтон

1
Мартин, я подумал и обнаружил, что эти функции работают с 0-6 с понедельника по воскресенье. В моих бизнес-правилах говорится, что недели начинаются в воскресенье в 00:00, так что работа с WEEKDAY стала немного некрасивой. Вы правы насчет удобочитаемости.
O. Jones

@OllieJones отлично! Спасибо огромное!
Джо Ченг

1
Отличное решение и статья. Спасибо!
Джереми Уиггинс,

Есть способ сделать «понедельник» первым днем?
Педро Хоакин

25

Вы можете получить объединенный номер года и недели (200945) с помощью YEARWEEK () . Если я правильно понимаю вашу цель, это должно позволить вам сгруппировать свои многолетние данные.

Если вам нужна фактическая временная метка начала недели, это не так хорошо:

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

Для ежемесячного упорядочивания вы можете рассмотреть функцию LAST_DAY () - сортировка будет по последнему дню месяца, но это должно быть эквивалентно сортировке по первому дню месяца ... не так ли?


1
+1 Мартин. Да уж, я забыл про YEARWEEK () ... Я просто мало им пользуюсь.
mjv


2

Если вам нужна дата окончания недели, это тоже подойдет. Это будет подсчитывать количество записей за каждую неделю. Пример: если три заказа на работу были созданы между (включительно) с 1/2/2010 по 1/8/2010 и 5 были созданы между (включительно) с 01.09.2010 по 16.01.2010, это вернет:

3 08.01.2010
5 16.01.2010

Мне пришлось использовать дополнительную функцию DATE (), чтобы обрезать поле даты и времени.

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;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.