Lịch lặp lại / Sự kiện lặp lại - Phương pháp lưu trữ tốt nhất


311

Tôi đang xây dựng một hệ thống sự kiện tùy chỉnh và nếu bạn có một sự kiện lặp lại giống như thế này:

Sự kiện A lặp lại cứ sau 4 ngày bắt đầu từ ngày 3 tháng 3 năm 2011

hoặc là

Sự kiện B lặp lại cứ sau 2 tuần vào thứ ba bắt đầu từ ngày 1 tháng 3 năm 2011

Làm cách nào tôi có thể lưu trữ dữ liệu đó trong Cơ sở dữ liệu theo cách giúp đơn giản hóa việc tra cứu. Tôi không muốn các vấn đề về hiệu suất nếu có một số lượng lớn các sự kiện và tôi phải trải qua từng sự kiện khi kết xuất lịch.


Bạn có thể giải thích tại sao 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ể?
Murali Murugesan

@Murali Boy này đã cũ, nhưng tôi khá chắc chắn 1299132000 được cho là ngày hiện tại.
Brandon Wamboldt

@BrandonWamboldt, tôi đã thử ý tưởng của bạn với SQL Server. stackoverflow.com/questions/20286332/display-next-event-date . Tôi muốn tìm tất cả các mục tiếp theo như phiên bản c #
Billa

Câu trả lời:


211

Lưu trữ các mẫu lặp lại "Đơn giản"

Đối với lịch dựa trên PHP / MySQL của tôi, tôi muốn lưu trữ thông tin sự kiện lặp lại / định kỳ một cách hiệu quả nhất có thể. Tôi không muốn có một số lượng lớn các hàng và tôi muốn dễ dàng tra cứu tất cả các sự kiện sẽ diễn ra vào một ngày cụ thể.

Phương pháp dưới đây rất tốt trong việc lưu trữ thông tin lặp lại xảy ra đều đặn, chẳng hạn như mỗi ngày, mỗi n ngày, mỗi tuần, mỗi tháng mỗi năm, v.v. riêng biệt như mọi tuần bắt đầu vào thứ ba và mỗi tuần bắt đầu từ thứ năm.

Giả sử tôi có hai bảng, một bảng được gọi eventsnhư thế này:

ID    NAME
1     Sample Event
2     Another Event

Và một bảng gọi events_metanhư thế này:

ID    event_id      meta_key           meta_value
1     1             repeat_start       1299132000
2     1             repeat_interval_1  432000

Với repeat_start là một ngày không có thời gian là dấu thời gian unix và repeat_interval một lượng tính bằng giây giữa các khoảng thời gian (432000 là 5 ngày).

repeat_interval_1 đi với repeat_start của ID 1. Vì vậy, nếu tôi có một sự kiện lặp lại vào thứ ba và thứ năm hàng tuần, thì repeat_interval sẽ là 604800 (7 ngày) và sẽ có 2 repeat_starts và 2 repeat_inter. Bảng sẽ trông như thế này:

ID    event_id      meta_key           meta_value
1     1             repeat_start       1298959200 -- This is for the Tuesday repeat
2     1             repeat_interval_1  604800
3     1             repeat_start       1299132000 -- This is for the Thursday repeat
4     1             repeat_interval_3  604800
5     2             repeat_start       1299132000
6     2             repeat_interval_5  1          -- Using 1 as a value gives us an event that only happens once

Sau đó, nếu bạn có một lịch lặp đi lặp lại mỗi ngày, lấy các sự kiện cho ngày đó, truy vấn sẽ như thế này:

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
LIMIT 0 , 30

Thay thế {current_timestamp}bằng dấu thời gian unix cho ngày hiện tại (Trừ thời gian, do đó giá trị giờ, phút và giây sẽ được đặt thành 0).

Hy vọng điều này sẽ giúp người khác quá!


Lưu trữ các mẫu lặp lại "phức tạp"

Phương pháp này phù hợp hơn để lưu trữ các mẫu phức tạp như

Event A repeats every month on the 3rd of the month starting on March 3, 2011

hoặc là

Event A repeats Friday of the 2nd week of the month starting on March 11, 2011

