Takvim Yinelenen / Yinelenen Etkinlikler - En İyi Depolama Yöntemi


311

Ben özel bir olay sistemi inşa ediyorum ve böyle bir yinelenen olay varsa:

A olayı 3 Mart 2011'den itibaren her 4 günde bir tekrarlanır

veya

Etkinlik B 1 Mart 2011'den itibaren Salı günü her 2 haftada bir tekrarlanır

Bunu, veritabanını aramayı kolaylaştıracak şekilde nasıl saklayabilirim. Çok sayıda etkinlik varsa performans sorunları istemiyorum ve takvimi oluştururken her birini gözden geçirmek zorundayım.


Neden 1299132000 kodlanmış olduğunu açıklayabilir misiniz ? Belirtilen bitiş tarihi için gerçekleşme tarihlerini ve kullanıcıyı almam gerekirse ne yapar?
Murali Murugesan

@Murali Boy bu eski, ama eminim ki 1299132000'in şu anki tarih olması gerekiyor.
Brandon Wamboldt

@BrandonWamboldt, fikrinizi SQL Server ile denedim. stackoverflow.com/questions/20286332/display-next-event-date . Ben c # sürüm
Billa

Yanıtlar:


211

"Basit" Yinelenen Kalıpları Saklama

PHP / MySQL tabanlı takvimim için yinelenen / yinelenen etkinlik bilgilerini olabildiğince verimli bir şekilde saklamak istedim. Çok sayıda satıra sahip olmak istemedim ve belirli bir tarihte gerçekleşecek tüm etkinlikleri kolayca aramak istedim.

Aşağıdaki yöntem, her gün, her n gün, her hafta, her yıl her ay, vb. Gibi düzenli aralıklarla gerçekleşen tekrarlanan bilgileri depolamak için harikadır . Bu, saklanan her Salı ve Perşembe türü kalıpları da içerir. ayrı ayrı her hafta Salı gününden başlayarak her hafta Perşembe gününden başlayarak.

Biri şöyle denir iki tablo var varsayalım events:

ID    NAME
1     Sample Event
2     Another Event

Ve şöyle bir tablo events_meta:

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

Repeat_start, unix zaman damgası olarak zaman bulunmayan bir tarih ve repeat_interval aralıkları arasında saniye cinsinden bir miktar olduğunda (432000 5 gündür).

repeat_interval_1, ID 1'in repeat_start'ıyla gider. Bu nedenle, her Salı ve her Perşembe tekrarlayan bir etkinliğim varsa, repeat_interval 604800 (7 gün) olur ve 2 repeat_starts ve 2 repeat_intervals olur. Tablo şöyle görünecektir:

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

Ardından, her gün gerçekleşen bir takviminiz varsa ve bulunduğu güne ait etkinlikleri kaparsanız sorgu şu şekilde görünür:

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

{current_timestamp}Geçerli tarih için unix zaman damgası ile değiştirme (Saat eksi, böylece saat, dakika ve saniye değerleri 0 olarak ayarlanır).

Umarım bu başka birine de yardımcı olacaktır!


"Karmaşık" Yinelenen Kalıpları Saklama

Bu yöntem, aşağıdaki gibi karmaşık desenleri depolamak için daha uygundur

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

veya

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

En fazla esneklik için bunu yukarıdaki sistemle birleştirmenizi tavsiye ederim. Bunun için tablolar şöyle olmalıdır:

ID    NAME
1     Sample Event
2     Another Event

Ve şöyle bir tablo events_meta:

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, potansiyel olarak 1 ile 5 arasında olabilen bu ayın haftasını temsil eder. repeat_weekdayhaftanın günü, 1-7.

Takviminizde bir ay görünümü oluşturmak için günler / haftalar arasında geçiş yaptığınızı varsayarsak, şöyle bir sorgu oluşturabilirsiniz:

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

Yukarıdaki yöntemle birleştirilen bu yöntem, çoğu tekrarlayan / tekrarlayan olay biçimini kapsayacak şekilde birleştirilebilir. Bir şey kaçırmışsam lütfen yorum bırakın.


1
Ben "Basit" Yinelenen Desenleri Saklama deniyorum. Salı günü her hafta tekrarlamak gerekirse, repeat_start değiştirmek veya son tarih ile yeni bir kayıt oluşturmak gerekir. ya da ilk repeat_start dayalı her hafta tekrarlamak için bir yolu var mı ???
tuvalet

