Mặc dù câu trả lời hiện được chấp nhận là một trợ giúp rất lớn đối với tôi, tôi muốn chia sẻ một số sửa đổi hữu ích giúp đơn giản hóa các truy vấn và cũng tăng hiệu suất.
Sự kiện lặp lại "đơn giản"
Để xử lý các sự kiện tái diễn đều đặn, như:
Repeat every other day
hoặc là
Repeat every week on Tuesday
Bạn nên tạo hai bảng, một bảng được gọi events
như thế này:
ID NAME
1 Sample Event
2 Another Event
Và một bảng gọi events_meta
như thế này:
ID event_id repeat_start repeat_interval
1 1 1369008000 604800 -- Repeats every Monday after May 20th 2013
1 1 1369008000 604800 -- Also repeats every Friday after May 20th 2013
Với ngày repeat_start
là dấu thời gian unix không có thời gian (1369008000 tương ứng với ngày 20 tháng 5 năm 2013) và repeat_interval
số tiền tính bằng giây giữa các khoảng thời gian (604800 là 7 ngày).
Bằng cách lặp qua từng ngày trong lịch, bạn có thể nhận được các sự kiện lặp lại bằng truy vấn đơn giản này:
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE (( 1299736800 - repeat_start) % repeat_interval = 0 )
Chỉ cần thay thế trong dấu thời gian unix (1299736800) cho mỗi ngày trong lịch của bạn.
Lưu ý việc sử dụng modulo (ký hiệu%). Biểu tượng này giống như phép chia thông thường, nhưng trả về '' phần còn lại '' thay vì thương số và như vậy là 0 bất cứ khi nào ngày hiện tại là bội số chính xác của repeat_interval từ repeat_start.
So sánh hiệu suất
Điều này nhanh hơn đáng kể so với câu trả lời dựa trên "meta_keys" được đề xuất trước đây, như sau:
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
RIGHT JOIN `events_meta` EM2 ON EM2.`meta_key` = CONCAT( 'repeat_interval_', EM1.`id` )
WHERE EM1.meta_key = 'repeat_start'
AND (
( CASE ( 1299132000 - EM1.`meta_value` )
WHEN 0
THEN 1
ELSE ( 1299132000 - EM1.`meta_value` )
END
) / EM2.`meta_value`
) = 1
Nếu bạn chạy GIẢI THÍCH truy vấn này, bạn sẽ lưu ý rằng nó yêu cầu sử dụng bộ đệm tham gia:
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| 1 | SIMPLE | EM1 | ALL | NULL | NULL | NULL | NULL | 2 | Using where |
| 1 | SIMPLE | EV | eq_ref | PRIMARY | PRIMARY | 4 | bcs.EM1.event_id | 1 | |
| 1 | SIMPLE | EM2 | ALL | NULL | NULL | NULL | NULL | 2 | Using where; Using join buffer |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
Các giải pháp với 1 tham gia ở trên không yêu cầu bộ đệm như vậy.
Mô hình "phức tạp"
Bạn có thể thêm hỗ trợ cho các loại phức tạp hơn để hỗ trợ các loại quy tắc lặp lại này:
Event A repeats every month on the 3rd of the month starting on March 3, 2011
hoặc là
Event A repeats second Friday of the month starting on March 11, 2011
Bảng sự kiện của bạn có thể trông giống hệt nhau:
ID NAME
1 Sample Event
2 Another Event
Sau đó, để thêm hỗ trợ cho các quy tắc phức tạp này, hãy thêm các cột events_meta
như vậy:
ID event_id repeat_start repeat_interval repeat_year repeat_month repeat_day repeat_week repeat_weekday
1 1 1369008000 604800 NULL NULL NULL NULL NULL -- Repeats every Monday after May 20, 2013
1 1 1368144000 604800 NULL NULL NULL NULL NULL -- Repeats every Friday after May 10, 2013
2 2 1369008000 NULL 2013 * * 2 5 -- Repeats on Friday of the 2nd week in every month
Lưu ý rằng bạn chỉ cần một trong hai chỉ định một repeat_interval
hoặc một bộ repeat_year
, repeat_month
, repeat_day
, repeat_week
, và repeat_weekday
dữ liệu.
Điều này làm cho việc lựa chọn cả hai loại đồng thời rất đơn giản. Chỉ cần lặp qua từng ngày và điền vào các giá trị chính xác, (1370563200 cho ngày 7 tháng 6 năm 2013, sau đó là năm, tháng, ngày, số tuần và ngày trong tuần như sau):
SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE (( 1370563200 - repeat_start) % repeat_interval = 0 )
OR (
(repeat_year = 2013 OR repeat_year = '*' )
AND
(repeat_month = 6 OR repeat_month = '*' )
AND
(repeat_day = 7 OR repeat_day = '*' )
AND
(repeat_week = 2 OR repeat_week = '*' )
AND
(repeat_weekday = 5 OR repeat_weekday = '*' )
AND repeat_start <= 1370563200
)
Điều này trả về tất cả các sự kiện lặp lại vào Thứ Sáu của tuần thứ 2, cũng như bất kỳ sự kiện nào lặp lại vào mỗi Thứ Sáu, do đó, nó trả về cả ID sự kiện 1 và 2:
ID NAME
1 Sample Event
2 Another Event
* Sidenote trong SQL ở trên Tôi đã sử dụng các chỉ mục ngày mặc định của Ngày của PHP , vì vậy "5" cho thứ Sáu
Hy vọng điều này sẽ giúp những người khác nhiều như câu trả lời ban đầu đã giúp tôi!
1299132000
mã hóa cứng? Điều này sẽ làm gì nếu tôi cần lấy ngày xảy ra và người dùng cho ngày kết thúc cụ thể?