web-dev-qa-db-ja.com

連想テーブルを使用するよりも、フラグをビットマスクとして格納する方が良い場合はいつですか?

ユーザーがさまざまな機能を使用するためのさまざまな権限(読み取り、作成、ダウンロード、印刷、承認など)を持っているアプリケーションに取り組んでいます。権限のリストが頻繁に変更されることは想定されていません。これらの権限をデータベースに保存する方法について、いくつかのオプションがあります。

オプション2の方が優れているのはどのような場合ですか?

オプション1

連想テーブルを使用します。

ユーザー
 ---- 
ユーザーId(PK)
名前
部門
権限
 ---- 
 PermissionId(PK)
 Name
User_Permission 
 ---- 
 UserId(FK)
 PermissionId(FK)

オプション2

各ユーザーのビットマスクを保存します。

ユーザー
 ---- 
 UserId(PK)
 Name 
 Department 
 Permissions
[Flags]
enum Permissions {
    Read = 1,
    Create = 2,
    Download = 4,
    Print = 8,
    Approve = 16
}
75
Ryan Kohn

素晴らしい質問です!

最初に、「より良い」についていくつかの仮定をしてみましょう。

ディスクスペースについてはあまり気にしていないと思います。スペースの観点からはビットマスクが効率的ですが、SQLサーバーを使用している場合、それが問題になるかどうかはわかりません。

私はあなたがスピードを気にしていると思います。計算を使用する場合、ビットマスクは非常に高速ですが、ビットマスクを照会するときにインデックスを使用することはできません。これはそれほど重要ではありませんが、どのユーザーに作成アクセス権があるかを知りたい場合、クエリは次のようになります。

select * from user where permsission & CREATE = TRUE

(今日、SQL Serverにアクセスできません)。数学的な演算のため、そのクエリはインデックスを使用できません。そのため、膨大な数のユーザーがいる場合、これは非常に苦痛です。

保守性を気にかけていると思います。保守性の観点から見ると、ビットマスクは、明示的な権限を格納するほど根本的な問題ドメインほど表現力がありません。ほとんどの場合、データベースを含む複数のコンポーネント間でビットマスクフラグの値を同期する必要があります。不可能ではないが、裏側の痛み。

したがって、「より良い」と評価する別の方法がない限り、ビットマスクルートは、正規化されたデータベース構造にアクセス許可を格納するよりも優れていると思います。 「結合を行う必要があるために遅くなる」ことに同意しません-完全に機能しないデータベースがない限り、これを測定することはできません(アクティブインデックスの利点がないクエリは顕著になる可能性があります)数千レコードでも遅くなります)。

63
Neville Kuyt

個人的には、連想テーブルを使用します。

ビットマスクフィールドは、クエリを実行して結合することが非常に困難です。

これを常にC#フラグ列挙型にマップし、パフォーマンスが向上した場合はデータベースをリファクタリングできます。

時期尚早の最適化に対する読みやすさ;)

11
Oded

正規化されたアクセス許可を保存します(ビットマスクではありません)。これは明らかにシナリオではrequirementではありませんが(特に、アクセス許可が頻繁に変更されない場合)、クエリがはるかに簡単かつ明確になります。

5
Adam Robinson

決定的な答えはありませんがあるので、-あなたのために何をするかです。しかし、ここに私のキャッチがあります:

オプション1を使用する場合

  • あなたは許可が多くに成長すると期待しています
  • データベースストアドプロシージャ自体で権限チェックを行う必要がある場合
  • テーブル内のレコードが大幅に増加しないように、数百万のユーザーを想定していません

オプション2を使用する場合

  • 権限は一握りに制限されます
  • あなたは何百万ものユーザーを期待しています
5
Aliostad

構造が変更されず、常に一緒に使用される場合に役立ちます。そうすれば、サーバーへの往復がほとんどなくなります。また、変数の1回の割り当てですべての権限に影響を与えることができるため、パフォーマンス面でも優れています。

私は個人的には好きではありません...一部のパフォーマンス重視のアプリケーションでは、それらはまだ使用されています。ボードを1回の比較で評価できるため、これらを使用してchess-AIを実装したことを覚えています。

1
Simon Dufour

私は常にそれを正規化して保存しますnlessデータベースは単にレコードを保持しているだけであり、取得と保存以外にこれを行うことはありません。このシナリオは、ログイン時にユーザーのアクセス許可文字列がフェッチされ、サーバーコードで処理されてキャッシュされる場合です。その場合、非正規化されていることはそれほど重要ではありません。

文字列に格納していて、DBレベルでそれを処理しようとしている場合、ページXのアクセス許可を取得するために、いくつかの体操を行う必要があります。

1
Mike M.

以下の理由により、ビットマスクを使用しないことをお勧めします。

  • インデックスを効率的に使用できません
  • クエリisより難しい
  • 読みやすさ/メンテナンスに深刻な影響がある
  • 平均的な開発者はビットマスクが何であるかを知りません
  • 柔軟性が低下する(数値のビットのnrの上限)

クエリパターン、計画された機能セット、データ分散に応じて、オプション1を使用するか、次のような簡単なものを使用します。

user_permissions(
   user_id
  ,read     
  ,create   
  ,download 
  ,print    
  ,approve  
  ,primary key(user_id)
);

列の追加はスキーマの変更ですが、「パージ」の権限を追加するには、それに伴うコードが必要になるため、権限は思ったほど動的である必要はないかもしれません。

ユーザーベースの90%が単一の権限を持っていないなど、データの不調な分布がある場合、次のモデルも正常に機能します(ただし、より大きなスキャンを実行すると壊れます(1つの5方向結合と1つの完全なテーブル)スキャン)。

user_permission_read(
   user_id
  ,primary key(user_id)
  ,foreign key(user_id) references user(user_id)
)

user_permission_write(
   user_id
  ,primary key(user_id)
  ,foreign key(user_id) references user(user_id)
)

user_permission_etcetera(
   user_id
  ,primary key(user_id)
  ,foreign key(user_id) references user(user_id)
)
1
Ronnis

ビットマスクフィールドを使用してアクセス許可を格納することを考えることができるのは、古いモバイルデバイスのように、物理メモリの量に本当に制約があるときだけです。実際には、保存するメモリの量はそれだけの価値はありません。何百万人ものユーザーでさえ、ハードドライブのスペースは安価であり、ビットマスク以外のアプローチを使用することで、アクセス許可などをより簡単に拡張できます(これは、誰がどのアクセス許可を持っているかなどのレポートに関するものです)。

私が遭遇したこの最大の問題の1つは、ユーザー権限をデータベースに直接割り当てることです。アプリケーション自体を管理するためにアプリケーションを試して使用する必要があることはわかっていますが、アプリケーションデータはあまり一般的ではありませんが、必要な場合もあります。ビットマスクが実際には文字フィールドでなく、整数の代わりに誰かがどのような権限を持っているかを簡単に確認できない場合は、フィールドを更新して書き込みアクセスなどを誰かに与える方法をアナリストなどに説明してみてください。あなたの計算は正しいです。

1
kemiller2002