web-dev-qa-db-ja.com

フィルター基準に基づいてユーザーグループを格納するためのデータベース設計

ユーザーグループを定義するシステムを設計しています。グループは、クエリを実行して、関連するユーザーのプロモーションをターゲットにするために使用されます。たとえば、「今週戻ってきたら5ドル無料」のオファーを、特定の販売店でしばらく買い物をしていないユーザーに送信するには、.

グループは、多数のフィルターのセットによって定義されます。例えば:

  • 過去30日以内に購入した
  • 商人Xで買い物をした
  • 合計使用額が50ドルを超える

マーケティング部門のニーズの変化に応じて、新しいフィルターが定期的に追加されることを期待できます。

ユーザーグループには、名前と、関連する一連のフィルターがあります。例えば:

const lapsedMcDonaldsUsers = UserGroup({
  name: "Big McDonalds spenders who've lapsed",
  filters: [
    TotalSpent({greaterThan: 100.00, atMerchant: MCDONALDS_ID}),
    DaysSinceLastPurchase({greaterThan: 30, atMerchant: MCDONALDS_ID})
  ]
})

マーケティング部門の誰かが、Webインターフェースを使用してグループとそのフィルターを定義します。その定義はデータベースに保存されます。その後、ユーザーグループに送信されるメールをスケジュールできます。送信時に、フィルターに基づくクエリが実行され、受信者のリストが生成されます。

問題は、ユーザーグループとフィルターを格納するためのデータベーススキーマをどのように定義するのが最適かです。これが私の最初の試みです:

      UserGroups
      ----------
      id: primary_key
      name: string


      Filters
      -------
      id: primary_key


            TotalSpentFilters
            -----------------
            id: primary_key
            filter_id: foreign_key
            merchant_id: foreign_key
            total_spent: integer


            DaysSinceLastPurchaseFilters
            ----------------------------
            id: primary_key
            filter_id: foreign_key
            merchant_id: foreign_key
            days_since: integer

            .... (many more)


      UserGroupFiltersBindery
      -----------------------
      id: primary_key
      user_group_id: foreign_key
      filter_id: foreign_key

したがって、私はテーブル継承パターンを使用しています。この場合、個々のフィルターは、一般的なFiltersテーブルのレコードを指す独自のテーブルで定義されています。次に、UserGroupFiltersBinderyUserGroupとそのFiltersをリンクする責任があります。

このデザインについての考えは?改善からのアイデア?起こりうる問題?

5
Jonah

フィルターは、実際にはSQLステートメントの述語です。 (WHERE句)

述語は以下で構成されます。

列名、演算子、および値

さらに、AND/ORの組み合わせを持つ複数の述語が存在する可能性があります。

したがって、名前、演算子、値の表から始めます。述語がどのように組み合わされるかについての表も。

次に、テーブルを読み取ってWHERE句を適切に構築するためのコードを記述します。

**Filter Table**
ID, Description
1, 10 Purchases with a total amount over 100 dollars

**Predicate Table**
ID, Filter ID, Name, Operator, Value
1,1,PurchaseCount,GreaterThanEqual,10
2,1,PurchaseAmount,GreaterThan,100

**PredicateCombination Table**
ID, Filter ID, LeftPredId, RightPredId, Condition
1, 1, 1, 2, AND
2
Jon Raynor

ここにアイデアがあります:

フィルターの各コンポーネントを「フィルターフィールド」テーブルのレコードとして定義します。

フィルターフィールドは、どのフィールドにどのようにフィルターをかけるかを定義します。

たとえば、データ構造は次のようになります。

 filter 
 ------ 
 id-PK 
 name-"Spent> = $ 20 at ThatBurgerPlace" 
 [などの論理名.____。] dataField 
 --------- 
 id-PK 
 name-たとえば、「最後に見た日付」、「使用された合計金額」[など。 ____。] 
 filterField 
 ----------- 
 id-PK 
 filter_id-filter.idへのFK 
 data_field_id-dataField.id 
 operatorへのFK->、<、=、!=などの定義された比較演算子のリストからのものでなければなりません... 
 filter_value-フィルターします。
 

そしてそれを埋めるためのいくつかのデータ:

フィルター
 ------ 
 ID |名前
 ----- + ------ 
 1 | BurgerPlaceで20ドル以上使ったユーザー
 2 |過去30日以内に購入したユーザー
 3 | BurgerPlace 
 
 dataField 
 --------- 
 IDに50ドル以上費やした失効ユーザー名前
 ----- + ------------ 
 1 |支出額
 2 |訪問したベンダー
 3 |最新の購入日
 
 filterFields 
 ------------ 
 ID | filter_id | data_field_id |演算子| filter_value 
 ----- + ------------ + ---------------- + -------- -+ ------------- 
 1 | 1 | 1 | > = | 20ドル
 2 | 1 | 2 | = |バーガープレイス
 3 | 2 | 3 | <= | 30日
 4 | 3 | 1 | > = | 50ドル
 5 | 3 | 2 | = |バーガープレイス
 6 | 3 | 3 | > = | 90日

上記の例では、失効したBurgerPlaceの顧客/ユーザーを見つけるためのフィルターを定義しました。50ドル以上費やしていても、直近の購入日は90日以上前です。

凝ったものにしたい場合は、次のようなフィールドのタイプを定義することもできます。

 dataField 
 --------- 
 id-PK 
 name-たとえば、「最後に見た日付」、「使用した合計金額」
 data_type-in​​t、string、dateなど。

フィルターを作成する人が日付に文字列演算子を使用するのを防ぐために使用できます。

よりトリッキーなのは、マーチャント/ベンダーIDなどによって、フィルター値に外部キー制約を適用できるようにする場合です。

また、dataFieldに基づいてユーザー/顧客エンティティの値を検索する方法がすでにあると思います。

「SQLインジェクション」はよりエレガントでしょうか?それとも昔ながらのビューですか?

id: primary_key
name: string
sql: string
0
bbaassssiiee

おめでとうございます。これで「マーケットセグメンテーション分析」と呼ばれるものの受信側になりました。あなたが本当に幸運であるならば、あなたが実装しなければならないあいまいで予測不可能な人口統計学的指標を思いつくことに専念するマーケティングアナリストがいます。乾杯!

以前にこれを行ったことがあるので、エレガントなフィルタリングテーブル構造を忘れて(マーケティングが思いつくようなことを予想することはできません)、代わりに完全に柔軟なことを行うことをお勧めします。

2つのオプションが思い浮かびます:

  1. 個々の table-valued UDF を使用して、各フィルターを満たす行を返します。マーケティングが新しいフィルターのアイデアを思いついたら、新しいUDFを書くだけです。これにより、データベースにあるデータのみが必要である限り、完全な柔軟性が得られます。リードのリストを取得する必要がある場合は、必要に応じてUDFに参加します。

  2. Filter-customer結合テーブルを定義します(FilterIDおよびCustomerIDの2つの列のみ)。結合テーブルに1日に1回実行されるサービスまたはエージェントジョブを入力します。これにより、データベース、他のデータベース、または外部サービス(IPジオロケーションAPIなど)からのデータを使用するための完全な柔軟性が得られます。リードのリストをプルする必要がある場合は、正しいフィルターIDを使用して、顧客テーブルを結合テーブルに結合するだけです。

0
John Wu