5

さまざまなビジネスの「営業時間」を保存するアプリを作成しています。アイテムが開いているかどうかを簡単に確認できるように、このデータを表す最も簡単な方法は何ですか?

いくつかのオプション:

  • 「開いている/閉じている」とマークできるブロックを (15 分ごとに) セグメント化します。チェックには、「オープン」ビットが目的の時間に設定されているかどうかを確認する必要があります (列車のスケジュールに少し似ています)。
  • 時間範囲 (午前 11 時から午後 2 時、午後 5 時から午後 7 時など) のリストを保存し、現在の時間が指定された範囲内にあるかどうかを確認します (これは、上記の文字列を解析するときに私たちの脳が行うことです)。

時刻表情報の保存とクエリの経験がある人はいますか?何かアドバイスはありますか?

(「月の第 1 火曜日は休業」など、あらゆる種類のクレイジーなコーナー ケースがありますが、それは別の日にします)。

4

10 に答える 10

5

連続する各時間ブロックを開始時間と期間として保存します。これにより、時間がいつ日付の境界をまたぐかを簡単に確認できます

営業時間が日付の境界を越えないことが確実な場合 (つまり、終夜営業や 72 時間のマラソン イベントなどは決してない)、開始/終了時間で十分です。

于 2008-09-26T22:05:57.313 に答える
3

最も柔軟なソリューションは、ビットセット アプローチを使用することです。1 週間は 168 時間なので、15 分間の期間は 672 回あります。これは 84 バイトに相当するスペースであり、許容できるはずです。

于 2008-09-26T22:06:10.587 に答える
2

次のようなテーブルを使用します。

BusinessID | weekDay | OpenTime | CloseTime 
---------------------------------------------
     1          1        9           13
     1          2        5           18
     1          3        5           18
     1          4        5           18
     1          5        5           18
     1          6        5           18
     1          7        5           18

ここでは、通常は 5 時から 6 時まで営業していますが、日曜日は営業時間を短縮しています。

開いている場合のクエリは (psedo-sql)

SELECT @isOpen = CAST
   (SELECT 1 FROM tblHours 
       WHERE BusinessId = @id AND weekDay = @Day 
       AND CONVERT(Currentime to 24 hour) IS BETWEEN(OpenTime,CloseTime)) AS BIT;

エッジ ケースを保存する必要がある場合は、1 日 1 つずつ、365 エントリを保持するだけです。実際にはそれほど多くはありません。day 列と businessId 列にインデックスを配置します。

ビジネスのタイムゾーンを別のテーブルに保存することを忘れないでください (正規化してください)。これらの比較を行う前に、自分の時間とそれを変換します。

于 2008-09-26T23:15:06.523 に答える
1

Johnathan Hollandが言ったことに加えて、私は同じ日に複数のエントリを許可します。

また、小数時間、または分用の別の列も考慮に入れます。

