कैलेंडर आवर्ती / दोहराए जाने वाले कार्यक्रम - सर्वश्रेष्ठ संग्रहण विधि


311

मैं एक कस्टम ईवेंट सिस्टम बना रहा हूं, और यदि आपके पास एक दोहराई जाने वाली घटना है जो इस तरह दिखती है:

इवेंट ए 3 मार्च 2011 को शुरू होने वाले हर 4 दिनों में दोहराता है

या

ईवेंट बी 1 मार्च 2011 को शुरू होने वाले प्रत्येक 2 सप्ताह में मंगलवार को दोहराता है

मैं इसे एक डेटाबेस में कैसे स्टोर कर सकता हूं जिससे यह देखने में सरल हो। अगर बड़ी संख्या में कार्यक्रम होते हैं, तो मुझे प्रदर्शन के मुद्दे नहीं चाहिए, और मुझे कैलेंडर को प्रस्तुत करते समय हर एक से गुजरना होगा।


क्या आप बता सकते हैं कि 1299132000 हार्डकोड क्यों है? अगर मुझे दी गई अंतिम तिथि के लिए पश्चात की तारीख और उपयोगकर्ता प्राप्त करने की आवश्यकता है तो यह क्या करेगा?
मुरली मुरुगेसन

@ मुरली ब्वॉय यह पुराना है, लेकिन मुझे पूरा यकीन है कि 1299132000 वर्तमान तारीख माना जाता है।
ब्रैंडन वम्बोल्ड 14

@BrandonWamboldt, मैंने SQL सर्वर के साथ आपके विचार की कोशिश की। stackoverflow.com/questions/20286332/display-next-event-date । मैं अगले सभी आइटम जैसे c # वर्जन
Billa

जवाबों:


211

"सरल" दोहराते हुए पैटर्न को संग्रहीत करना

मेरे PHP / MySQL आधारित कैलेंडर के लिए, मैं संभवतः के रूप में कुशलता से पुनरावृत्ति / घटना की जानकारी संग्रहीत करना चाहता था। मैं बड़ी संख्या में पंक्तियाँ नहीं रखना चाहता था, और मैं एक विशिष्ट तिथि पर होने वाली सभी घटनाओं को आसानी से देखना चाहता था।

विधि नीचे, हर जानकारी है कि इस तरह हर दिन नियमित अंतराल, पर होता दोहरा भंडारण में महान है n , दिन, हर सप्ताह, हर महीने हर साल, आदि आदि यह प्रत्येक मंगलवार और गुरुवार प्रकार पैटर्न के रूप में अच्छी तरह से शामिल हैं, क्योंकि वे जमा हो जाती है प्रत्येक सप्ताह के रूप में अलग-अलग मंगलवार से शुरू होता है और प्रत्येक सप्ताह गुरुवार से शुरू होता है।

यह मानते हुए कि मेरे पास दो टेबल हैं, एक को eventsइस तरह बुलाया जाता है:

ID    NAME
1     Sample Event
2     Another Event

और events_metaइस तरह की एक तालिका कहा जाता है:

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

रिपी_स्टार्ट के साथ एक यूनिक्स टाइमस्टैम्प के रूप में कोई समय नहीं होने के कारण, और अंतरालों के बीच सेकंड में एक राशि को दोहराएँ_पुनर्वल (432000 5 दिन है)।

repeat_interval_1 ID 1 के repeat_start के साथ जाता है। इसलिए यदि मेरे पास कोई ऐसी घटना है जो हर मंगलवार और हर गुरुवार को दोहराती है, तो rep_interval 604800 (7 दिन) होगा, और 2 repeat_starts और 2+_interval होंगे। तालिका इस तरह दिखाई देगी:

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

फिर, यदि आपके पास एक कैलेंडर है जो हर दिन से गुजरता है, तो उस दिन की घटनाओं को हथियाने के लिए, जिस दिन क्वेरी इस तरह दिखाई देगी:

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}वर्तमान तिथि के लिए यूनिक्स टाइमस्टैम्प के साथ प्रतिस्थापित करना (समय समय, इसलिए घंटे, मिनट और दूसरा मान 0 पर सेट किया जाएगा)।

उम्मीद है कि यह किसी और को भी मदद करेगा!