Tôi khuyên bạn nên kết hợp điều này với hệ thống trên để linh hoạt nhất. Các bảng cho điều này sẽ như thế nào:

ID    NAME
1     Sample Event
2     Another Event

Và một bảng gọi events_metanhư thế này:

ID    event_id      meta_key           meta_value
1     1             repeat_start       1299132000 -- March 3rd, 2011
2     1             repeat_year_1      *
3     1             repeat_month_1     *
4     1             repeat_week_im_1   2
5     1             repeat_weekday_1   6

repeat_week_imđại diện cho tuần của tháng hiện tại, có thể nằm trong khoảng từ 1 đến 5. repeat_weekdaytrong ngày trong tuần, 1-7.

Bây giờ giả sử bạn đang lặp qua các ngày / tuần để tạo chế độ xem theo tháng trong lịch của mình, bạn có thể soạn một truy vấn như thế này:

SELECT EV . *
FROM `events` AS EV
JOIN `events_meta` EM1 ON EM1.event_id = EV.id
AND EM1.meta_key = 'repeat_start'
LEFT JOIN `events_meta` EM2 ON EM2.meta_key = CONCAT( 'repeat_year_', EM1.id )
LEFT JOIN `events_meta` EM3 ON EM3.meta_key = CONCAT( 'repeat_month_', EM1.id )
LEFT JOIN `events_meta` EM4 ON EM4.meta_key = CONCAT( 'repeat_week_im_', EM1.id )
LEFT JOIN `events_meta` EM5 ON EM5.meta_key = CONCAT( 'repeat_weekday_', EM1.id )
WHERE (
  EM2.meta_value =2011
  OR EM2.meta_value = '*'
)
AND (
  EM3.meta_value =4
  OR EM3.meta_value = '*'
)
AND (
  EM4.meta_value =2
  OR EM4.meta_value = '*'
)
AND (
  EM5.meta_value =6
  OR EM5.meta_value = '*'
)
AND EM1.meta_value >= {current_timestamp}
LIMIT 0 , 30

Điều này kết hợp với phương pháp trên có thể được kết hợp để bao gồm hầu hết các mẫu sự kiện lặp lại / lặp lại. Nếu tôi bỏ lỡ bất cứ điều gì xin vui lòng để lại nhận xét.


1
Tôi đang thử các mẫu lặp lại "Đơn giản" của bạn. nếu tôi cần nó lặp lại mỗi tuần vào thứ ba, tôi có cần sửa đổi repeat_start hoặc tạo một bản ghi mới với ngày cuối cùng không. hoặc có cách nào để nó lặp lại mỗi tuần dựa trên lần lặp đầu tiên ???
loo

1
Câu trả lời của bạn là một sự giúp đỡ @roguecoder lớn nhưng nó đã không hoàn toàn làm việc off the bat ... Tôi tìm thấy câu trả lời của tôi sau khi bài đăng này: stackoverflow.com/questions/10545869/...
Bến Sinclair

1
Trong AND ( ( CASE ( 1299132000 - EM1.meta_value ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.meta_value) END ) / EM2.meta_value ) = 1này / EM2.meta_valueđược đặt sai?
Murali Murugesan

1
Đây là một trợ giúp lớn. Làm thế nào bạn có thể đề nghị truy cập chúng dưới dạng hồ sơ cá nhân, nếu bạn muốn có ý kiến ​​hoặc đăng ký về các sự kiện riêng lẻ?
johnrees

26
Điều đáng chú ý là bạn không nên sử dụng các giá trị được mã hóa cứng trong các khoảng thời gian lặp lại, tức là 86400vài giây trong một ngày, vì nó không ảnh hưởng đến thời gian tiết kiệm ánh sáng ban ngày. Đó là thích hợp hơn để tính toán những điều động một cách nhanh chóng và thay vào đó lưu trữ interval = dailyinterval_count = 1hay interval = monthlyinterval_count = 1.
Corey Ballou

185

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 eventsnhư thế này:

ID    NAME
1     Sample Event
2     Another Event

