Làm thế nào để nhóm theo tuần trong MySQL?


92

Máy chủ bảng của Oracle cung cấp một chức năng tích hợp sẵn , TRUNC(timestamp,'DY'). Hàm này chuyển đổi bất kỳ dấu thời gian nào thành nửa đêm của ngày Chủ nhật trước đó. Cách tốt nhất để làm điều này trong MySQL là gì?

Oracle cũng đề xuất TRUNC(timestamp,'MM')chuyển đổi một dấu thời gian thành nửa đêm của ngày đầu tiên của tháng mà nó xảy ra. Trong MySQL, cái này rất đơn giản:

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

Nhưng DATE_FORMATthủ thuật này sẽ không hoạt động trong nhiều tuần. Tôi biết về WEEK(timestamp)chức năng này, nhưng tôi thực sự không muốn số tuần trong năm; công cụ này dành cho công việc nhiều năm.


bạn trunc bình (sysdate, 'W'), không trunc (sysdate, 'DY')
David Aldridge

TRUNC(date, 'DY' )bắt đầu từ chủ nhật hàng tuần. 'W'bắt đầu vào thứ Hai, ít nhất là trong hệ thống của tôi.
O. Jones

Câu trả lời:


123

Bạn có thể sử dụng cả hai YEAR(timestamp)WEEK(timestamp), và sử dụng cả hai những biểu thức trong SELECTGROUP BYkhoản.

Không quá thanh lịch, nhưng chức năng ...

Và tất nhiên, bạn cũng có thể kết hợp hai phần ngày tháng này trong một biểu thức duy nhất, chẳng hạn như

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

Chỉnh sửa : Như Martin đã chỉ ra, bạn cũng có thể sử dụng YEARWEEK(mysqldatefield)hàm, mặc dù đầu ra của nó không thân thiện với mắt như công thức dài hơn ở trên.


Chỉnh sửa 2 [3 năm rưỡi sau!]:
YEARWEEK(mysqldatefield)Với đối số thứ hai tùy chọn ( mode) được đặt thành 0 hoặc 2 có lẽ là cách tốt nhất để tổng hợp theo tuần hoàn chỉnh (nghĩa là bao gồm các tuần kéo dài hơn ngày 1 tháng 1), nếu đó là những gì được mong muốn. Cách YEAR() / WEEK()tiếp cận ban đầu được đề xuất trong câu trả lời này có tác dụng chia dữ liệu tổng hợp cho các tuần "phân tầng" như vậy thành hai: một với năm cũ, một với năm mới.
Việc cắt giảm sạch sẽ hàng năm, với chi phí là có tối đa hai tuần một phần, một tuần một phần, thường được mong muốn trong kế toán, v.v. và vì vậy YEAR() / WEEK()cách tiếp cận này tốt hơn.


12
YEARWEEK()kết quả trong một INT(6)mà tôi tin rằng sẽ được nhanh hơn để loại / join / nhóm với
KCD

@mjv: điều gì sẽ xảy ra khi hai năm có cùng một tuần? like for this 31-12-2012 to 6-1-2013 (Mon to Sun). có giải pháp nào cho điều này không?
hardik

@hardik: xem Chỉnh sửa 2. Tùy thuộc vào nhu cầu của mỗi người YEARWEEK () có thể là một chìa khóa tốt hơn để tổng hợp, nếu một người muốn làm việc với các tuần đầy đủ hơn là chia nhỏ tuần cuối cùng / đầu tiên của năm.
mjv

@mjv đây là một giải pháp cho điều đó liên kết
hardik

đảm bảo "dấu thời gian" không phải là số nguyên, giống như tôi đã có trong bảng của mình. TUẦN (timestamp) có loại cột ngày / timestamp như một đối số hợp lệ
Usman Shaukat

37

Câu trả lời được chấp nhận ở trên không phù hợp với tôi, bởi vì nó sắp xếp các tuần theo thứ tự bảng chữ cái, không theo thứ tự thời gian:

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

Đây là giải pháp của tôi để đếm và nhóm theo tuần:

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

Tạo ra:

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
đặt hàng theo ngày ( ORDER BY date ASC) cũng nên thực hiện thủ thuật
Fender

36

Hình dung ra ... nó hơi rườm rà, nhưng đây rồi.

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

Và, nếu các quy tắc kinh doanh của bạn cho biết các tuần của bạn bắt đầu vào Thứ Hai, hãy thay đổi -1thành -2.


Biên tập

Nhiều năm đã trôi qua và cuối cùng tôi đã viết được điều này. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/


@Ollie - Tôi sẽ cân nhắc sử dụng DAYOFWEEK () hoặc WEEKDAY () để hỗ trợ khả năng đọc.
martin clayton

1
Martin, tôi đã xem xét điều đó và phát hiện ra rằng các hàm đó chạy 0-6 cho Thứ Hai-Chủ Nhật. Các quy tắc kinh doanh của tôi nói rằng các tuần bắt đầu vào Chủ nhật lúc 00:00, vì vậy, mọi thứ trong WEEKDAY có một chút xấu xí. Bạn nói đúng về khả năng đọc.
O. Jones

@OllieJones hoàn hảo! Cảm ơn bạn rất nhiều!
Joe Cheng

1
Giải pháp và bài báo tuyệt vời. Cảm ơn bạn!
Jeremy Wiggins

Có một cách để đặt "thứ hai" là ngày nắm tay?
Pedro Joaquín

25

Bạn có thể lấy số năm và tuần được ghép nối (200945) bằng cách sử dụng hàm YEARWEEK () . Nếu tôi hiểu đúng mục tiêu của bạn, điều đó sẽ cho phép bạn nhóm dữ liệu nhiều năm của mình.

Nếu bạn cần dấu thời gian thực tế cho đầu tuần, thì nó sẽ kém đẹp hơn:

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

Đối với thứ tự hàng tháng, bạn có thể xem xét hàm LAST_DAY () - sắp xếp theo ngày cuối cùng của tháng, nhưng điều đó sẽ tương đương với sắp xếp theo ngày đầu tiên của tháng ... phải không?


1
+1 Martin. Duh cho tôi, tôi đã quên mất YEARWEEK () ... Tôi không sử dụng nó nhiều.
mjv

4

Chỉ cần quảng cáo điều này trong lựa chọn:

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

group_by(week);

cách nhóm theo tháng
Ravi Teja

Nó không lấy dữ liệu liên tục mà chỉ cho một ngày cụ thể
Chique_Code

2

Nếu bạn cần ngày "kết thúc tuần", điều này cũng sẽ hoạt động. Điều này sẽ đếm số lượng bản ghi cho mỗi tuần. Ví dụ: Nếu ba đơn đặt hàng công việc được tạo từ (bao gồm) 1/2/2010 đến 1/8/2010 và 5 được tạo từ (bao gồm) 1/9/2010 đến 1/16/2010, điều này sẽ trả về:

3 1/8/2010
5 1/16/2010

Tôi đã phải sử dụng thêm hàm DATE () để cắt bớt trường ngày giờ của mình.

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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.