5

私は、モバイルカーディテーリング会社 (およびできれば他の会社) の管理に役立つ管理アプリケーションを構築しています。一部のデータをモデル化する方法を理解するのに苦労しています。

この質問は、私が投稿した以前の質問に関連していますが、以下の関連情報を再現しました: データベース設計 - Google アプリ エンジン

このアプリケーションには、「予定」と「明細」という概念があります。

予定は、従業員がサービスを提供するために予定されている場所と時間です。

項目は、サービス、料金、または割引とそれに関連する情報です。予定に入る可能性のある項目の例:

名前: 価格: 手数料: 所要時間   
フル ディテール、レギュラー サイズ: 160 75 3.5 時間
$10 オフの詳細クーポン: -10 0 0 時間
プレミアム詳細: 220 110 4.5 時間
派生合計 (明細項目ではない): $370 $185 8.0 時間

このアプリケーションの以前の実装では、明細項目は 1 つの予定に含まれていました。これはほとんどの場合問題なく動作しましたが、時々問題が発生しました。たとえば、雨のために予定が途中で中断され、技術者が翌日戻ってきて仕上げなければならなかった場合などです。この状況では、同じ品目に対して 2 つの予約が必要でした。このような場合、2 番目の予定に「明細項目」を設定して「終了」などと読み上げることで、データを少しごまかすだけで、費用は 0 ドルになります。

この次のバージョンでは、次のようなテーブル構造を使用して、明細項目を複数の予定と照合できるようにすることを検討しています。

Appointment
 start_time
 etc...

Line_Item
 appointment_Key_List
 name
 price
 etc...

この構造の一般的な問題は、構造が複雑で、1 つの項目を複数の予定と一致させることが適切かどうかさえわからないことです。Line Items が 1 つの Appointment の一部にしかならない場合、実際には各 Appointment に Line Items のリストを入れるだけで済みます。

より具体的な問題は、Google App Engine を使用していて、一連の予定とそれに関連する項目をクエリする場合、最初に一連の予定をクエリしてから、その回線に対して 2 番目のクエリを実行する必要があることです。 IN 演算子を使用して、Line_Item の予定キーのいずれかが、前のクエリから返された一連の予定キーに該当するかどうかをテストします。クエリを分割する必要があるキーが 30 個を超える場合、2 番目のクエリは失敗します。この複雑で大規模な読み取りクエリを回避するためにデータを非正規化することもできますが、おそらくある程度非正規化する必要がありますが、必要に応じて複雑さを避けたいと思います。

私の質問は、この種の状況は通常どのようにモデル化されているのですか? ラインアイテムを複数の予定とペアにすることは適切ですか、それとも、「2 日間の仕事の前半」と「2 日間の仕事の後半」のように、ラインアイテムを単に予定ごとに別々のものに分割するのが普通ですか? ." 同様の成功したアプリケーションはどのようにこれを行いますか? この種の状況における経験則は何ですか? 問題が少ないことが判明した実装はどれですか?

ありがとう!

4

2 に答える 2

2