Và một bảng gọi events_metanhư 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_startlà 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_intervalsố 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_metanhư 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_weekdaydữ 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!


6
Điều này thật tuyệt vời, cảm ơn bạn! Bạn có biết mình sẽ mã hóa "cứ sau 2 tháng vào thứ Hai đầu tiên" hay "cứ sau 3 tháng vào thứ Hai đầu tiên", v.v.?
Jordan Lev

6
Tôi đồng ý điều này là tuyệt vời. Tuy nhiên, tôi gặp phải tình huống khó xử như Jordan Lev đã làm. Trường repeat_interval không tốt cho việc lặp lại các tháng vì một số tháng dài hơn các tháng khác. Ngoài ra, làm thế nào để bạn giới hạn thời gian của một sự kiện định kỳ. Tức là, cứ sau 2 tháng vào thứ Hai đầu tiên trong 8 tháng. Bảng nên có một số loại ngày kết thúc.
Abinadi

11
Đây là một câu trả lời tuyệt vời. Tôi đã bỏ ngày lặp lại và thêm ngày lặp lại, nhưng câu trả lời này đã giúp rất nhiều.
Iain Collins

3
Mẹo: Đối với các mẫu phức tạp, người ta có thể loại bỏ repeat_intervalcột và biểu thị nó trong các cột tiếp theo (ví dụ repeat_year: v.v.) Đối với hàng đầu tiên, tình huống lặp lại vào mỗi thứ Hai sau ngày 20 tháng 5 năm 2013, có thể được biểu thị bằng cách đặt 1 vào repeat_weekdayvà một *trong các cột khác.
musubi

3
@OlivierMATROT @milos Ý tưởng là đặt trường bạn muốn được sửa một cách rõ ràng và phần còn lại thành ký tự đại diện *. Vì vậy, đối với "mỗi tháng vào ngày 3", bạn chỉ cần đặt repeat_daythành 3, các trường còn lại repeatthành * (để repeat_intervaltrống) và đặt repeat_start thành mã thời gian unix cho ngày 3 tháng 3 năm 2011 là ngày neo của bạn.
ahoffner

28

Cải tiến: thay thế dấu thời gian bằng ngày

Là một cải tiến nhỏ cho câu trả lời được chấp nhận mà sau đó đã được tinh chỉnh bởi ahoffner - có thể sử dụng định dạng ngày thay vì dấu thời gian. Những ưu điểm là:

  1. ngày có thể đọc được trong cơ sở dữ liệu
  2. không có vấn đề với năm> 2038 và dấu thời gian
  3. cần phải cẩn thận với các dấu thời gian dựa trên các ngày được điều chỉnh theo mùa, tức là ở Vương quốc Anh ngày 28 tháng 6 bắt đầu sớm hơn một giờ so với ngày 28 tháng 12 để có được dấu thời gian từ ngày có thể phá vỡ thuật toán đệ quy.

để thực hiện việc này, thay đổi DB repeat_startsẽ được lưu dưới dạng 'ngày' và repeat_intervalhiện giữ ngày thay vì giây. tức là 7 cho lặp lại 7 ngày.

thay đổi dòng sql:

WHERE (( 1370563200 - repeat_start) % repeat_interval = 0 )

đến:

WHERE ( DATEDIFF( '2013-6-7', repeat_start ) % repeat_interval = 0)

Mọi thứ khác vẫn giữ nguyên. Đơn giản!


Vậy nếu tôi muốn sự kiện của mình lặp lại hàng năm thì sao? repeat_interval nên lưu trữ 365 ngày? Điều gì sẽ xảy ra nếu năm của họ có 365 ngày?
TGeorge

3
@ George02 nếu sự kiện là hàng năm, bạn rời lặp lại NULL và lặp lại
jerrygarciuh

25

Tôi sẽ làm theo hướng dẫn này: https://github.com/bmoeskau/Extensible/blob/master/recurrence-overview.md

Ngoài ra, hãy đảm bảo bạn sử dụng định dạng iCal để không phát minh lại bánh xe và nhớ Quy tắc số 0: KHÔNG lưu trữ các trường hợp sự kiện định kỳ riêng lẻ dưới dạng hàng trong cơ sở dữ liệu của bạn!


