弊社では、iCal文字列を使用して定期的なアポイントメント基準を保存し、数か月前に定期的なアポイントメントを使用してカレンダーシステムを実装しました。
これらが原因で、パフォーマンスの問題が発生し始めています。
私たちの現在の考えはこれらの線に沿ったものです...
ユーザーが日付範囲のすべての予定を要求します。通常は30日以内です。
データベースから、この日付範囲に該当するすべての非定期的なイベントを取得します。次に、iCal文字列を含むすべてのイベントを取得し、それぞれをプロセッサで実行して、要求された日付範囲でイベントが繰り返されるかどうかを判断します。たとえば、ユーザーは毎週火曜日と水曜日に繰り返されるイベントを2014年1月1日に開始し、永久に継続することができます。ただし、別のイベントが2014年1月1日に始まり、毎週火曜日と水曜日に1か月間繰り返され、現在、選択した範囲外です。最初のものは、日付範囲まで繰り返され、含まれます。 2番目のものはしません。
私の知る限り、実際にデータベースからこれらのイベントを1つ1つプルして、それらをテストして日付範囲と一致するかどうかを確認する必要がないように、フィルタリングの種類を決定する良い方法はありません。 。
繰り返し発生するイベントの数が増えるにつれ、データベースはますます多くのレコードを返すリクエストでログアウトし、サーバーはこれらのそれぞれを処理する必要がありますが、そのほとんどは必要ありませんが、取得するまでわかりませんそれらをサーバーに送信して処理します。
誰かがこれに直面したことがあり、返されるレコードの数を制限するために実装できるより良いアルゴリズムはありますか?
トム・カイトの本の1つで、彼はこの正確な問題を抱えている会社について説明しています。彼らはあなたがしたことをし、パフォーマンスはひどいものでした。彼の推奨は、定期的な予定の各インスタンスを将来のある時点までデータベースに保存することでした。これにより、予定の検索に関するパフォーマンスの問題がすべて解決されました。
はい、いくつかの予定の何百もの発生があるかもしれません。だから何?開始時刻を格納するだけでよく、それはほんの数キロバイトです。そして、それらを無期限に保存する必要はありません。 2、3年で十分です。最後に2017年の予定を知りたいのはいつですか。
あなたの問題は最初の文にあるようです:
私たちの会社は、数か月前に定期的なアポイントメントでカレンダーシステムを実装しましたiCal文字列を使用して、定期的なアポイントメントの基準を保存します。
日付範囲を決定するためにすべてのレコードをスキャンする必要があることは、単純にスケーラブルではありません。本質的には、データベースをフラットファイルとして使用しているため、当然低速です。
生のiCal文字列を保存する代わりに、文字列を解釈しますbeforeデータベースに配置して、次のようなクエリを実行できるようにします
select * from records
where startdate < "2014-01-30 00:00:00" and enddate > "2014-01-01 00:00:00";
本当にiCal文字列が必要な場合は、それもDBに残すことができますが、予定の開始/終了日などは、効率的に検索できるようにdb列にする必要があります。
私はこれを認識してこれに対する私の解決策を提供したいと思います:
私のソリューションでは、予定ごとに開始日と終了日があります。単一の予定の場合、終了日を開始日と同じにすることができます。将来の予定が繰り返し発生する場合は、終了日を空のままにするか、将来の値まで遠くに残すことができます。
これで、すべての予定に対して、タイプとパターンという追加の列があります。この列には、毎日、毎週、毎月、隔週などの繰り返しパターンを指定する列挙型の値を含めることができます。繰り返し発生しない予定の場合、タイプは1回のみです。
これは、予定が実際に繰り返し発生する予定の一部であるが、予定外のパターンになるように再スケジュールされた場合も扱います。そのタイプを_re-schedule
_などに設定すると、元の定期的な予定を参照するための参照列が追加される可能性があります。
日付範囲に一致するすべての予定(任意のタイプの)を検索する場合は、「タイプとパターン」列とともに開始日と終了日を使用できます。 2014年10月1日から2014年12月1日までの範囲で予定を検索する場合の具体例は次のとおりです。
_select * from APP as A where
((A.sd >= $start AND A.sd <= $end) OR (A.ed <= $end AND A.ed >= $start) OR (A.sd <= $start AND A.ed >= $end) )
AND (A.type = 'Single' OR A.type = 'ReSchedule' OR inRange($start, $end, A.sd, A.type) )
_
ここでは、inRange()
の独自の関数を作成する必要があります。セマンティクスは_$end
_変数を整数として取り、_A.sd
_の整数を引いて、パターン間隔で除算します。次に、_$end
_から部門のmodを引いたものが_$start
_以上かどうかを確認します
InRange()
は、すべての生の一致を調べることにより、クエリの速度を低下させます。しかし、これは元の設計概念よりもはるかに優れています。 PL/SQLを使用してInRange()
を実装するか、SQLクエリの結果を使用して外部メソッドを記述して、生の一致を適切にフィルタリングできます。
私の見たところ、データベースには1種類の予定のみが格納されているはずです。その予定情報には、(とりわけ)開始日、繰り返しパターン、終了日が含まれている必要があります。
Dbには、「定期的な」ものごとの予定はなく、最初の予定とその繰り返しパターンだけを指定する必要があります。反対側のアプリケーションは、それに関連する予定をプルし、パターンに基づいて「定期的な」予定を計算します。これはもちろん、次の30日間に発生するすべてのアポイントメントが必要な場合、開始日が現在+30日未満で終了日が現在より大きいすべてのレコードをプルします。
データベースでこれを処理したい場合は、おそらくこれらの計算を実行して、これらの追加の予定を含む一時テーブルを返すようにdbを作成できます。
最後に、アポイントメントを作成/更新するときに、終了日(希望する場合)に、計算された終了日(クライアントエンドによって明示的に設定されるか、クライアントエンドまたはデータベース関数によって暗黙的に計算される)を入力する必要があります。イベントパターンは、発生数またはその他の事前計算可能な基準を指定します。