あなたが提案しているアプローチはうまくいきます。項目の「appointment_Key_list」をリスト プロパティとしてモデル化すると、期待どおりに機能します。IN 演算子を使用する必要はありません。これは、データストア内の単一の値をキーのリストと照合するためのものです (例: "WHERE datastore_column IN ('a', 'b', 'c'))。逆を行っています-データストア内のリストに対して単一の値を照合します。

ただし、逆の方がタスクに適している可能性があることをお勧めします。各 Appointment に項目キーのリストを持たせます。これはほとんど同じように動作しますが、予定に関するすべてのデータを取得するには、まず予定をフェッチしてから、Appointment エンティティのキ​​ーを使用して、明細項目を一括取得します。Appointment のキーがわかっている場合は、クエリを実行する必要がまったくありません。

私は Pindatjuh に、リスト プロパティのクエリが単一値のクエリよりも効率的である理由を説明しようとしてきましたが、明らかにより詳細な説明が必要なので、これ以上苦労することなく、ここに...

App Engine Datastore のインデックス作成に関する簡単な入門書

Python と Java はデータストアにさまざまな高レベルのインターフェイスを提供しますが、データストア自体はエンティティと呼ばれる低レベルの抽象化を話します。エンティティは次のもので構成されます。

  1. 一意の主キー
  2. (名前、値) ペアのリスト

プライマリ キーは、既に使い慣れた Datastore キーです。(name, value) ペアのリストは、エンティティ内のデータに対する App Engine の表現です。これまでのところ、簡単です。次の値を持つエンティティ:

a_string = "Hello, world"
an_int = 123

これに似たものにシリアル化されます:

[('a_string', 'Hello, world'), ('an_int', 123)]

しかし、これはリストとどのように相互作用するのでしょうか? リストは「複数の値を持つ」プロパティとして扱われます。つまり、n 個のアイテムを持つリストは、n 個の個別のプロパティとして格納されます。例はおそらくこれをより明確にします:

a_string = "Hello, world"
an_int = 123
a_list_of_ints = [42, 314, 9]

次のようにシリアル化されます。

[('a_string', 'Hello, world'), ('an_int', 123), ('a_list_of_ints', 42), ('a_list_of_ints', 314), ('a_list_of_ints', 9)]

ご覧のとおり、リストはすべて同じ名前の一連の値で表されます。データストアからデータをロードすると、SDK は繰り返される値を見て、それをリストに変換します。

これが重要になるのは、インデックス作成と相互作用する場合です。「a_string」と「an_int」にインデックスがあるとします。値を挿入または変更すると、App Engine はその値の一連のインデックス エントリを生成します。上記のインデックスと上記のエンティティの場合、次のようなインデックスに単一の行が生成されます。

('Hello, world', 123, a_key)

('a_key' は、元のエンティティのキ​​ーのプレースホルダーです。) このインデックスを使用するクエリを実行する場合、適切なプレフィックス (たとえば、'SELECT * FROM Kind WHERE a_string = "Hello, world" ORDER BY an_int')。

ただし、リストをインデックスに登録すると、App Engine は複数のインデックス行を挿入します。「an_int」および「a_list_of_ints」のインデックスは、上記のエンティティに対して次の行を生成します。

(123, 42, a_key)
(123, 314, a_key)
(123, 9, a_key)

ここでも、クエリは以前と同じように機能します。App Engine は、インデックス内の正しいプレフィックスを持つ行を検索するだけです。リスト内のエントリの数は、クエリの速度には影響しません。インデックス エントリの生成と書き込みにかかった時間のみに影響します。実際、クエリ プランナーは、'a_list_of_ints' が複数の値を持つプロパティであることを認識していません。単に他のインデックス エントリと同じように扱います。

つまり、一言で言えば:

  1. 要素を 1 つ含むリストと個々のプロパティの間には、インデックス作成とクエリの用語において実質的な違いはありません。
  2. インデックス付きリストのサイズは、インデックス作成に必要な時間と領域に影響しますが、クエリには影響しません。
  3. 単純な等価フィルターを使用して、リスト内の特定の値を持つ任意のエンティティと一致するクエリを実行できます。
于 2010-06-30T18:02:15.723 に答える
1

この種の問題の通常の解決策は、モデルを正規化することです。つまり、第一正規形に正規化することです。

正規化された形式のモデルには、AppointmentLine_Item行への参照を含む3番目のテーブルがあります。

Appointment
 start_time
 ...

Line_Item
 name
 price
 ...

Appointment_Line_Item
 appointment_key
 line_item_key

しかし問題があります!Google App Engineを使用していて、そのデータストアは非常に制限されており(「GQLはSQLのようなJOINを実行できません」)、ほとんどの場合、非正規化が必要です。

リストのようなフィールドを使用することを提案しました。これを使用することは可能ですが、インデックスを作成するのは非常に困難です。appointment_keyデータベースの行ごとのリストでキー()を検索することは、実際には実行されていません。私は2つの可能性を提案します:

  1. 複製しLine_Itemます。

    Line_Item
     appointment_key
     name
     price
     finished
     ...
    

    アイテムが従業員によって完成されたかどうかにかかわらず、ALine_Itemには状態が必要です。finished従業員がすべてのラインアイテムを完了していない場合は、それらを未完了としてマークし、新しい予定を作成して、未完了のすべてのアイテムをコピーします。appointment_keyすべてのフィールドにインデックスを付けることができますLine_Items。これは良いことです。ただし、重複したデータが問題になる可能性があります。

  2. の動的フィールドLine_Item

    Line_Item
     duplicate_key
     appointment_key
     name
     price
     finished
     ...
    

    別のフィールドまたはnullを指す新しいフィールドを作成duplicate_keyします(このキーを予約してください!)。Nullは、が元の値であることを意味し、その他の値は、これがフィールドが指すフィールドの複製であることを意味します。重複としてマークされたのすべてのフィールドは、:を除いて元のフィールドを継承するため、必要なストレージが少なくなります。また、ルックアップ時間を短縮するために、このソリューションにはインデックスを付ける必要があります。これには、複製ごとに1つの追加クエリが必要であり、問​​題になる可能性があります。Line_ItemLine_ItemLine_ItemLine_ItemLine_ItemLine_ItemLine_Itemappointment_keyappointment_keyLine_Item

今、それは明確な選択です:より良い速度またはより良いストレージのどちらか。モデルの複雑さが軽減され、ストレージが最新のシステムで問題になることはないため、最初に説明します。一般に、複雑さが少ないということは、バグが少なく、開発/テストのコストが少ないことを意味します。これは、ストレージ要件のコストを正当化するものです。

于 2010-06-26T19:48:31.930 に答える