Kalender Acara Berulang / Berulang - Metode Penyimpanan Terbaik


311

Saya sedang membangun sistem acara khusus, dan jika Anda memiliki acara berulang yang terlihat seperti ini:

Event A berulang setiap 4 hari mulai tanggal 3 Maret 2011

atau

Acara B berulang setiap 2 minggu pada hari Selasa mulai tanggal 1 Maret 2011

Bagaimana saya bisa menyimpannya dalam Database dengan cara yang akan memudahkan pencarian. Saya tidak ingin masalah kinerja jika ada sejumlah besar acara, dan saya harus membahas masing-masing dan setiap saat membuat kalender.


Bisakah Anda jelaskan mengapa 1299132000 hardcoded? Apa yang akan dilakukan jika saya perlu mendapatkan tanggal dan pengguna untuk tanggal akhir tertentu?
Murali Murugesan

@Murali Boy ini sudah tua, tapi saya cukup yakin 1299132000 seharusnya menjadi tanggal saat ini.
Brandon Wamboldt

@BrandonWamboldt, saya mencoba ide Anda dengan SQL Server. stackoverflow.com/questions/20286332/display-next-event-date . Saya ingin menemukan semua item berikutnya seperti versi c #
Billa

Jawaban:


211

Menyimpan Pola Pengulangan "Sederhana"

Untuk kalender berbasis PHP / MySQL saya, saya ingin menyimpan informasi acara berulang / berulang seefisien mungkin. Saya tidak ingin memiliki banyak baris, dan saya ingin dengan mudah mencari semua acara yang akan berlangsung pada tanggal tertentu.

Metode di bawah ini adalah besar di menyimpan mengulangi informasi yang terjadi secara berkala, misalnya setiap hari, setiap n hari, setiap minggu, setiap bulan setiap tahun, dll dll Ini termasuk setiap Selasa dan Kamis jenis pola juga, karena mereka disimpan secara terpisah setiap minggu dimulai pada hari Selasa dan setiap minggu dimulai pada hari Kamis.

Dengan asumsi saya punya dua tabel, yang disebut eventsseperti ini:

ID    NAME
1     Sample Event
2     Another Event

Dan sebuah tabel bernama events_metaseperti ini:

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

Dengan repeat_start menjadi tanggal tanpa waktu sebagai cap waktu unix, dan repeat_interval jumlah dalam detik antar interval (432000 adalah 5 hari).

repeat_interval_1 berjalan dengan repeat_start dari ID 1. Jadi jika saya memiliki acara yang berulang setiap hari Selasa dan setiap hari Kamis, repeat_interval akan menjadi 604800 (7 hari), dan akan ada 2 repeat_start dan 2 repeat_intervals. Tabelnya akan terlihat seperti ini:

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

Kemudian, jika Anda memiliki kalender yang berulang setiap hari, mengambil acara untuk hari itu, kueri akan terlihat seperti ini:

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

Mengganti {current_timestamp}dengan cap waktu unix untuk tanggal saat ini (Mengurangi waktu, sehingga nilai jam, menit dan kedua akan ditetapkan ke 0).

Semoga ini akan membantu orang lain juga!


Menyimpan Pola Pengulangan "Kompleks"

Metode ini lebih cocok untuk menyimpan pola yang kompleks seperti

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

atau

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

Saya akan merekomendasikan menggabungkan ini dengan sistem di atas untuk fleksibilitas paling. Tabel untuk ini harus seperti:

ID    NAME
1     Sample Event
2     Another Event

Dan sebuah tabel bernama events_metaseperti ini:

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_immewakili minggu pada bulan berjalan, yang bisa berpotensi antara 1 dan 5. repeat_weekdaydi hari minggu, 1-7.

Sekarang dengan asumsi Anda mengulangi hari / minggu untuk membuat tampilan bulan di kalender Anda, Anda dapat membuat kueri seperti ini:

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

Ini dikombinasikan dengan metode di atas dapat dikombinasikan untuk mencakup sebagian besar pola peristiwa berulang / berulang. Jika saya melewatkan sesuatu, silakan tinggalkan komentar.


1
Saya mencoba Menyimpan Pola Pengulangan "Sederhana" Anda. jika saya perlu mengulangi setiap minggu pada hari Selasa, apakah saya perlu memodifikasi repeat_start atau membuat catatan baru dengan tanggal terakhir. atau adakah cara untuk mengulanginya setiap minggu berdasarkan repeat_start pertama ???
loo

1
Jawaban Anda sangat membantu @roguecoder tetapi tidak cukup berhasil ... Saya menemukan jawaban saya setelah posting ini: stackoverflow.com/questions/10545869/…
Ben Sinclair