"कॉम्प्लेक्स" दोहराए जाने वाले पैटर्न को संग्रहीत करना

जटिल पैटर्न जैसे कि भंडारण के लिए यह विधि बेहतर है

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

या

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

मैं सबसे लचीलेपन के लिए उपरोक्त प्रणाली के साथ संयोजन करने की सलाह दूंगा। इसके लिए सारणी इस प्रकार होनी चाहिए:

ID    NAME
1     Sample Event
2     Another Event

और 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वर्तमान महीने के सप्ताह का प्रतिनिधित्व करता है, जो 1 और 5 संभावित के बीच हो सकता है। repeat_weekdayसप्ताह के दिन में, 1-7।

अब मान लें कि आप अपने कैलेंडर में एक महीने का दृश्य बनाने के लिए दिन / सप्ताह के माध्यम से लूप कर रहे हैं, तो आप इस तरह से एक प्रश्न लिख सकते हैं:

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

उपरोक्त विधि के साथ संयुक्त यह सबसे दोहराव / आवर्ती घटना पैटर्न को कवर करने के लिए जोड़ा जा सकता है। अगर मैंने कुछ भी याद किया है तो कृपया एक टिप्पणी छोड़ दें।


1
मैं आपके स्टोरिंग "सिंपल" रिपीटिंग पैटर्न की कोशिश कर रहा हूं। अगर मुझे इसे हर हफ्ते मंगलवार को दोहराने की आवश्यकता है, तो क्या मुझे दोहराने की तारीख को संशोधित करने या अंतिम तिथि के साथ एक नया रिकॉर्ड बनाने की आवश्यकता है। या फिर इसके लिए एक तरीका है कि हर हफ्ते पहले रिपीट_स्टार्ट के आधार पर दोहराएं ???
लू

1
आपका उत्तर एक महान मदद @roguecoder था, लेकिन यह बल्ले से काफी काम नहीं कर रहा था ... मुझे इस पोस्ट के बाद मेरा जवाब मिला: stackoverflow.com/questions/10545869/…
बेन सिनक्लेयर

1
में AND ( ( CASE ( 1299132000 - EM1.meta_value ) WHEN 0 THEN 1 ELSE ( 1299132000 - EM1.meta_value) END ) / EM2.meta_value ) = 1इस है / EM2.meta_valueगलत तरीके से रखा गया?
मुरली मुरुगेसन

1
यह एक बड़ी मदद है। आप इन्हें व्यक्तिगत रिकॉर्ड के रूप में एक्सेस करने का सुझाव कैसे देंगे, कहते हैं, यदि आप व्यक्तिगत घटनाओं पर टिप्पणी या चेक-इन करना चाहते थे?
जॉन्स

26
यह ध्यान देने योग्य है कि आपको 86400एक दिन में दोहराने अंतराल, यानी सेकंड के लिए हार्डकोड मूल्यों का उपयोग नहीं करना चाहिए , क्योंकि यह दिन के बचत समय में कारक नहीं है। यह मक्खी पर गतिशील रूप से इन बातों की गणना और बजाय स्टोर करने के लिए अधिक उपयुक्त है interval = dailyऔर interval_count = 1या interval = monthlyऔर interval_count = 1
कोरी बल्लू

185

जबकि वर्तमान में स्वीकृत उत्तर मेरे लिए बहुत बड़ी मदद थी, मैं कुछ उपयोगी संशोधनों को साझा करना चाहता था जो प्रश्नों को सरल बनाती हैं और प्रदर्शन को भी बढ़ाती हैं।


"सरल" घटनाओं को दोहराएं

नियमित अंतराल पर होने वाली घटनाओं को संभालने के लिए, जैसे:

Repeat every other day 

या

Repeat every week on Tuesday 

आपको दो तालिकाओं का निर्माण करना चाहिए, एक को eventsइस तरह बुलाया जाता है:

ID    NAME
1     Sample Event
2     Another Event

और 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

साथ repeat_startही समय (मई 20 वीं 2013 करने के लिए १३६९००८००० मेल खाती है), और के साथ एक यूनिक्स टाइमस्टैम्प तारीख जा रहा है repeat_intervalअंतराल के बीच की राशि सेकंड में (604,800 7 दिन है)।

कैलेंडर में प्रत्येक दिन लूपिंग करके आप इस सरल क्वेरी का उपयोग करके बार-बार होने वाली घटनाओं को प्राप्त कर सकते हैं:

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