2
Làm thế nào bạn có thể mô hình hóa một người dùng theo dõi đã tham dự một trường hợp cụ thể? Liệu nó có ý nghĩa để thoát khỏi Quy tắc # 0 trong trường hợp này?
Daniel Sullivan

2
@DannySullivan Từ đỉnh đầu của tôi, tôi sẽ có một thực thể khác attendedEventvới baseInstanceIdinstanceStartDate- Đó là ví dụ về sự kiện cơ bản mà bạn đã tạo chế độ xem lịch quy tắc định kỳ và sử dụng ngày bắt đầu để chỉ định thông tin về trường hợp cụ thể đó. Thực thể này cũng có thể có một cái gì đó giống như attendedListIddẫn đến một bảng khác id,attendedUserId
Gal Bracha

@DannySullivan Tôi biết đã một lúc kể từ khi bạn hỏi. Nhưng bên ngoài nhận xét trước đó, bạn luôn có thể thực hiện tra cứu ngược lại để xem liệu người dùng đó có phải là một phần của mẫu tái phát sự kiện hay không. Điều đó sẽ cho bạn biết nếu họ ít nhất đã được lên lịch cho sự kiện này. Cho dù họ có thực sự tham dự hay không là một câu chuyện khác nhau sẽ phù hợp hơn với những lời bình luận của DannySullivan.
BRogers

24

Đối với tất cả các bạn quan tâm đến điều này, bây giờ bạn chỉ cần sao chép và dán để bắt đầu trong vòng vài phút. Tôi đã lấy lời khuyên trong các ý kiến ​​tốt nhất có thể. Hãy cho tôi biết nếu tôi thiếu một cái gì đó.

"PHIÊN BẢN COMPLEX":

sự kiện

+ ---------- + ---------------- +
| ID | TÊN |
+ ---------- + ---------------- +
| 1 | Sự kiện mẫu 1 |
| 2 | Sự kiện thứ hai |
| 3 | Sự kiện thứ ba |
+ ---------- + ---------------- +

sự kiện

+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| ID | event_id | repeat_start | repeat_interval | repeat_year | lặp lại_month | lặp lại_day | repeat_week | repeat_weekday |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| 1 | 1 | 2014/07-04 | 7 | NULL | NULL | NULL | NULL | NULL |
| 2 | 2 | 2014-06-26 | NULL | 2014 | * | * | 2 | 5 |
| 3 | 3 | 2014/07-04 | NULL | * | * | * | * | 5 |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +

Mã SQL:

CREATE TABLE IF NOT EXISTS `events` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(255) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;

--
-- Dumping data for table `events`
--

INSERT INTO `events` (`ID`, `NAME`) VALUES
(1, 'Sample event'),
(2, 'Another event'),
(3, 'Third event...');