1
Cevabınız çok yardımcı oldu @roguecoder ama yarasa tamamen işe yaramadı ... Cevabımı bu yazıdan sonra buldum: stackoverflow.com/questions/10545869/…
Ben Sinclair

1
Gelen AND ( ( CASE ( 1299132000 - EM1.meta_value ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.meta_value) END ) / EM2.meta_value ) = 1bu edilmektedir / EM2.meta_valueyanlış yerleştirilmiş?
Murali Murugesan

1
Bu çok yardımcı. Tek tek etkinlikler hakkında yorum yapmak veya check-in yapmak istiyorsanız bunlara tek tek kayıt olarak erişmeyi nasıl önerirsiniz?
johnrees

26
Tekrar aralıkları için sabit kodlanmış değerleri kullanmamanız gerektiğini belirtmek gerekir, örneğin 86400bir gün içinde saniyeler, çünkü yaz saatini etkilemez. Bu şeyleri anında dinamik olarak hesaplamak ve bunun yerine saklamak interval = dailyve interval_count = 1veya interval = monthlyve daha uygun olur interval_count = 1.
Corey Ballou

185

Şu anda kabul edilen cevap bana çok yardımcı olsa da, sorguları basitleştiren ve performansı artıran bazı yararlı değişiklikleri paylaşmak istedim.


"Basit" Tekrar Olaylar

Düzenli aralıklarla tekrarlanan olayları işlemek, örneğin:

Repeat every other day 

veya

Repeat every week on Tuesday 

Biri şu şekilde adlandırılan iki tablo oluşturmalısınız events:

ID    NAME
1     Sample Event
2     Another Event

Ve şöyle bir tablo events_meta:

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

İle repeat_starthiçbir zaman (20 Mayıs 2.013-1369008000 tekabül) ve bir unix zaman tarih olmak repeat_intervalaralıklarla arasındaki saniyede bir miktarda (604800 7 gündür).

Takvimdeki her günü tekrarlayarak bu basit sorguyu kullanarak tekrarlanan etkinlikler alabilirsiniz:

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

Sadece takviminizdeki her tarih için unix-zaman damgasını (1299736800) kullanın.

Modulo (% işareti) kullanımını not edin. Bu sembol normal bölüm gibidir, ancak bölüm yerine '' kalan '' değerini döndürür ve geçerli tarih, repeat_start öğesinin repeat_interval değerinin tam katı olduğunda 0 olur.

Performans karşılaştırması

Bu, daha önce önerilen "meta_keys" tabanlı cevaptan önemli ölçüde daha hızlıdır;

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

Bu sorguyu EXPLAIN çalıştırırsanız, bir birleştirme arabelleği kullanılmasını gerektirdiğini göreceksiniz:

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

Yukarıdaki 1 birleştirmeye sahip çözelti böyle bir tampon gerektirmez.


"Karmaşık" Desenler

Bu tür tekrarlama kurallarını desteklemek için daha karmaşık türler için destek ekleyebilirsiniz:

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

veya

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

Etkinlikler tablonuz tamamen aynı görünebilir:

ID    NAME
1     Sample Event
2     Another Event

Sonra bu karmaşık kurallar için destek eklemek için aşağıdaki events_metagibi sütunlar ekleyin :

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    

Sadece bir belirtmeniz ya gerektiğini Not repeat_interval veya bir dizi repeat_year, repeat_month, repeat_day, repeat_week, ve repeat_weekdayveri.

Bu, her iki türün seçimini aynı anda çok basit hale getirir. Her bir güne göz atın ve doğru değerleri girin (7 Haziran 2013 için 1370563200 ve ardından yıl, ay, gün, hafta numarası ve hafta içi günleri aşağıdaki gibi):

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
  )

Bu, 2. haftanın Cuma günü yinelenen tüm etkinliklerin yanı sıra her Cuma yinelenen tüm etkinlikleri döndürür, böylece hem etkinlik kimliği 1 hem de 2'yi döndürür:

ID    NAME
1     Sample Event
2     Another Event

* Yukarıdaki SQL Sidenote PHP Tarihinin varsayılan hafta içi dizinleri kullandım, bu yüzden Cuma günü "5"


Umarım bu, orijinal cevap bana yardımcı olduğu kadar başkalarına da yardımcı olur!


6
Bu inanılmaz, teşekkürler! "İlk Pazartesi günü 2 ayda bir" veya "ilk Pazartesi günü 3 ayda bir" vb. Nasıl kodlayacağınız hakkında bir fikriniz var mı?
Jordan Lev