अपने कैलेंडर में प्रत्येक तिथि के लिए यूनिक्स-टाइमस्टैम्प (1299736800) में स्थानापन्न करें।

मॉडुलो (% चिन्ह) के उपयोग पर ध्यान दें। यह प्रतीक नियमित विभाजन की तरह है, लेकिन भागफल के बजाय '' शेष '' लौटाता है, और ऐसा 0 होता है जब भी वर्तमान तिथि रिपीट_स्टार्ट से रिपीट_एंटरवल का एक सटीक एकाधिक होता है।

प्रदर्शन की तुलना

यह पहले से सुझाए गए "मेटा_कीज़" की तुलना में काफी तेज है, जो कि इस प्रकार है:

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

यदि आप इस क्वेरी को चलाते हैं, तो आप ध्यान देंगे कि इसके लिए एक जुड़ने वाले बफर के उपयोग की आवश्यकता है:

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

1 से ऊपर के समाधान के साथ इस तरह के बफर की आवश्यकता नहीं है।


"जटिल" पैटर्न

आप इन प्रकार के दोहराने के नियमों का समर्थन करने के लिए अधिक जटिल प्रकारों के लिए समर्थन जोड़ सकते हैं:

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

या

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

आपकी ईवेंट तालिका बिल्कुल समान दिख सकती है:

ID    NAME
1     Sample Event
2     Another Event

फिर इन जटिल नियमों के लिए समर्थन जोड़ने के लिए कॉलम को इस events_metaतरह जोड़ें :

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    

ध्यान दें कि आप बस या तो एक निर्दिष्ट करने की आवश्यकता repeat_interval या का एक सेट repeat_year, repeat_month, repeat_day, repeat_week, और repeat_weekdayडेटा।

इससे दोनों प्रकारों का चयन एक साथ बहुत सरल हो जाता है। बस प्रत्येक दिन के माध्यम से लूप करें और सही मान भरें, (7 जून 2013 के लिए 1370563200, और फिर वर्ष, माह, दिन, सप्ताह संख्या और सप्ताह का दिन निम्नानुसार है):

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
  )

यह 2 सप्ताह के शुक्रवार को दोहराने वाली सभी घटनाओं के साथ-साथ हर शुक्रवार को दोहराने वाली सभी घटनाओं को भी लौटाता है, इसलिए यह घटना 1 और 2 दोनों को लौटाता है:

ID    NAME
1     Sample Event
2     Another Event

* उपरोक्त एसक्यूएल में मैंने PHP दिनांक के डिफ़ॉल्ट कार्यदिवस सूचकांक का उपयोग किया , इसलिए शुक्रवार के लिए "5"


आशा है कि यह दूसरों की मदद करता है जितना कि मूल उत्तर ने मेरी मदद की!


6
यह आश्चर्यजनक है, धन्यवाद! क्या आपके पास कोई विचार है कि आप "हर 2 महीने पहले सोमवार को" या "हर 3 महीने पहले सोमवार को" इत्यादि को कैसे एनकोड करेंगे?
जॉर्डन लेव

6
मैं मानता हूं कि यह आश्चर्यजनक है। मैं उसी दुविधा में था जो जॉर्डन लेव ने किया था। बार-बार दोहराने वाले महीनों के लिए रिपीट_इंटरवाल क्षेत्र अच्छा नहीं है क्योंकि कुछ महीने दूसरों की तुलना में लंबे होते हैं। इसके अलावा, आप किसी आवर्ती घटना की अवधि को कैसे सीमित करते हैं। यानी, 8 महीने के लिए पहले सोमवार को हर 2 महीने। तालिका में किसी प्रकार की समाप्ति तिथि होनी चाहिए।
अबिनदी

11
यह एक उत्कृष्ट उत्तर है। मैंने repeat_interval को गिरा दिया है और repeat_end तिथि जोड़ दी है, लेकिन इस उत्तर ने बहुत मदद की।
इयान कॉलिंस