CREATE TABLE IF NOT EXISTS `events_meta` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `event_id` int(11) NOT NULL,
  `repeat_start` date NOT NULL,
  `repeat_interval` varchar(255) NOT NULL,
  `repeat_year` varchar(255) NOT NULL,
  `repeat_month` varchar(255) NOT NULL,
  `repeat_day` varchar(255) NOT NULL,
  `repeat_week` varchar(255) NOT NULL,
  `repeat_weekday` varchar(255) NOT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `ID` (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;

--
-- Dumping data for table `events_meta`
--

INSERT INTO `events_meta` (`ID`, `event_id`, `repeat_start`, `repeat_interval`, `repeat_year`, `repeat_month`, `repeat_day`, `repeat_week`, `repeat_weekday`) VALUES
(1, 1, '2014-07-04', '7', 'NULL', 'NULL', 'NULL', 'NULL', 'NULL'),
(2, 2, '2014-06-26', 'NULL', '2014', '*', '*', '2', '5'),
(3, 3, '2014-07-04', 'NULL', '*', '*', '*', '*', '1');

cũng có sẵn dưới dạng xuất MySQL (để dễ dàng truy cập)

Mã ví dụ PHP index.php:

<?php
    require 'connect.php';    

    $now = strtotime("yesterday");

    $pushToFirst = -11;
    for($i = $pushToFirst; $i < $pushToFirst+30; $i++)
    {
        $now = strtotime("+".$i." day");
        $year = date("Y", $now);
        $month = date("m", $now);
        $day = date("d", $now);
        $nowString = $year . "-" . $month . "-" . $day;
        $week = (int) ((date('d', $now) - 1) / 7) + 1;
        $weekday = date("N", $now);

        echo $nowString . "<br />";
        echo $week . " " . $weekday . "<br />";



        $sql = "SELECT EV.*
                FROM `events` EV
                RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
                WHERE ( DATEDIFF( '$nowString', repeat_start ) % repeat_interval = 0 )
                OR ( 
                    (repeat_year = $year OR repeat_year = '*' )
                    AND
                    (repeat_month = $month OR repeat_month = '*' )
                    AND
                    (repeat_day = $day OR repeat_day = '*' )
                    AND
                    (repeat_week = $week OR repeat_week = '*' )
                    AND
                    (repeat_weekday = $weekday OR repeat_weekday = '*' )
                    AND repeat_start <= DATE('$nowString')
                )";
        foreach ($dbConnect->query($sql) as $row) {
            print $row['ID'] . "\t";
            print $row['NAME'] . "<br />";
        }

        echo "<br /><br /><br />";
    }
?>

Mã ví dụ PHP connect.php:

<?
// ----------------------------------------------------------------------------------------------------
//                                       Connecting to database
// ----------------------------------------------------------------------------------------------------
// Database variables
$username = "";
$password = "";
$hostname = ""; 
$database = ""; 

// Try to connect to database and set charset to UTF8
try {
    $dbConnect = new PDO("mysql:host=$hostname;dbname=$database;charset=utf8", $username, $password);
    $dbConnect->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

} catch(PDOException $e) {
    echo 'ERROR: ' . $e->getMessage();
}
// ----------------------------------------------------------------------------------------------------
//                                      / Connecting to database
// ----------------------------------------------------------------------------------------------------
?>

Ngoài ra mã php có sẵn ở đây (để dễ đọc hơn):
index.php

connect.php
Bây giờ, việc thiết lập này sẽ khiến bạn mất vài phút. Không phải giờ. :)


2
làm thế nào tôi có thể truy vấn để có được tất cả các sự kiện lặp lại trong phạm vi ngày .. đó là để có được tất cả các sự kiện lặp lại trong khoảng thời gian 2014-10-01 đến 2014-12-30. cảm ơn vì bài đăng của bạn
Well Wisher

@Wellwisher - lặp lại ... cho đến khi và bảng tạm thời stackoverflow.com/questions/34407833/...
Brad Kent

1
@Alex Làm thế nào tôi có thể xóa một trường hợp xảy ra từ sự kiện lặp đi lặp lại.
Pugazeaverhi

1
Tôi biết rằng đây là một chủ đề cũ nhưng tại sao loại varchar trên cột repeat_ *? Bạn không thể sử dụng số nguyên và giá trị âm thay vì '*'?
Olivier MATROT

1
Cảm ơn mã. Tuy nhiên tôi phải nhận xét rằng việc triển khai db / truy vấn của bạn hơi đáng lo ngại và rất không hiệu quả. Ví dụ, tại sao sử dụng varchar (255) cho các cột đơn giản như vậy (như @OlivierMATROT đã đề cập, bạn có thể sử dụng số nguyên và thậm chí nếu không, tại sao 255?). Và nếu bạn đang lặp lại truy vấn 30 lần, tại sao không sử dụng các câu lệnh hoặc thủ tục? Chỉ nói vì lợi ích nếu ai đó sắp thực hiện điều này.
Rony

15

Trong khi các giải pháp được đề xuất hoạt động, tôi đã cố gắng thực hiện với Lịch đầy đủ và nó sẽ yêu cầu hơn 90 cuộc gọi cơ sở dữ liệu cho mỗi chế độ xem (vì nó tải hiện tại, trước đó và tháng tới), điều mà tôi không quá vui mừng.