1
Dalam AND ( ( CASE ( 1299132000 - EM1.meta_value ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.meta_value) END ) / EM2.meta_value ) = 1adalah ini / EM2.meta_valueditempatkan salah?
Murali Murugesan

1
Ini sangat membantu. Bagaimana Anda menyarankan untuk mengakses ini sebagai catatan individu, katakanlah, jika Anda ingin memiliki komentar atau lapor-masuk pada peristiwa individual?
johnrees

26
Perlu dicatat bahwa Anda tidak boleh menggunakan nilai-nilai hardcoded untuk interval ulangi, yaitu 86400detik dalam sehari, karena itu tidak memperhitungkan waktu musim panas. Lebih tepat untuk menghitung hal-hal ini secara dinamis dengan cepat dan bukannya menyimpan interval = dailydan interval_count = 1atau interval = monthlydan interval_count = 1.
Corey Ballou

185

Sementara jawaban yang diterima saat ini sangat membantu saya, saya ingin berbagi beberapa modifikasi berguna yang menyederhanakan pertanyaan dan juga meningkatkan kinerja.


Acara Ulangi "Sederhana"

Untuk menangani acara yang berulang secara berkala, seperti:

Repeat every other day 

atau

Repeat every week on Tuesday 

Anda harus membuat dua tabel, yang disebut eventsseperti ini:

ID    NAME
1     Sample Event
2     Another Event

Dan sebuah tabel bernama events_metaseperti ini:

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

Dengan repeat_startmenjadi tanggal cap waktu unix tanpa waktu (1369008000 sesuai dengan 20 Mei 2013), dan repeat_intervaljumlah dalam detik antar interval (604800 adalah 7 hari).

Dengan mengulang setiap hari di kalender, Anda bisa mendapatkan acara berulang menggunakan kueri sederhana ini:

SELECT EV.*
FROM `events` EV
RIGHT JOIN `events_meta` EM1 ON EM1.`event_id` = EV.`id`
WHERE  (( 1299736800 - repeat_start) % repeat_interval = 0 )

Cukup gantikan dengan cap waktu unix (1299736800) untuk setiap tanggal di kalender Anda.

Perhatikan penggunaan modulo (tanda%). Simbol ini seperti pembagian reguler, tetapi mengembalikan '' sisa '' alih-alih hasil bagi, dan dengan demikian adalah 0 setiap kali tanggal saat ini adalah kelipatan dari repeat_interval dari repeat_start.

Perbandingan Kinerja

Ini jauh lebih cepat daripada jawaban berbasis-meta_keys yang disarankan sebelumnya, yaitu sebagai berikut:

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

Jika Anda menjalankan EXPLAIN kueri ini, Anda akan mencatat bahwa itu mengharuskan penggunaan buffer gabungan:

+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+
| 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 |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+--------------------------------+

Solusi dengan 1 bergabung di atas tidak memerlukan buffer seperti itu.


Pola "Kompleks"

Anda dapat menambahkan dukungan untuk jenis yang lebih kompleks untuk mendukung jenis aturan berulang ini:

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

atau

Event A repeats second Friday of the month starting on March 11, 2011

Tabel acara Anda dapat terlihat persis sama:

ID    NAME
1     Sample Event
2     Another Event

Kemudian untuk menambahkan dukungan untuk aturan-aturan kompleks ini tambahkan kolom events_metaseperti:

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    

Catatan bahwa Anda hanya perlu baik menentukan repeat_interval atau satu set repeat_year, repeat_month, repeat_day, repeat_week, dan repeat_weekdaydata.

Ini membuat pemilihan kedua jenis secara bersamaan sangat sederhana. Lewati saja setiap hari dan isi nilai yang benar, (1370563200 untuk 7 Juni 2013, lalu tahun, bulan, hari, nomor minggu, dan hari kerja sebagai berikut):

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
  )

Ini mengembalikan semua peristiwa yang berulang pada hari Jumat minggu ke-2, serta setiap peristiwa yang berulang setiap hari Jumat, sehingga mengembalikan kedua peristiwa ID 1 dan 2:

ID    NAME
1     Sample Event
2     Another Event

* Sidenote dalam SQL I di atas menggunakan indeks hari kerja default Tanggal PHP , jadi "5" untuk hari Jumat


Semoga ini bisa membantu orang lain sebanyak jawaban asli membantu saya!


6
Ini luar biasa, terima kasih! Apakah Anda tahu bagaimana Anda akan menyandikan "setiap 2 bulan pada hari Senin pertama" atau "setiap 3 bulan pada hari Senin pertama", dll?
Jordan Lev