3
युक्ति: जटिल पैटर्न के लिए, कोई repeat_intervalस्तंभ को समाप्त कर सकता है और बाद के कॉलम (अर्थात repeat_year, आदि) में इसका प्रतिनिधित्व कर सकता है । पहली पंक्ति के लिए, 20 मई, 2013 के बाद प्रत्येक सोमवार को दोहराने की स्थिति, 1 को रखकर प्रदर्शित की जा सकती है। repeat_weekdayऔर *दूसरे कॉलम में।
मुसुबी

3
@OlivierMATROT @milos विचार उस क्षेत्र को सेट करना है जिसे आप स्पष्ट रूप से तय करना चाहते हैं, और बाकी को वाइल्डकार्ड पर *। तो "3 जी पर हर महीने" के लिए आप सिर्फ 3 पर सेट repeat_dayकरें, बाकी repeatक्षेत्रों को * ( repeat_intervalशून्य छोड़ दें ), और 3 मार्च 2011 को अपने एंकर की तारीख के लिए रिपिक्स_स्टार्ट को यूनिक्स टाइमपास पर सेट करें।

28

संवर्धन: तिथि के साथ टाइमस्टैम्प को बदलें

स्वीकृत उत्तर के लिए एक छोटी सी वृद्धि के रूप में जो बाद में अहोफ़नर द्वारा परिष्कृत किया गया था - टाइमलैम्प के बजाय दिनांक प्रारूप का उपयोग करना संभव है। लाभ हैं:

  1. डेटाबेस में पठनीय तिथियाँ
  2. 2038 और टाइमस्टैम्प के वर्षों के साथ कोई समस्या नहीं है
  3. हटाए जाने वाले टाइमस्टैम्प के साथ सावधानी बरतने की जरूरत है जो कि मौसम के अनुसार समायोजित तारीखों पर आधारित हैं अर्थात यूके में 28 जून को 28 दिसंबर की तुलना में एक घंटे पहले शुरू होता है ताकि किसी तारीख से टाइमस्टैम्प प्राप्त करना पुनरावृत्ति एल्गोरिदम को तोड़ सके।

ऐसा करने के लिए, DB repeat_startको 'तारीख' के रूप में संग्रहीत करने के लिए परिवर्तित करें और repeat_intervalअब सेकंड के बजाय दिनों को पकड़ें। यानी 7 दिनों के दोहराने के लिए 7।

sql लाइन बदलें:

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

सेवा:

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

बाकी सब कुछ वैसा ही रहता है। साधारण!


तो क्या होगा अगर मैं चाहता हूं कि मेरी घटना साल-दर-साल दोहराई जाए? रिपीट_डेनवल को 365 दिन स्टोर करना चाहिए? क्या होगा यदि उनके पास 366 दिन हैं?
TGeorge

3
@ George02 अगर घटना वार्षिक है तो आप repeat_interval छोड़ दें NULL और repeat_year * है, तो इस बात पर निर्भर करता है कि आप किस पुनरावृत्ति को दोहरा सकते हैं।
jerrygarciuh

25

मैं इस गाइड का पालन करूंगा: https://github.com/bmoeskau/Extensible/blob/master/recurrence-overview.md

यह भी सुनिश्चित करें कि आप iCal प्रारूप का उपयोग करें ताकि पहिया को फिर से मजबूत न करें और नियम # 0 को याद रखें: अपने डेटाबेस में पंक्तियों के रूप में व्यक्तिगत आवर्ती घटना उदाहरणों को संग्रहीत न करें!


2
आप उन ट्रैकिंग उपयोगकर्ताओं को कैसे मॉडल करेंगे, जिन्होंने एक विशिष्ट उदाहरण में भाग लिया है? क्या इस मामले में नियम # 0 से तोड़ना समझदारी है?
डैनी सुलिवन

2
@DannySullivan मेरे सिर के ऊपर से, मैं किसी अन्य संस्था के लिए होता है attendedEventके साथ baseInstanceIdऔर instanceStartDate- यही कारण है कि उदाहरण के लिए आधार घटना है जिसमें से आप आवर्ती नियम कैलेंडर दृश्य बनाया है और कि विशिष्ट उदाहरण फिर इस इकाई भी कर सकता है के बारे में जानकारी निर्दिष्ट करने के लिए आरंभ तिथि का उपयोग करें ऐसा कुछ है attendedListIdजिसके लिए एक और तालिका की ओर जाता है id,attendedUserId
Gal Bracha