6
İnanıyorum ki bu inanılmaz. Yine de Jordan Lev ile aynı ikilemle karşılaştım. Repeat_interval alanı ayları tekrarlamak için iyi değildir çünkü bazı aylar diğerlerinden daha uzundur. Ayrıca, yinelenen bir etkinliğin süresini nasıl sınırlandırırsınız? Yani, her 2 ayda bir ilk Pazartesi günü 8 ay. Tablonun bir tür bitiş tarihi olmalıdır.
Abinadi

11
Bu mükemmel bir cevap. Repeat_interval'ı bıraktım ve repeat_end tarihini ekledim, ancak bu cevap çok yardımcı oldu.
Iain Collins

3
İpucu: Karmaşık desenler için, repeat_intervalsütun ortadan kaldırılabilir ve sonraki sütunlarda (ör. repeat_year, Vb.) Temsil edilebilir . İlk satır için, 20 Mayıs 2013 tarihinden sonra her Pazartesi tekrarlanması durumu, repeat_weekdayve *diğer sütunlarda bir.
musubi

3
@OlivierMATROT @milos Fikir düzeltilmesini istediğiniz alanı ve geri kalanını joker karakter olarak ayarlamaktır *. Yani " repeat_day3'te her ay" için sadece 3'e ayarlıyorsunuz , repeatalanların geri kalanı * ( repeat_intervalboş bırak ) olarak ayarlanmış ve tekrar tarihini 3 Mart 2011 için unix zaman koduna ayarlamışsınız.
ahoffner

28

Geliştirme: zaman damgasını tarihle değiştir

Daha sonra ahoffner tarafından rafine edilen kabul edilen cevaba küçük bir iyileştirme olarak - zaman damgası yerine bir tarih formatı kullanmak mümkündür. Avantajları:

  1. veritabanında okunabilir tarihler
  2. 2038 ve zaman damgası ile ilgili sorun yok
  3. kaldırımların mevsimsellikten arındırılmış tarihlere dayanan zaman damgalarına dikkat etmesi gerekir; örneğin, İngiltere'de 28 Haziran 28 Aralık'tan bir saat önce başlar, bu nedenle bir tarihten bir zaman damgası türetmek özyineleme algoritmasını bozabilir.

bunu yapmak için, repeat_start'tarih' türü olarak saklanacak DB'yi değiştirin ve repeat_intervalşimdi saniye yerine gün tutun. yani 7 günlük bir tekrar için 7.

sql satırını değiştirin:

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

için:

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

diğer her şey aynı kalır. Simples!


Peki ya olayımın her yıl tekrarlanmasını istersem? repeat_interval 365 gün saklamalı mı? Ya 366 gün geçirirlerse?
TGeorge

3
@ George02 etkinlik yıllık ise repeat_interval NULL ve repeat_year * olarak ayrılırsanız, yinelenme durumuna bağlı olarak repeat_month ve repeat_day öğesini örneğin 11 Mart veya repeat_month, repeat_week ve repeat_weekday öğelerini 2 Mart Salı günü Nisan'a ayarlamak üzere ayarlayabilirsiniz.
jerrygarciuh

25

Bu kılavuzu takip ederim: https://github.com/bmoeskau/Extensible/blob/master/recurrence-overview.md

Ayrıca iCal formatını kullandığınızdan emin olun, böylece tekerleği yeniden icat etmeyin ve Kural # 0: Yinelenen olay örneklerini veritabanınızda satır olarak saklamayın!


2
Belirli bir örneğe katılan izleme kullanıcılarını nasıl modellersiniz? Bu durumda Kural # 0'dan kopmak mantıklı mı?
Danny Sullivan

2
@DannySullivan Başımın üstünden, ve attendedEventile başka bir varlığım olurdu - Bu, örneğin, yinelenen kurallar takvim görünümünü oluşturduğunuz ve o belirli örnekle ilgili bilgileri belirtmek için başlangıç ​​tarihini kullandığınız temel olaydır. başka bir tabloya yol açan gibi bir şey var ,baseInstanceIdinstanceStartDateattendedListIdidattendedUserId
Gal Bracha

@DannySullivan Sizden bu yana biraz zaman geçtiğini biliyorum. Ancak önceki yorumun dışında, kullanıcının etkinlik yineleme modelinin bir parçası olup olmadığını görmek için her zaman geriye doğru arama yapabilirsiniz. Bu, en azından etkinlik için planlanmış olup olmadıklarını size söyleyecektir. Aslında katılıp katılmadıkları, DannySullivan'ın yorumunda daha fazla yer alacak farklı bir hikaye.
BRogers

