33

私は Mysql ベースのカレンダー システムを作成します。このシステムでは、毎週月曜日を永遠に繰り返すパターンを使用できます。また、静的/1 回限りのイベントもカバーする必要があります。私が疑問に思っているのは、どのソリューションを使用するのが最も論理的 (かつ最適) かということです。4 つの方法がありますが、その中からどれを選択するか迷っています。

方法 1

fromパラメータとを受け入れる関数を作成しますto。この関数は、 を介して既存の静的スケジュールをインポートする一時テーブル table を作成しますINSERT ... SELECTfromその後、パターン テーブルを読み取り、およびに基づいて peroid を介して一時テーブルにデータを入力しますto

このソリューションは、クエリがデータを取得するのがより簡単になり、ロードしている月に応じてテーブルを再作成できるため、無限に機能するという観点からは素晴らしいようです。私が興味を持っているのは、これが遅い方法であるかどうかです。

方法 2

サブクエリとJOIN静的カレンダーを使用して、指定されたパターンを作成して結合します。

クエリがはるかに大きくなり、おそらくまったくうまくいかないので、これはかなり面倒に思えます(?)。

方法 3

基本的にINSERTは、1 年先のパターンです。次に、cronジョブが再投入され、常に1年先になると思います。

これは簡単な方法ですが、不要なデータが大量に保存されているように感じられ、私が求めている無限にはなりません。

方法 #4 (Veger の提案)

私の理解が正しければ、このメソッドは別のクエリからパターンを取得し、実行時にイベントを作成します。いくつかの行を作成する単純なパターンを考えるという点で、方法#1に関する私の考えに似ています。

ただし、これがMysqlの外部で実装される場合、私が求めているいくつかのデータベース機能が失われます。


皆さんが私の状況を理解してくれることを願っています.

個人的には方法 1 が一番気に入っていますが、呼び出しのたびにカレンダー テーブルを再作成するのが遅いかどうかに興味があります。

4

8 に答える 8

14

私は以前、この種のカレンダーを作成したことがあります。これを行う最善の方法は、cron がスケジュールされている方法でアプローチすることであることがわかりました。したがって、データベースで、分、時間、日、月、および曜日のフィールドを作成します。

6 月と 8 月の毎週金曜日の午後 10 時のイベントの場合、エントリは次のようになります。

Minute  Hour  DayOfMonth  Month  DayOfWeek
 0       22     *          6,8       5

次に、この情報を無視して開始日と期間のみを使用する 1 回限りのイベントとしてフラグを立てるフィールドを作成できます。最終的にその終了を繰り返すイベント (3 か月間、毎週末など) の場合は、終了日フィールドを追加するだけです。

これにより、簡単に選択して戻すことができ、保存する必要があるデータの量を減らすことができます。クエリも簡素化されます。

一時テーブルを作成する必要はないと思います。関連するイベントを選択し直すには、カレンダー ビューでそれらを選択します。カレンダー ビューが月単位の場合、選択内容は次のようになります。

SELECT Events.* 
FROM Events 
WHERE (Month LIKE '%,'.$current_month.',%' OR Month = '*') 
    AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'" 
    AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"

明らかに、これは準備されたステートメントにある必要があります。また、カンマで区切られた月のリストの最初と最後の値の前後にカンマがあることも前提としています (例: ,2,4,6,)。Month必要に応じて、テーブルと 2 つの間の結合テーブルを作成することもできます。残りは、カレンダーをレンダリングするときに php で解析できます。

カレンダーの週単位のビューを表示する場合、次の方法で選択できます。

SELECT Events.* 
FROM Events 
WHERE (DayOfMonth IN ('.implode(',', $days_this_week).','*') 
    AND (Month LIKE '%,'.$current_month.',%' OR Month = '*')) 
    AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'" 
    AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"

私はこれらのクエリをテストしていないので、混乱した括弧か何かがあるかもしれません. しかし、それは一般的な考え方でしょう。

したがって、表示している日ごとに選択を実行するか、ビューのすべて (月、週など) を選択し直して、各日のイベントをループすることができます。

于 2012-06-13T10:20:55.893 に答える
9

私はVegerのソリューションが一番好きです..複数の行を入力する代わりに、パターンを入力するだけです。私はフォーマットを提案しcrontabます..とにかくうまく機能します。

特定の顧客がカレンダーをロードし、パターンに基づいてイベントを入力するときに、すべてのパターンを照会できます。1 人のユーザーに対して何千ものパターンがある場合を除き、これはそれほど遅くはありません。また、多数の行イベントを長期間保存するよりも高速である必要があります。一度にすべてのパターンを選択し、いくつかの前処理を行う必要がありますが、もう一度言いますが、ユーザーごとにいくつのパターンが予想されるでしょうか? 1000程度でもかなり速いはずです。

于 2012-06-10T22:32:25.090 に答える
4

私はまだ GW Basic でプログラミングをしていたので、この考えを持っていました ;-) しかし、当時、私はオプション #3 を取り、それで終わりでした。それと他の回答のいくつかを振り返ってみると、これが私の現在の解決策になります。

テーブル構造