@DannySullivan मुझे पता है कि आपके पूछने के बाद से कुछ समय हो गया है। लेकिन पिछली टिप्पणी के बाहर आप हमेशा यह देखने के लिए एक रिवर्स लुकअप कर सकते हैं कि क्या उपयोगकर्ता इवेंट पुनरावृत्ति पैटर्न का हिस्सा था। यह आपको बताएगा कि क्या वे कम से कम कार्यक्रम के लिए निर्धारित थे। चाहे वे वास्तव में उपस्थित थे या नहीं एक अलग कहानी है जो डैनीसुल्लीवन की टिप्पणी की तर्ज पर अधिक होगी।
22

24

आप सभी के लिए जो इस में रुचि रखते हैं, अब आप मिनटों में शुरू करने के लिए कॉपी और पेस्ट कर सकते हैं। मैंने टिप्पणियों में सलाह ली और साथ ही साथ मैं कर सका। अगर मुझे कुछ याद आ रहा है तो मुझे बताएं।

"कॉम्पलेक्स संस्करण":

आयोजन

+ ---------- + ---------------- +
| आईडी | NAME |
+ ---------- + ---------------- +
| 1 | नमूना घटना 1 |
| 2 | दूसरी घटना |
| 3 | तीसरी घटना |
+ ---------- + ---------------- +

events_meta

+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| आईडी | event_id | बार-बार_स्टार्ट | बार-बार_छंद | दोहरान_यार | बार-बार_मौत | दोहराना_दिवस | बार-बार_साफ | बार-बार_विवेक |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +
| 1 | 1 | 2014-07-04 | 7 | नल | नल | नल | नल | नल |
| 2 | 2 | 2014-06-26 | नल | 2014 | * * 2 | 5 |
| 3 | 3 | 2014-07-04 | नल | * * * * 5 |
+ ---- + ---------- + -------------- + ------------------ + ------------- + -------------- + ------------ + ------- ------ + ---------------- +

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

MySQL निर्यात (आसान पहुँच के लिए) के रूप में भी उपलब्ध है

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

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

इसके अलावा यहाँ php कोड उपलब्ध है (बेहतर पठनीयता के लिए):
index.php
और
connect.php
अब इसे सेट करने में आपको मिनट लगने चाहिए। घंटे नहीं। :)


2
मैं एक तिथि सीमा के भीतर सभी दोहराया घटनाओं को प्राप्त करने के लिए कैसे क्वेरी कर सकता हूं .. जो कि 2014-10-01 से 2014-12-30 के बीच सभी दोहराया घटनाओं को प्राप्त करना है। आपकी पोस्ट के लिए धन्यवाद
वेल विशर

@Wellwisher - दोहराएँ ... जब तक और अस्थायी तालिका stackoverflow.com/questions/34407833/…
ब्रैड केंट

1
@ एलेक्स मैं बार-बार होने वाली घटना से एक घटना को कैसे हटा सकता हूं।
पुगाजेंथी

1
मुझे पता है कि यह एक पुराना धागा है, लेकिन दोहराव_ * कॉलम पर varchar प्रकार क्यों? क्या आप '*' के बजाय पूर्णांक और ऋणात्मक मान का उपयोग नहीं कर सकते?
ओलिवियर MATROT

1
कोड के लिए धन्यवाद। हालाँकि मुझे यह टिप्पणी करनी चाहिए कि आपका db / क्वेरीज़ कार्यान्वयन थोड़ा परेशान करने वाला है, और बहुत अक्षम है। उदाहरण के लिए, ऐसे सरल स्तंभों के लिए varchar (255) का उपयोग क्यों करें (जैसा कि @OlivierMATROT में उल्लेख किया गया है, आप पूर्णांकों का उपयोग कर सकते हैं, और यदि नहीं, तो 255 भी क्यों?)। और यदि आप 30 बार क्वेरी दोहरा रहे हैं, तो बयान या प्रक्रिया का उपयोग क्यों नहीं करते हैं? यदि कोई इसे लागू करने वाला है तो उसके लिए सिर्फ यह कह रहा हूं।
रॉनी

15

प्रस्तावित समाधान काम करते समय, मैं पूर्ण कैलेंडर के साथ लागू करने की कोशिश कर रहा था और इसके लिए प्रत्येक दृश्य के लिए 90 से अधिक डेटाबेस कॉल की आवश्यकता होगी (क्योंकि यह वर्तमान, पिछले और अगले महीने लोड होता है), जो, मैं बहुत रोमांचित नहीं था।