6
Saya setuju ini luar biasa. Saya mengalami dilema yang sama seperti yang dilakukan Jordan Lev. Bidang repeat_interval tidak baik untuk mengulangi bulan karena beberapa bulan lebih lama dari yang lain. Juga, bagaimana Anda membatasi durasi acara berulang. Yaitu, setiap 2 bulan pada hari Senin pertama selama 8 bulan. Tabel tersebut harus memiliki semacam tanggal akhir.
Abinadi

11
Ini jawaban yang sangat bagus. Saya telah membatalkan repeat_interval dan menambahkan tanggal repeat_end, tetapi jawaban ini sangat membantu.
Iain Collins

3
Kiat: Untuk pola yang rumit, seseorang dapat menghilangkan repeat_intervalkolom dan mewakilinya di kolom berikutnya (yaitu repeat_year, dll.) Untuk baris pertama, situasi pengulangan setiap Senin setelah 20 Mei 2013, dapat diwakili dengan menempatkan 1 di repeat_weekdaydan *di kolom lainnya.
musubi

3
@OlivierMATROT @milos Idenya adalah mengatur bidang yang ingin Anda perbaiki secara eksplisit, dan sisanya ke wildcard *. Jadi untuk "setiap bulan pada tanggal 3" Anda baru saja menetapkan repeat_dayke 3, sisa repeatbidang ke * (biarkan repeat_intervalnol), dan atur repeat_start ke kode waktu unix untuk 3 Maret 2011 menjadi jangkar tanggal Anda.
ahoffner

28

Peningkatan: ganti cap waktu dengan tanggal

Sebagai tambahan kecil untuk jawaban yang diterima yang kemudian disempurnakan oleh ahoffner - dimungkinkan untuk menggunakan format tanggal daripada cap waktu. Keuntungannya adalah:

  1. tanggal yang dapat dibaca dalam database
  2. tidak ada masalah dengan tahun> 2038 dan cap waktu
  3. menghapus perlu berhati-hati dengan cap waktu yang didasarkan pada tanggal yang disesuaikan secara musiman yaitu di Inggris 28 Juni dimulai satu jam lebih awal dari 28 Desember sehingga memperoleh cap waktu dari tanggal dapat mematahkan algoritma rekursi.

untuk melakukan ini, ubah DB repeat_startagar disimpan sebagai tipe 'tanggal' dan repeat_intervalsekarang tahan beberapa hari daripada detik. yaitu 7 untuk pengulangan 7 hari.

ubah baris sql:

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

untuk:

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

yang lainnya tetap sama. Sederhana!


Jadi bagaimana jika saya ingin acara saya diulang dari tahun ke tahun? repeat_interval harus menyimpan 365 hari? Bagaimana jika mereka memiliki waktu 366 hari?
TGeorge

3
@ George02 jika acara tahunan, Anda meninggalkan repeat_interval NULL dan repeat_year adalah * kemudian tergantung pada apa yang kambuh Anda dapat mengatur repeat_month dan repeat_day misalnya 11 Maret atau repeat_month, repeat_week, dan repeat_weekday untuk menetapkan Selasa ke-2 pada bulan April.
jerrygarciuh

25

Saya akan mengikuti panduan ini: https://github.com/bmoeskau/Extensible/blob/master/recurrence-overview.md

Pastikan juga Anda menggunakan format iCal agar tidak menemukan kembali roda dan ingat Aturan # 0: JANGAN menyimpan contoh kejadian berulang sebagai baris di basis data Anda!


2
Bagaimana Anda memodelkan pengguna pelacakan yang telah menghadiri acara tertentu? Apakah masuk akal untuk melanggar Peraturan # 0 dalam kasus ini?
Danny Sullivan

2
@DannySullivan Dari atas kepala saya, saya akan memiliki entitas lain attendedEventdengan baseInstanceIddan instanceStartDate- Yaitu sebagai contoh acara dasar dari mana Anda membuat tampilan kalender aturan berulang dan menggunakan tanggal mulai untuk menentukan informasi pada contoh spesifik itu Lalu entitas ini juga bisa memiliki sesuatu seperti attendedListIdyang mengarah ke meja lain id,attendedUserId
Gal Bracha

@DannySullivan Saya tahu ini sudah lama sejak Anda bertanya. Tetapi di luar komentar sebelumnya Anda selalu dapat melakukan pencarian terbalik untuk melihat apakah pengguna itu adalah bagian dari pola perulangan acara. Itu akan memberi tahu Anda jika mereka setidaknya dijadwalkan untuk acara tersebut. Apakah mereka benar-benar hadir atau tidak adalah cerita yang berbeda yang lebih sesuai dengan komentar DannySullivan.
BRogers