start (datetime)
stop (datetime, nullable)
interval_unit ([hour, day, week, month, year?])
interval_every (1 = every <unit>, 2 every two <units>, etc.)
type ([positive (default), negative]) - will explain later

オプションのフィールド:

title
duration

このtypeフィールドは、イベントの処理方法を決定します。

  1. ポジティブ; 通常の扱い、それはカレンダーに表示されます
  2. ネガティブ; このイベントは別のイベントをキャンセルします (例: 毎週月曜日ですが、14 日は除きます)

ヘルパー クエリ

このクエリは、表示するイベントを絞り込みます。

SELECT * FROM `events`
WHERE `start` >= :start AND (`stop` IS NULL OR `stop` < :stop)

日付のみ (時間コンポーネントなし) で範囲をクエリすると仮定すると、 の値は:stop範囲より 1 日早くなるはずです。

次に、処理したいさまざまなイベントについて説明します。

単一のイベント

start = '2012-06-15 09:00:00'
stop = '2012-06-15 09:00:00'
type = 'positive'

イベントは 2012 年 6 月 15 日の午前 9 時に 1 回発生します

境界のある繰り返しイベント

start = '2012-06-15 05:00:00'
interval_unit = 'day'
interval_every = 1
stop = '2012-06-22 05:00:00'
type = 'positive'

イベントは、2012 年 6 月 15 日から毎日午前 5 時に発生します。最後のイベントは22日です

無制限の繰り返しイベント

start = '2012-06-15 13:00:00'
interval_unit = 'week'
interval_every = 2
stop = null
type = 'positive'

イベントは 2012 年 6 月 15 日から 2 週間ごとに午後 1 時に発生します

例外を伴う繰り返しイベント

start = '2012-06-15 16:00:00'
interval_unit = 'week'
interval_every = 1
type = 'positive'
stop = null

start = '2012-06-22 16:00:00'
type = 'negative'
stop = '2012-06-22 16:00:00'

イベントは、2012 年 6 月 22 日から毎週午後 4 時に発生します。22日じゃないけど

于 2012-06-14T06:56:38.597 に答える
0

最適なソリューションは、標準への準拠 (RFC5545) を優先するか、MySQL 内だけで作業するかによって異なります。

繰り返しルールエンジンが必要とする柔軟性に依存します。単純なルール (毎月 1 日または毎年 1 月など) が必要な場合は、上記のソリューションについて詳しく説明します。

ただし、アプリケーションで、より複雑なルールを含む既存の標準 (RFC5545) との互換性を提供する必要がある場合は、カレンダー アプリを作成するときにこの SO 投稿を確認する必要があります。データベースに日付または繰り返しルールを保存する必要がありますか?

于 2012-12-26T19:59:43.077 に答える
0

たぶん、 MySQL Eventsからいくつかの素晴らしいアイデアをチェックしてください

その他:
http://phpmaster.com/working-with-mysql-events/?utm_source=rss&utm_medium=rss&utm_campaign=phpmaster-working-with-mysql-events

http://dev.mysql.com/doc/refman/5.5/en/create-event.html

于 2012-06-17T13:56:50.427 に答える
0

この行の周りに何かをお勧めします: 繰り返しイベントと静的イベントの 2 つの異なるタイプが明確にあり、タイプに応じて異なる属性を持つため、イベント テーブルを 2 つに分割します。

次に、特定のイベント ルックアップに対して、各イベント タイプに対して 1 つずつ、合計 2 つのクエリを実行します。静的イベント テーブルの場合、(少なくとも) 1 つの datetime フィールドが必要になるため、特定の月のルックアップでは条件 ( event_date > FirstDayOfTheMonth および event_date < LastDayOfTheMonth ) でそのフィールドを使用するだけです。週次/年次ビューの同じロジック。

この結果セットは、定期的なイベント テーブルからの 2 番目の結果セットと結合されます。可能性のある属性は、2 つの主要な変数として曜日/月日を使用する crontab エントリに似ている可能性があります。月単位で見る場合は、

select * from recurring_events where DayOfWeek in (1,2,3,4,5,6,7) or (DayOfMonth > 0 and DayOfMonth < @NumberOfDaysInThisMonth )

週次/年次ビューの場合も同様です。これをさらに簡単にインターフェース化するには、「日付 A と日付 B の間にある曜日」を決定するためのすべてのロジックを備えたストアド プロシージャを使用します。

両方の結果セットを取得したら、それらをクライアントでまとめて表示することができます。これの利点は、「モック/空のレコード」や事前に入力する非同期cronジョブが不要になることです。クエリはオンザフライで簡単に実行でき、パフォーマンスが実際に低下する場合は、特にこのシステムの場合、キャッシュレイヤーを追加します本質的に、キャッシュは完全に理にかなっています。

于 2012-06-16T12:56:53.757 に答える
0

ここで説明したとおりにします。無限のカレンダーが作成されます。

PHP/MySQL: データベースで繰り返しイベントをモデル化するが、日付範囲をクエリする