24

Bununla ilgilenen herkes için, şimdi birkaç dakika içinde başlamak için kopyalayıp yapıştırabilirsiniz. Yorumlarda olabildiğince tavsiye aldım. Bir şey eksik olup olmadığımı bana bildirin.

"KOMPLEKS VERSİYON":

Etkinlikler

+ ---------- + ---------------- +
| ID | ADI |
+ ---------- + ---------------- +
| 1 | Örnek olay 1 |
| 2 | İkinci olay |
| 3 | Üçüncü etkinlik |
+ ---------- + ---------------- +

events_meta

+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| ID | event_id | repeat_start | tekrar_sayısı | tekrar_yıl | tekrar_ay | tekrar_gün | tekrarla_hafta | 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 |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +

SQL kodu:

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

MySQL dışa aktarma olarak da mevcuttur (kolay erişim için)

PHP örnek kod dizini.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 />";
    }
?>

PHP örnek kodu 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
// ----------------------------------------------------------------------------------------------------
?>

Ayrıca php kodu burada mevcuttur (daha iyi okunabilirlik için):
index.php
ve
connect.php
Şimdi bu ayarın yapılması birkaç dakika sürecektir. Saat değil. :)


2
bir tarih aralığında tüm tekrarlanan olayları almak için nasıl sorgulayabilirim .. bu 2014-10-01 ila 2014-12-30 arasındaki tüm tekrarlanan olayları almak için. gönderi için teşekkürler
Peki Wisher

@Wellwisher - tekrarlayın ... kadar ve geçici masa stackoverflow.com/questions/34407833/…
Brad Kent

1
@Alex Tekrarlanan olaydan bir örnek oluşumunu nasıl silebilirim.
Pugazhenthi

1
Bu eski bir iş parçacığı olduğunu biliyorum ama neden repeat_ * sütunlarında varchar türü? '*' Yerine bir tam sayı ve negatif bir değer kullanamaz mısınız?
Olivier MATROT