24

Untuk Anda yang tertarik dengan ini, sekarang Anda bisa menyalin dan menempel untuk memulai dalam beberapa menit. Saya menerima saran dalam komentar sebaik mungkin. Beritahu saya jika saya kehilangan sesuatu.

"COMPLEX VERSION":

acara

+ ---------- + ---------------- +
| ID | NAME |
+ ---------- + ---------------- +
| 1 | Contoh acara 1 |
| 2 | Acara kedua |
| 3 | Acara ketiga |
+ ---------- + ---------------- +

events_meta

+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| ID | event_id | repeat_start | repeat_interval | repeat_year | repeat_month | repeat_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 |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +

Kode 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');

juga tersedia sebagai ekspor MySQL (untuk akses mudah)

Contoh kode 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 />";
    }
?>

Contoh kode 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
// ----------------------------------------------------------------------------------------------------
?>

Juga kode php tersedia di sini (untuk keterbacaan yang lebih baik):
index.php
dan
connect.php
Sekarang pengaturan ini akan membawa Anda beberapa menit. Bukan jam. :)


2
bagaimana saya bisa meminta untuk mendapatkan semua peristiwa berulang dalam rentang tanggal .. yaitu untuk mendapatkan semua peristiwa berulang antara 2014-10-01 hingga 2014-12-30. terima kasih untuk posting Anda
Well Wisher

@Wellwisher - ulangi ... sampai dan tabel sementara stackoverflow.com/questions/34407833/…
Brad Kent

1
@Alex Bagaimana saya bisa menghapus satu kejadian dari kejadian berulang.
Pugazhenthi

1
Saya tahu ini adalah utas lama tapi mengapa tipe varchar di repeat_ * kolom? Tidak bisakah Anda menggunakan bilangan bulat dan nilai negatif alih-alih '*'?
Olivier MATROT

1
Terima kasih untuk kodenya. Namun saya harus mengatakan bahwa implementasi db / query Anda sedikit mengganggu, dan sangat tidak efisien. Misalnya mengapa menggunakan varchar (255) untuk kolom sederhana seperti itu (seperti @OlivierMATROT disebutkan, Anda bisa menggunakan integer, dan bahkan jika tidak, mengapa 255?). Dan jika Anda mengulangi permintaan sebanyak 30 kali, mengapa tidak menggunakan pernyataan atau prosedur? Hanya mengatakan demi jika seseorang akan menerapkan ini.
Rony

15

Sementara solusi yang diusulkan bekerja, saya mencoba menerapkan dengan Kalender Lengkap dan akan membutuhkan lebih dari 90 panggilan basis data untuk setiap tampilan (karena memuat saat ini, sebelumnya, dan bulan depan), yang, saya tidak terlalu senang.

Saya menemukan perpustakaan rekursi https://github.com/tplaner/When di mana Anda hanya menyimpan aturan dalam database dan satu permintaan untuk menarik semua aturan yang relevan.

Semoga ini akan membantu orang lain, karena saya menghabiskan berjam-jam berusaha mencari solusi yang baik.

Sunting: Perpustakaan ini untuk PHP


Saya juga ingin menggunakan kalender penuh. Bagaimana Kapan perpustakaan dapat membantu saya? Bagaimana cara mengekstrak acara propoer?
piernik

@ piernik - Saya akan mengatur perpustakaan seperti dalam dokumentasi dan jika Anda mengalami masalah tertentu, buka pertanyaan baru tentang stackoverflow dengan kode yang Anda siapkan dan masalah yang Anda hadapi. Saya yakin jika Anda melakukan banyak upaya di beberapa anggota akan membantu Anda.
Tim Ramsey

Maksud saya menggunakan WhenAnda harus menyimpan semua tanggal reccuring dalam database atau mendapatkan semua event reccuring dan menghasilkan tanggal dalam php no dalam database. Apakah saya benar?
piernik

@piernik Anda akan menyimpan Tanggal awal dan aturan / s dalam database dan gunakan Whenuntuk menghasilkan semua tanggal - yang diisi dari tanggal / aturan tersimpan awal.
Tim Ramsey

Ini juga tidak baik - Anda tidak bisa dalam satu perintah mysql mendapatkan acara yang benar - Anda harus menggunakan PHP untuk itu. Terima kasih
piernik

14

Mengapa tidak menggunakan mekanisme yang mirip dengan pekerjaan cron Apache? http://en.wikipedia.org/wiki/Cron