मुझे एक पुनरावर्तन पुस्तकालय https://github.com/tplaner/When मिला जहां आप बस डेटाबेस में नियमों को संग्रहीत करते हैं और सभी प्रासंगिक नियमों को खींचने के लिए एक क्वेरी।

उम्मीद है कि यह किसी और की मदद करेगा, क्योंकि मैंने एक अच्छा समाधान खोजने की कोशिश में कई घंटे बिताए।

संपादित करें: यह लाइब्रेरी PHP के लिए है


मैं फुलकेन्डर का भी उपयोग करना चाहता हूं। कैसे पुस्तकालय मेरी मदद कर सकता है? प्रचारक घटनाओं को कैसे निकालें?
पियर्सन

@ पियरनिक - मैं लाइब्रेरी को डॉक्यूमेंटेशन के रूप में सेटअप करूँगा और यदि आप विशिष्ट मुद्दों पर चल रहे हैं तो स्टैकओवरफ़्लो पर एक नया प्रश्न खोलें, जिसमें आपके पास सेटअप और आपके पास मौजूद मुद्दे हैं। मुझे यकीन है कि अगर आप कुछ सदस्यों में इतना प्रयास करेंगे तो आपको मदद मिलेगी।
टिम रैमसे

मेरा मतलब है कि Whenआप का उपयोग डेटाबेस में सभी reccuring तारीखों को संग्रहीत करने के लिए या सभी reccuring घटनाओं को प्राप्त करने और डेटाबेस में php नहीं में तारीखें उत्पन्न करने के लिए है। क्या मैं सही हू?
piernik

@piernik आप डेटाबेस में आरंभिक दिनांक और नियम / नियम संग्रहीत करेंगे और Whenसभी तिथियों को उत्पन्न करने के लिए उपयोग करेंगे - जो प्रारंभिक संग्रहीत दिनांक / नियमों से आबाद हैं।
टिम

यह भी अच्छा नहीं है - आप एक भी mysql कमांड में सही घटनाओं को प्राप्त नहीं कर सकते - आपको उसके लिए PHP का उपयोग करना होगा। धन्यवाद वैसे भी
piernik

14

अपाचे क्रोन नौकरियों के समान एक तंत्र का उपयोग क्यों नहीं करें? http://en.wikipedia.org/wiki/Cron

कैलेंडर के शेड्यूलिंग के लिए, मैं "बिट्स" के लिए मानक कैलेंडर reoccurence घटनाओं को समायोजित करने के लिए "बिट्स" के लिए थोड़े अलग मूल्यों का उपयोग करूंगा - बजाय सप्ताह के दिन (0 - 7), महीने (1 - 12), महीने के दिन (1 - 31), घंटे (0 - 23), मिनट (0 - 59)]

- मैं [वर्ष (प्रत्येक एन वर्ष दोहराएं), महीना (1 - 12), महीने का दिन (1 - 31), सप्ताह का महीना (1-5), सप्ताह का दिन (0 - 7) जैसे कुछ का उपयोग करता हूं ]

उम्मीद है की यह मदद करेगा।


6
मुझे लगता है कि सप्ताह के विकल्पों के बहुत अधिक दिन हैं। या तो 1-7 या 0-6 अधिक सटीक लगता है।
अबिनदी

2
रिपीट स्टोर करने के लिए क्रोन का उपयोग करना अच्छा है। लेकिन समस्या यह है कि इसे देखना बहुत मुश्किल है।
स्टोनी

@ व्लादिमीर आप कैसे स्टोर करते हैं, हर दो tuesday (प्रत्येक दो सप्ताह tuesday पर)
julestruong

@julestruong इस वेबसाइट को ऐसा लगता है जैसे इसका उत्तर है: coderwall.com/p/yzzu5a/running-a-cron-job-every-other-week
Ashton Wiersdorf