Tôi tìm thấy một thư viện đệ quy https://github.com/tplaner/Khi bạn chỉ cần lưu trữ các quy tắc trong cơ sở dữ liệu và một truy vấn để lấy tất cả các quy tắc có liên quan.

Hy vọng rằng điều này sẽ giúp người khác, vì tôi đã dành rất nhiều giờ cố gắng để tìm một giải pháp tốt.

Chỉnh sửa: Thư viện này là dành cho PHP


Tôi cũng muốn sử dụng fullcalWiki. Làm thế nào khi thư viện có thể giúp tôi? Làm thế nào để trích xuất các sự kiện propoer?
piernik

@piernik - Tôi sẽ thiết lập thư viện như trong tài liệu và nếu bạn đang gặp vấn đề cụ thể, hãy mở một câu hỏi mới trên stackoverflow với mã bạn đã thiết lập và các vấn đề bạn đang gặp phải. Tôi chắc chắn nếu bạn nỗ lực nhiều trong một số thành viên sẽ giúp bạn.
Tim Ramsey

Ý tôi là việc sử dụng WhenBạn phải lưu trữ tất cả các ngày lưu trữ trong cơ sở dữ liệu hoặc nhận tất cả các sự kiện giải mã và tạo ngày trong php no trong cơ sở dữ liệu. Tôi có đúng không
piernik

@piernik Bạn sẽ lưu trữ Ngày ban đầu và quy tắc / s trong cơ sở dữ liệu và sử dụng Whenđể tạo tất cả các ngày - được điền từ ngày / quy tắc được lưu trữ ban đầu.
Tim Ramsey

Điều đó cũng không tốt - Bạn không thể trong một lệnh mysql có được các sự kiện chính xác - Bạn phải sử dụng PHP cho điều đó. Dù sao cũng cảm ơn
piernik

14

Tại sao không sử dụng một cơ chế tương tự như các công việc định kỳ của Apache? http://en.wikipedia.org/wiki/Cron

Đối với lịch \ lập lịch, tôi sẽ sử dụng các giá trị hơi khác nhau cho "bit" để phù hợp với các sự kiện tái sử dụng lịch tiêu chuẩn - thay vì [ngày trong tuần (0 - 7), tháng (1 - 12), ngày trong tháng (1 - 31), giờ (0 - 23), phút (0 - 59)]

- Tôi sẽ sử dụng một cái gì đó như [Năm (lặp lại mỗi N năm), tháng (1 - 12), ngày trong tháng (1 - 31), tuần trong tháng (1-5), ngày trong tuần (0 - 7) ]

Hi vọng điêu nay co ich.


6
Tôi nghĩ rằng đó là quá nhiều lựa chọn trong ngày. 1-7 hoặc 0-6 có vẻ chính xác hơn.
Abinadi

2
Thật tốt khi sử dụng cron để lưu trữ lặp lại. nhưng vấn đề là rất khó tra cứu.
Stony

@Vladimir làm thế nào để bạn lưu trữ, cứ hai ngày thứ ba (cứ hai tuần một lần vào thứ ba)
julestruong

@julestruong trang web này có vẻ như đã có câu trả lời: coderwall.com/p/yzzu5a/rasty-a-cron-job-every-other-week
Ashton Wiersdorf

cron có tính biểu cảm hạn chế, vì nó không trạng thái (chỉ so sánh ngày / thời gian hiện tại / thời gian hạ nhiệt với một mẫu), do đó, nó không thể đại diện cho một số mô hình kinh doanh / con người phổ biến như "mỗi ngày thứ ba" hoặc "cứ sau 7 giờ", yêu cầu ghi nhớ sự xuất hiện cuối cùng Điều này không rõ ràng; bạn có thể nghĩ rằng bạn chỉ cần nói ngày / 3 hoặc giờ / 7 trong crontab, nhưng sau đó vào cuối tháng / ngày, bạn có "ngày còn lại" ngày / giờ ít hơn 3 hoặc 7; với kết quả thảm khốc có thể.
Jaime Guerrero

5