Untuk penjadwalan kalender \ saya akan menggunakan nilai yang sedikit berbeda untuk "bit" untuk mengakomodasi acara pengulangan kalender standar - alih-alih [hari dalam seminggu (0 - 7), bulan (1 - 12), hari bulan (1 - 31), jam (0 - 23), min (0 - 59)]

- Saya akan menggunakan sesuatu seperti [Tahun (ulangi setiap N tahun), bulan (1 - 12), hari bulan (1 - 31), minggu bulan (1-5), hari minggu (0 - 7) ]

Semoga ini membantu.


6
Saya pikir itu terlalu banyak pilihan hari dalam seminggu. Baik 1-7 atau 0-6 tampaknya lebih akurat.
Abinadi

2
Ada baiknya menggunakan cron untuk menyimpan pengulangan. tapi masalahnya sangat sulit untuk dicari.
Stony

@Vladimir bagaimana Anda menyimpan, setiap dua hari Selasa (setiap dua minggu pada hari Selasa)
julestruong

@julestruong situs web ini sepertinya memiliki jawabannya: coderwall.com/p/yzzu5a/running-a-cron-job-every-other-week
Ashton Wiersdorf

cron memiliki ekspresifitas terbatas, karena tidak memiliki kewarganegaraan (hanya membandingkan tanggal / waktu hipotetis / saat ini dengan suatu pola), karena itu, cron tidak dapat mewakili pola bisnis / manusia umum tertentu seperti "setiap hari ketiga" atau "setiap 7 jam", yang membutuhkan mengingat kejadian terakhir. Ini tidak jelas; Anda mungkin berpikir bahwa Anda hanya mengatakan hari / 3 atau jam / 7 di crontab, tetapi kemudian pada akhir bulan / hari, Anda memiliki "sisa" hari / jam yang kurang dari 3 atau 7; dengan kemungkinan hasil bencana.
Jaime Guerrero

5

Saya mengembangkan bahasa pemrograman esoterik hanya untuk kasus ini. Bagian terbaik tentang itu adalah skema yang lebih sedikit dan platform mandiri. Anda hanya perlu menulis program pemilih, untuk jadwal Anda, sintaks yang dibatasi oleh seperangkat aturan yang dijelaskan di sini -

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

Aturannya dapat diperluas dan Anda dapat menambahkan segala jenis penyesuaian berdasarkan jenis logika pengulangan yang ingin Anda lakukan, tanpa khawatir tentang migrasi skema dll.

Ini adalah pendekatan yang sama sekali berbeda dan mungkin memiliki beberapa kelemahannya sendiri.


4

Kedengarannya sangat mirip peristiwa MySQL yang disimpan dalam tabel sistem. Anda dapat melihat struktur dan mencari tahu kolom mana yang tidak diperlukan:

   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

@Rogue Coder

Ini bagus!

Anda cukup menggunakan operasi modulo (MOD atau% di mysql) untuk membuat kode Anda sederhana di akhir:

Dari pada:

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

Melakukan:

$current_timestamp = 1299132000 ;

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

Untuk mengambil ini lebih jauh, seseorang dapat memasukkan peristiwa yang tidak terulang selamanya.

Sesuatu seperti "repeat_interval_1_end" untuk menunjukkan tanggal "repeat_interval_1" terakhir dapat ditambahkan. Namun ini, membuat kueri lebih rumit dan saya tidak bisa benar-benar mengetahui cara melakukan ini ...

Mungkin seseorang bisa membantu!


1

Dua contoh yang Anda berikan sangat sederhana; mereka dapat direpresentasikan sebagai interval sederhana (yang pertama adalah empat hari, yang kedua adalah 14 hari). Bagaimana Anda membuat model ini akan sepenuhnya bergantung pada kompleksitas pengulangan Anda. Jika yang Anda miliki di atas benar-benar sesederhana itu, maka simpan tanggal mulai dan jumlah hari dalam interval ulang.

Namun, jika Anda perlu mendukung hal-hal seperti

Event A berulang setiap bulan pada tanggal 3 bulan dimulai pada tanggal 3 Maret 2011

Atau

Acara A mengulangi Jumat kedua bulan itu dimulai pada 11 Maret 2011

Maka itu pola yang jauh lebih kompleks.


1
Saya membuat menambahkan aturan yang lebih kompleks yang baru saja Anda nyatakan di kemudian hari tetapi tidak untuk saat ini. Bagaimana saya memodelkan kueri SQL untuk mendapatkan acara pada katakan 7 Maret 2011 sehingga akan mendapatkan acara berulang saya?
Brandon Wamboldt
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.