欠点は、クエリ中に計算が行われることです。高性能の Web サイトが必要な場合は、データのプリロードが最適です。1 つのイベントの値を簡単に変更できるようにするために、カレンダーのすべてのイベントを事前に読み込む必要さえありません。しかし、今から ... までのすべての日付を保存するのが賢明でしょう。

キャッシュされた値を使用すると、無限ではなくなりますが、速度は向上します。

簡単にアクセスできる awner のコピー:

col を 1 つだけ呼び出して集計テーブルを作成し、idそのテーブルに 0 から 500 までの数字を入力します。これを使用して、while ループを使用する代わりに簡単に選択を行うことができます。

Id
-------------------------------------
0
1
2
etc...

次に、イベントをテーブルに保存しName as varcharstartdate as datetimerepeats as int

Name    | StartDate            |   Repeats
-------------------------------------
Meeting | 2012-12-10 00:00:00  |   7
Lunch   | 2012-12-10 00:00:00  |   1

これで、集計テーブルを使用して、次を使用して 2 つの日付の間のすべての日付を選択できます。

SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
FROM `tally`
WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
ORDER BY Id ASC


ShowDate
-------------------------------------
2012-12-09 00:00:00
2012-12-10 00:00:00
2012-12-11 00:00:00
2012-12-12 00:00:00
2012-12-13 00:00:00
2012-12-14 00:00:00
2012-12-15 00:00:00
2012-12-16 00:00:00
2012-12-17 00:00:00
2012-12-18 00:00:00
2012-12-19 00:00:00
2012-12-20 00:00:00

次に、これをイベント テーブルに結合して、開始日と表示日の差を計算します。この結果をrepeats列で割り、余りが の場合、0一致しています。

すべてを組み合わせると、次のようになります。

SELECT E.Id, E.Name, E.StartDate, E.Repeats, A.ShowDate, DATEDIFF(E.StartDate, A.ShowDate) AS diff
FROM events AS E, (
    SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
    FROM `tally`
    WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
    ORDER BY Id ASC
) a
WHERE MOD(DATEDIFF(E.StartDate, A.ShowDate), E.Repeats)=0
AND A.ShowDate>=E.StartDate

その結果、

Id  | Name       |StartDate             | Repeats   | ShowDate              | diff
---------------------------------------------------------------------------------
1   | Meeting    | 2012-12-10 00:00:00  | 7         | 2012-12-10 00:00:00   | 0
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-10 00:00:00   | 0
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-11 00:00:00   | -1
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-12 00:00:00   | -2
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-13 00:00:00   | -3
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-14 00:00:00   | -4
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-15 00:00:00   | -5
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-16 00:00:00   | -6
1   | Meeting    | 2012-12-10 00:00:00  | 7         | 2012-12-17 00:00:00   | -7
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-17 00:00:00   | -7
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-18 00:00:00   | -8
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-19 00:00:00   | -9
2   | Lunch      | 2012-12-10 00:00:00  | 1         | 2012-12-20 00:00:00   | -10

これで、速度を上げることができます (そしてそうすべきです!)。たとえば、テーブルに日付を直接格納することで、dateadd で集計テーブルを使用する代わりに、すべての日付を直接選択できます。キャッシュでき、再計算する必要がないものはすべて優れています。

于 2012-12-27T11:35:34.483 に答える
0

私は実際にこれに似たものを探しており、これまでの私のソリューション (まだ構造化やコード化を開始していない紙の上) は 2 つのテーブルに格納されています。

  1. 「イベント」は、最初に発生した日付、タイトル、説明 (および自動インクリメント ID) を取得します。

  2. 「events_recursion」テーブルは、前のテーブル (たとえば、event_id フィールドを使用) への相互参照を行い、次の 2 つの方法で機能します。

    2.A: すべての発生を日付別に保存します (つまり、発生ごとに 1 つのエントリを保存するため、「今月の毎週金曜日」を保存する場合は 4、「2012 年の毎月 1 日」を保存する場合は 12)。

    2.B: または、フィールド内の最初のイベントの日付 + 次のような別のフィールド内の最後の発生 (または再帰の終わり) の日付から間隔を保存します (秒単位で保存します)。

ID: 2 EVENT_ID: 1 INTERVAL: 604800 (間違っていなければ 1 週間) END: 1356912000 (今年の終わりのはず)

次に、スケジュールを表示する php を開くと、その月にまだアクティブなイベントが 2 つのテーブル間のジョイントでチェックされます。

すべてを 1 つのテーブルに保存する代わりに、相互参照された 2 つのテーブルを使用する理由は、私のプロジェクトでは、「毎週金曜日と毎月第 3 月曜日」などの非常にクレイジーなイベントが発生するという事実に由来します (この場合は、イベント テーブルに 1 つのエントリと 2 番目のテーブルに同じ "event_id" フィールドを持つ 2 つのエントリ ところで、私のプロジェクトは音楽教師向けであり、一度に 3 か月または 6 か月ごとに決定された厳密なスケジュールで小さな仕事があり、本当に混乱しています)。

しかし、私が言ったように、私はまだ始めていないので、あなたの解決策を見るのを楽しみにしています.

PS: 私の英語を許してください (そして忘れてください)。

于 2012-06-17T00:11:42.877 に答える