なんで?多くのレストランやいくつかの企業、そして世界中の多くの企業が昼食や午後の休憩をとっています。また、多くのレストラン(私の家の近くにある2つは、15刻みではない奇妙な時間に閉店します。1つは日曜日の午後9時40分に閉店し、もう1つは午前1時40分に閉店します。

また、感謝祭の日の早い時間に閉店するなど、休日の時間の問題もあるため、カレンダーベースのオーバーライドが必要です。

おそらく、実行できるのは、次のような日付/時刻のオープン、日時のクローズです。

businessID  | datetime              | type
==========================================
        1     10/1/2008 10:30:00 AM    1
        1     10/1/2008 02:45:00 PM    0
        1     10/1/2008 05:15:00 PM    1
        1     10/2/2008 02:00:00 AM    0
        1     10/2/2008 10:30:00 AM    1

など(タイプ:1が開いていて0が閉じている)

また、今後1〜2年のすべての日を、1〜2年前に事前計算してください。int、date / time / bitの3つの列しかないため、データ消費を最小限に抑える必要があることに注意してください。

これにより、特定の日付が判明したときに、特別な日の奇数時間の特定の日付を変更することもできます。

また、深夜の交差、および12/24時間の変換も処理します。

また、タイムゾーンに依存しません。開始時間と期間を保存する場合、終了時間を計算するときに、マシンはTZ調整時間を提供しますか?それはあなたが望むものですか?より多くのコード。

オープンクローズステータスのクエリに関する限り:問題の日時をクエリし、

select top 1 type from thehours where datetimefield<=somedatetime and businessID = somebusinessid order by datetime desc

次に「タイプ」を見てください。1の場合は開いており、0の場合は閉じています。

PS:私は10年間小売店にいました。だから私は中小企業のクレイジーアワーの問題に精通しています。

于 2008-09-26T23:31:18.137 に答える
1

わかりました、私はそれが価値があるもののためにこれに投入します.

かなりの数のことを処理する必要があります。

  • 高速で高性能なクエリ
  • 時間の任意の増分、9:01 PM、12:14 など。
  • 国際 (?) - これがタイムゾーンでも問題になるかどうかはわかりませんが、少なくとも私の場合は、ここに精通している誰かが自由にチャイムを鳴らしてください。
  • 開館~翌日休館(正午開館、午前2時閉館)
  • 複数の時間帯 / 日
  • 特定の日 (休日など) を上書きする機能
  • 上書きを繰り返す機能
  • 任意の時点でクエリを実行し、ビジネスを開始する機能 (現在、未来の時間、過去の時間)
  • 閉店間近のビジネスの結果を簡単に除外する機能 (30 分以内に閉店するビジネスをフィルター処理します。食品/飲料業界では、閉店の 5 分前に現れるようなユーザーをユーザーにしたくありません)

私は提示されたアプローチの多くが好きで、それらのいくつかから借りています。私のウェブサイトやプロジェクトでは、考慮に入れる必要があるものは何でも、何百万ものビジネスがあり、ここでのアプローチのいくつかは、個人的にうまく拡張できないようです。

アルゴリズムと構造について私が提案するものは次のとおりです。

世界中で、いつでも、どこでも、1 週間は 7 日という具体的な仮定を立てる必要があります。1日は1440分です。可能なオープン/クローズの分の順列の数は限られています。

具体的ではありませんが、適切な仮定: オープン/クローズの議事録の多くの順列は、実際に保存されている順列の合計を削減する企業間で共有されます。私の人生には、このアプローチの実際の可能な組み合わせを簡単に計算できた時期がありましたが、誰かがそれを支援/有用だと考えることができれば、それは素晴らしいことです.

私は 3 つのテーブルを提案します: 読むのをやめる前に、これらのテーブルのうちの 2 つが十分に小さいキャッシュになることを現実世界で考えてみてください。このアプローチは、UI をデータ モデルに解釈し、必要に応じて元に戻すために必要なコードが非常に複雑であるため、すべての人に適しているわけではありません。走行距離とニーズは異なる場合があります。これは、それが何を意味するにせよ、合理的な「エンタープライズ」レベルのソリューションの試みです。

HoursOfOperations テーブル

ID | OPEN (分) | CLOSE (分)


1 | 360 | 1020 (例: 午前 9 時~午後 5 時)

2 | 365 | 1021 (例: エッジケース 9:05 AM - 5:01 PM (変人))

HoursOfOperations は何日かは気にせず、オープンとクローズと一意性だけです。始値と終値の組み合わせごとに 1 つのエントリしか存在できません。現在、環境に応じて、このテーブル全体をキャッシュするか、1 日の現在の時間などにキャッシュすることができます。とにかく、操作ごとにこのテーブルにクエリを実行する必要はありません。ストレージ ソリューションによっては、このテーブルのすべての列がパフォーマンスのためにインデックス化されていると想定しています。時間が経つにつれて、この表は INSERT の確率が指数関数的に逆になる可能性があります。ただし、実際には、このテーブルの処理はほとんどがインプロセス操作 (RAM) である必要があります。

Business2HoursMap

注: 私の例では、「日」をビット フラグ フィールド/列として格納しています。これは主に、私のニーズと、C# での LINQ / Flags Enums の進歩によるものです。これを 7 ビット フィールドに拡張することを妨げるものは何もありません。どちらのアプローチも、ストレージ ロジックとクエリ アプローチの両方で比較的似ているはずです。

別の注意: 「すべてのテーブルに PK ID 列が必要」というセマンティクスの議論には参加していません。そのための別のフォーラムを見つけてください。

ビジネス ID | 時間 ID | 日 (または、希望する場合は、BIT 月曜日、BIT 火曜日、...)


1 | 1 | 1111111 (このビジネスは毎日 9 時から 5 時まで営業しています)

2 | 2 | 1111110 (このビジネスは 9:05 ~ 5:01 M-Sat (月曜日 = 1 日目) に営業しています)

これが簡単にクエリできる理由は、目的の MOTD (その日の分) をいつでも非常に簡単に判断できるからです。明日の午後 5 時に何が開いているか知りたい場合は、すべての HoursOfOperations IDS WHERE Close >= 1020 を取得します。時間範囲を探していない限り、Open は重要ではありません。次の 30 分以内に閉店するビジネスを表示したくない場合は、それに応じて着信時刻を調整します (午後 5 時 (1020) ではなく、午後 5 時 30 分 (1050) を検索します)。2 番目のクエリは当然 ' HoursID IN (1, 2, 3, 4, 5) などのすべてのビジネスを私に与えてください.このアプローチには制限があるため、これはおそらく警告を発するはずです.しかし,誰かが上記の実際の順列の質問に答えることができれば,私たちはそうするかもしれません.赤旗を引き下げることができます.一度に方程式のいずれかの側で可能な順列のみが必要であると考えてください.

最初のテーブルがキャッシュされていることを考えると、これは簡単な操作です。2 番目の操作では、この潜在的に大きな行のテーブルをクエリしていますが、非常に小さな (SMALLINT) インデックス付きの列を検索しています。

さて、コード側の複雑さを目の当たりにしているかもしれません。私は特定のプロジェクトで主にバーをターゲットにしているので、「午前 11 時から午前 2 時 (翌日)」などの営業時間を持つビジネスがかなりの数あると想定するのは非常に安全です。それは実際、HoursOfOperations テーブルと Business2HoursMap テーブルの両方への 2 つのエントリになります。たとえば、午前 11 時から午前 2 時まで営業しているバーには、HoursOfOperations テーブル 660 から 1440 (午前 11 時から午前 0 時) および 0 から 120 (午前 0 時から午前 2 時) への 2 つの参照があります。これらの参照は、Business2HoursMap テーブルの実際の日数に反映されます。単純なケースでは、1 つのエントリ = 全日の時間参照 #1、別の全日の参照 #2 という 2 つのエントリとして反映されます。それが理にかなっていることを願っています、長い一日でした。

特別な日/休日/何でもオーバーライドします。上書きは本来、日付ベースであり、曜日ベースではありません。これは、いくつかのアプローチが、ことわざの丸いペグを四角い穴に押し込もうとする場所だと思います. 別のテーブルが必要です。

時間 ID | ビジネス ID | 日 | 日 | 月 | 月 | 年

1 | 2 | 1 | 1 | ヌル

「毎週火曜日に、この会社は 4 時間釣りに行く」のようなものが必要な場合、これは確かにより複雑になります。ただし、これにより非常に簡単にできることは、1 - オーバーライドを許可し、2 - 妥当な繰り返しのオーバーライドを許可することです。たとえば、年が NULL の場合、毎年元旦に、この変人バーは上記のデータ例に沿って、午前 9 時から午後 5 時まで営業しています。つまり、年が設定されている場合は、2013 年のみです。月が null の場合は、毎月の最初の日です。繰り返しになりますが、これは NULL 列だけですべてのスケジューリング シナリオを処理できるわけではありませんが、理論的には、必要に応じて絶対日付の長いシーケンスに依存することで、ほぼすべてを処理できます。

繰り返しますが、このテーブルをローリング デイ ベースでキャッシュします。少なくとも私のニーズでは、1 日のスナップショットでこのテーブルの行が非常に大きいことを現実的に見ることはできません。最初にこのテーブルを確認し、オーバーライドして、ストレージ側のはるかに大きな Business2HoursMap テーブルに対してクエリを保存します。

興味深い問題です。これを真剣に考える必要があったのはこれが初めてで、本当に驚いています。いつものように、私のアプローチのさまざまな洞察、アプローチ、または欠陥に非常に熱心です。

于 2013-11-19T06:45:50.803 に答える
1

すべてがより柔軟になるので、私は個人的に開始時間と終了時間に行くと思います。良い質問は次のとおりです: ブロック サイズが特定の時点で変化する可能性はどのくらいですか? 次に、状況に最も適した解決策を選択します(変更される可能性がある場合は、間違いなくタイムスパンを選択します)。

それらをタイムスパンとして保存し、アプリケーションでセグメントを使用できます。そうすれば、ブロックを使用して簡単に入力できると同時に、データストアを柔軟に変更できます。

于 2008-09-26T23:09:02.230 に答える
0

セグメントブロックの方が優れています。ユーザーが簡単に設定できるようにしてください。クリックアンドドラッグが良いです。

他のシステム(範囲など)は、真夜中の境界を越えると本当に迷惑になります。

それらをどのように保存するかについては、C++ではビットフィールドがおそらく最適でしょう。他のほとんどの言語では、配列の方が優れている可能性があります(多くの無駄なスペースがありますが、実行速度が速く、理解しやすいでしょう)。

于 2008-09-26T22:20:53.533 に答える
0

これらのエッジ ケースについては、基本構成とオーバーレイがあるかどうか、または営業時間などの完全な静的ストレージがあるかどうかを通知するため、今は少し考えます。

非常に多くの例外があり、定期的に (雪の日、イースター、聖金曜日などの不規則な休日など)、これが現実の信頼できる表現であると期待される場合 (適切な推測とは対照的に)、アーキテクチャですぐに対処します。

于 2008-09-26T23:12:38.527 に答える
0

このようなものはどうですか:

営業時間表

Business_id (int)
Start_Time (time)
End_Time (time)
Condition varchar/string
Open bit

'Condition' はラムダ式 ('where' 句のテキスト) です。クエリを動的に構築します。したがって、特定のビジネスについて、すべての開店時間と閉店時間を選択します

Let Query1 = select count(open) from store_hours where @t between start_time and end_time and open  = true and business_id = @id and (.. dynamically built expression)

Let Query2 = select count(closed) from store_hours where @t between start_time and end_time and open = false and business_id = @id and (.. dynamically built expression)

したがって、次のようなものを終了します。

select cast(Query1 as bit) & ~cast(Query2 as bit)

最後のクエリの結果が 1 の場合、店舗は時間 t に開いています。それ以外の場合は閉じています。

ここで必要なのは、where 句 (ラムダ式) を生成できる使いやすいインターフェイスだけです。

私が考えることができる唯一の他のケースは、ある日の午前 7 時から午前 2 時まで営業している店舗が、次の日の午後 11 時に閉店する場合です。システムは、2 日間の時間を賢く分割することで、それも処理できるはずです。

于 2008-09-27T00:12:04.513 に答える
-1

ここでメモリを節約する必要は確かにありませんが、おそらくクリーンでわかりやすいコードが必要です。「ちょっといじる」というのは、私見ではありません。

ここには、任意の数の一意のアイテムを保持し、アイテムがメンバーであるかどうかをすばやく簡単に判別できるセットコンテナーが必要です。セットアップには注意が必要ですが、日常的な使用では、単純に理解された1行のコードで、開いているか閉じているかが決まります。

コンセプト:たとえば、日曜日の深夜から、15分ごとにインデックス番号を割り当てます。

初期化:開いているときに15分ブロックごとのインデックス番号をセットに挿入します。(あなたが閉じているよりも開いている時間が少ないと仮定します。)

使用法:興味深い時間から、分単位で、前の日曜日の真夜中を引き、15で割ります。この数値がセットに含まれている場合は、開いています。

于 2008-09-26T22:23:51.327 に答える