Tôi đã phát triển một ngôn ngữ lập trình bí truyền chỉ cho trường hợp này. Phần tốt nhất về nó là lược đồ ít hơn và nền tảng độc lập. Bạn chỉ cần viết một chương trình chọn, cho lịch trình của bạn, cú pháp bị ràng buộc bởi bộ quy tắc được mô tả ở đây -

https://github.com/tusharmath/sheql/wiki/Rules

Các quy tắc có thể mở rộng và bạn có thể thêm bất kỳ loại tùy chỉnh nào dựa trên loại logic lặp lại mà bạn muốn thực hiện mà không phải lo lắng về việc di chuyển lược đồ, v.v.

Đây là một cách tiếp cận hoàn toàn khác và có thể có một số nhược điểm của riêng nó.


4

Âm thanh rất giống các sự kiện MySQL được lưu trữ trong các bảng hệ thống. Bạn có thể nhìn vào cấu trúc và tìm ra cột nào không cần thiết:

   EVENT_CATALOG: NULL
    EVENT_SCHEMA: myschema
      EVENT_NAME: e_store_ts
         DEFINER: jon@ghidora
      EVENT_BODY: SQL
EVENT_DEFINITION: INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP())
      EVENT_TYPE: RECURRING
      EXECUTE_AT: NULL
  INTERVAL_VALUE: 5
  INTERVAL_FIELD: SECOND
        SQL_MODE: NULL
          STARTS: 0000-00-00 00:00:00
            ENDS: 0000-00-00 00:00:00
          STATUS: ENABLED
   ON_COMPLETION: NOT PRESERVE
         CREATED: 2006-02-09 22:36:06
    LAST_ALTERED: 2006-02-09 22:36:06
   LAST_EXECUTED: NULL
   EVENT_COMMENT:


3

@ Mã hóa

Điều đó thật tuyệt!

Bạn chỉ có thể sử dụng thao tác modulo (MOD hoặc% trong mysql) để làm cho mã của bạn đơn giản ở cuối:

Thay vì:

AND (
    ( CASE ( 1299132000 - EM1.`meta_value` )
        WHEN 0
          THEN 1
        ELSE ( 1299132000 - EM1.`meta_value` )
      END
    ) / EM2.`meta_value`
) = 1

Làm:

$current_timestamp = 1299132000 ;

AND ( ('$current_timestamp' - EM1.`meta_value` ) MOD EM2.`meta_value`) = 1")

Để thực hiện điều này hơn nữa, người ta có thể bao gồm các sự kiện không tái diễn mãi mãi.

Một cái gì đó như "repeat_interval_1_end" để biểu thị ngày của "repeat_interval_1" cuối cùng có thể được thêm vào. Tuy nhiên, điều này làm cho truy vấn trở nên phức tạp hơn và tôi thực sự không thể tìm ra cách để làm điều này ...

Có lẽ ai đó có thể giúp đỡ!


1

Hai ví dụ bạn đưa ra rất đơn giản; chúng có thể được biểu diễn dưới dạng một khoảng đơn giản (lần đầu tiên là bốn ngày, lần thứ hai là 14 ngày). Cách bạn mô hình hóa điều này sẽ phụ thuộc hoàn toàn vào sự phức tạp của các đợt tái phát của bạn. Nếu những gì bạn có ở trên thực sự đơn giản, thì hãy lưu trữ ngày bắt đầu và số ngày trong khoảng thời gian lặp lại.

Tuy nhiên, nếu bạn cần hỗ trợ những thứ như

Sự kiện A lặp lại hàng tháng vào ngày 3 của tháng bắt đầu từ ngày 3 tháng 3 năm 2011

Hoặc là

Sự kiện A lặp lại thứ Sáu thứ hai trong tháng bắt đầu từ ngày 11 tháng 3 năm 2011

Sau đó, đó là một mô hình phức tạp hơn nhiều.


1
Tôi đã thêm các quy tắc phức tạp hơn mà bạn vừa nêu ở một thời điểm sau nhưng không phải bây giờ. Làm cách nào để mô hình hóa truy vấn SQL để nhận các sự kiện vào ngày 7 tháng 3 năm 2011 để nó có được sự kiện định kỳ của tôi?
Brandon Wamboldt
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.