क्रोन की सीमित अभिव्यक्ति है, क्योंकि यह स्टेटलेस है (केवल एक पैटर्न की वर्तमान / काल्पनिक तिथि / समय की तुलना), इस तरह, यह कुछ सामान्य व्यवसाय / मानव पैटर्न जैसे "हर तीसरे दिन" या "हर 7 घंटे" का प्रतिनिधित्व नहीं कर सकता है, जो अंतिम घटना को याद रखने की आवश्यकता है। यह स्पष्ट नहीं है; आप सोच सकते हैं कि आप सिर्फ कोंटैब में दिन / 3 या घंटे / 7 कहते हैं, लेकिन फिर महीने / दिन के अंत में, आपके पास "बचे हुए" दिन / घंटे हैं जो 3 या 7 से कम हैं; संभावित विनाशकारी परिणामों के साथ।
Jaime ग्युरेरो

5

मैंने इस मामले के लिए एक गूढ़ प्रोग्रामिंग भाषा विकसित की है। इसके बारे में सबसे अच्छी बात यह है कि यह स्कीमा कम और प्लेटफार्म स्वतंत्र है। आपको अपने चयन के लिए सिलेक्टर प्रोग्राम लिखना होगा, जिसका सिंटैक्स यहाँ वर्णित नियमों के समूह द्वारा विवश है -

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

नियम विस्तार योग्य हैं और स्कीमा माइग्रेशन आदि के बारे में चिंता किए बिना आप जिस तरह के दोहराव का प्रदर्शन करना चाहते हैं, उसके आधार पर आप किसी भी प्रकार का अनुकूलन जोड़ सकते हैं।

यह एक पूरी तरह से अलग दृष्टिकोण है और इसके अपने कुछ नुकसान हो सकते हैं।


4

MySQL ईवेंट्स को बहुत पसंद करता है जो सिस्टम टेबल में संग्रहीत हैं। आप संरचना को देख सकते हैं और यह पता लगा सकते हैं कि किन कॉलमों की जरूरत नहीं है:

   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:

4

आरआरयूएलई मानक इस आवश्यकता के लिए बनाया गया है अर्थात बचत और पुनरावृत्ति को समझना। Microsoft और Google दोनों अपने कैलेंडर ईवेंट में इसका उपयोग करते हैं। अधिक जानकारी के लिए कृपया इस दस्तावेज़ को देखें। https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html


3

@ राग कोडर

यह भी खूब रही!

आप अपने कोड को अंत में सरल बनाने के लिए बस modulo ऑपरेशन (MOD या mysql में%) का उपयोग कर सकते हैं:

के बजाय:

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

करना:

$current_timestamp = 1299132000 ;

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

इसे और आगे ले जाने के लिए, ऐसी घटनाओं को शामिल किया जा सकता है जो हमेशा के लिए पुनरावृत्ति न करें।

अंतिम "repeat_interval_1" की तारीख को निरूपित करने के लिए "repeat_interval_1_end" जैसा कुछ जोड़ा जा सकता है। यह हालांकि, क्वेरी को और अधिक जटिल बनाता है और मैं वास्तव में यह नहीं जान सकता कि यह कैसे करना है ...

शायद कोई मदद कर सके!


1

आपके द्वारा दिए गए दो उदाहरण बहुत सरल हैं; उन्हें एक साधारण अंतराल (पहले चार दिन, दूसरा 14 दिन) के रूप में दर्शाया जा सकता है। आप यह कैसे मॉडल करते हैं यह पूरी तरह से आपके पुनरावृत्ति की जटिलता पर निर्भर करेगा। यदि आपके पास जो कुछ है वह वास्तव में इतना सरल है, तो एक शुरुआत की तारीख और दोहराने के अंतराल में दिनों की संख्या को स्टोर करें।

अगर, हालांकि, आपको चीजों का समर्थन करने की आवश्यकता है

इवेंट ए हर महीने की 3 तारीख को दोहराता है जो 3 मार्च 2011 को शुरू होता है

या

इवेंट ए महीने का दूसरा शुक्रवार 11 मार्च, 2011 को शुरू होता है

फिर यह एक बहुत अधिक जटिल पैटर्न है।


1
मैंने अधिक जटिल नियम जोड़े जो आपने अभी बाद के बिंदु पर बताए हैं, लेकिन अभी के लिए नहीं। मैं 7 मार्च 2011 को घटनाओं को प्राप्त करने के लिए SQL क्वेरी को कैसे मॉडल करूंगा ताकि यह मेरी पुनरावृत्ति घटना हो?
ब्रैंडन वम्बोल्ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.