1
Kod için teşekkürler. Ancak db / sorguları uygulama biraz rahatsız edici ve çok verimsiz olduğunu belirtmek gerekir. Örneğin, neden bu kadar basit sütunlar için varchar (255) kullanıyorsunuz (@OlivierMATROT'un belirttiği gibi, tamsayılar kullanabilirsiniz, olmasa bile neden 255?). Sorguyu 30 kez tekrarlıyorsanız, neden ifadeler veya prosedürler kullanmıyorsunuz? Sadece biri bunu uygulamak üzereyken.
Rony

15

Önerilen çözümler çalışırken, Tam Takvim ile uygulamaya çalışıyordum ve her görünüm için (mevcut, önceki ve gelecek ay yüklendiği için) 90'dan fazla veritabanı çağrısı gerektiriyordu, ki bu da çok heyecanlanmadı.

Bir özyineleme kütüphanesi buldum https://github.com/tplaner/İlgili kuralların tümünü çekmek için kuralları veritabanında ve bir sorguda sakladığınız zaman.

Umarım bu başka birine yardımcı olur, çünkü iyi bir çözüm bulmaya saatlerce harcadım.

Düzenleme: Bu Kütüphane PHP içindir


Ayrıca fullcalendar kullanmak istiyorum. Kütüphane ne zaman bana yardımcı olabilir? Propoer olayları nasıl ayıklanır?
piernik

@piernik - Kütüphaneyi dokümantasyondaki gibi kurarım ve belirli sorunlarla karşılaşıyorsanız, stackoverflow ile ilgili kodunuz ve yaşadığınız sorunlarla ilgili yeni bir soru açın. Eminim bazı üyelere bu kadar çaba sarfederseniz, size yardımcı olacaktır.
Tim Ramsey

Yani kullanarak Whentüm yinelenen tarihleri ​​veritabanında saklamak veya tüm yinelenen olayları almak ve veritabanında php no tarihleri ​​oluşturmak zorunda. Haklı mıyım?
piernik

@piernik İlk Tarihi ve kuralı / kuralları veritabanında Whensaklar ve ilk saklanan tarih / kurallardan doldurulmuş tüm tarihleri ​​oluşturmak için kullanırsınız .
Tim Ramsey

İyi de değil - Tek bir mysql komutanı doğru olayları almak olamaz - Bunun için PHP kullanmak zorunda. Neyse teşekkürler
piernik

14

Neden Apache cron işlerine benzer bir mekanizma kullanmıyorsunuz? http://en.wikipedia.org/wiki/Cron

Takvim \ zamanlama için, standart takvim yeniden etkinlik etkinliklerini barındırmak için "bit" için biraz farklı değerler kullanırdım - [haftanın günü (0-7), ay (1-12), ayın günü (1-31), saat (0-23), min (0-59)]

- [Yıl (her N yılda bir tekrarla), ay (1-12), ayın günü (1-31), ayın haftası (1-5), haftanın günü (0-7) gibi bir şey kullanırdım ]

Bu yardımcı olur umarım.


6
Bence bu haftanın günü seçenekleri çok fazla. 1-7 veya 0-6 daha doğru görünüyor.
Abinadi

2
Tekrarlamayı saklamak için cron kullanmak iyidir. ama sorun şu ki, arama yapmak çok zor.
Taşlı

@Vladimir nasıl saklıyorsun, her iki salı (salı
günleri

@julestruong bu web sitesi cevabı var gibi görünüyor: coderwall.com/p/yzzu5a/running-a-cron-job-every-other-week
Ashton Wiersdorf

cron sınırlı bir ifadeye sahiptir, çünkü vatansızdır (sadece mevcut / varsayımsal tarih / saati bir modelle karşılaştırır), bu nedenle, "her üç günde bir" veya "7 saatte bir" gibi belirli ortak iş / insan modellerini temsil edemez. son olayı hatırlamayı gerektirir. Bu belli değil; crontab'da sadece gün / 3 veya saat / 7 dediğinizi düşünebilirsiniz, ancak ayın / günün sonunda, 3 veya 7'den daha az "artık" gün / saatiniz vardır; olası felaket sonuçları ile.
Jaime Guerrero

5

Bu durum için ezoterik bir programlama dili geliştirdim. Bununla ilgili en iyi bölüm, şemadan daha az ve platformdan bağımsız olmasıdır. Programınız için sözdizimi burada açıklanan kurallar dizisi ile kısıtlanan bir seçici program yazmanız yeterlidir -

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

Kurallar genişletilebilir ve şema taşıma işlemleri hakkında endişelenmeden gerçekleştirmek istediğiniz tekrarlama mantığına göre her türlü özelleştirmeyi ekleyebilirsiniz.

Bu tamamen farklı bir yaklaşımdır ve kendi dezavantajları olabilir.


4

Sistem tablolarında depolanan MySQL olaylarına çok benziyor. Yapıya bakabilir ve hangi sütunlara gerek olmadığını anlayabilirsiniz:

   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

@ErkekTerimleri

Bu harika!

Sonunda kodunuzu basitleştirmek için modulo işlemini (mysql'de MOD veya% olarak) kullanabilirsiniz:

Onun yerine:

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

Yapmak:

$current_timestamp = 1299132000 ;

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

Bunu daha da ileri götürmek için, sonsuza dek tekrarlanmayan olaylar da dahil edilebilir.

Son "repeat_interval_1" tarihini belirtmek için "repeat_interval_1_end" gibi bir şey eklenebilir. Bu ancak, sorguyu daha karmaşık hale getirir ve gerçekten nasıl yapılacağını anlayamıyorum ...

Belki birisi yardım edebilir!


1

Verdiğiniz iki örnek çok basit; basit bir aralık olarak temsil edilebilirler (birincisi dört gündür, ikincisi 14 gündür). Bunu nasıl modelleyeceğiniz, tamamen tekrarlarınızın karmaşıklığına bağlı olacaktır. Yukarıdakiler gerçekten bu kadar basitse, bir başlangıç ​​tarihini ve gün sayısını tekrar aralığında saklayın.

Ancak, aşağıdaki gibi şeyleri desteklemeniz gerekiyorsa

A olayı her ay 3 Mart 2011'de başlayan ayın 3'ünde tekrarlanır.

Veya

Etkinlik A, 11 Mart 2011'den itibaren ayın ikinci Cuma gününü tekrar eder

O zaman bu çok daha karmaşık bir model.


1
Daha sonra belirttiğiniz daha karmaşık kuralları ekledim ama şimdilik değil. Yinelenen olayı alacak şekilde 7 Mart 2011'de olayları almak için SQL sorgusunu nasıl modelleyebilirim?
Brandon Wamboldt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.