web-dev-qa-db-ja.com

列挙型をDBに格納するのはなぜですか?

this のように、列挙型をDBに格納する方法についてのアドバイスを求める多くの質問を見てきました。しかし、なぜあなたはそれをするのだろうかと思います。したがって、Personフィールドを持つエンティティgenderと、Gender enumがあるとします。次に、私の人物テーブルに性別の列があります。

正確さを強制する明白な理由の他に、アプリケーションにすでにあるものをマップするために、なぜ追加のテーブルgenderを作成するのかわかりません。そして、私はそのような複製が本当に好きではありません。

71
user3748908

概念や期待にこだわらない別の例を見てみましょう。ここには列挙型があり、それがバグの優先順位のセットです。

データベースにどのような値を保存していますか?

したがって、'C''H''M'、および'L'をデータベースに格納することができます。または'HIGH'など。これには、stringly-typedデータの問題があります。有効な値の既知のセットがあり、そのセットをデータベースに保存することができない場合、操作が困難になる可能性があります。

なぜデータをコードに格納するのですか?

List<String> priorities = {'CRITICAL', 'HIGH', 'MEDIUM', 'LOW'};か、コードにその効果がある何かがあります。これは、このデータの適切な形式へのさまざまなマッピングがあることを意味します(データベースにすべての大文字を挿入していますが、Criticalとして表示しています)。コードのローカライズも困難になりました。アイデアのデータベース表現を、コードに格納されている文字列にバインドしました。

このリストにアクセスする必要がある場合はどこでも、コードを複製するか、定数の束を持つクラスが必要です。どちらも良いオプションではありません。 otherこのデータを使用する可能性のあるアプリケーションがあることを忘れないでください(他の言語で記述されている可能性があります-Java Webアプリケーションには Crystal Reports 使用されるレポートシステムと Perl バッチジョブがデータをフィードします。レポートエンジンは、有効なデータのリストを知る必要があります( 'LOW'優先度に何もマークされておらず、それがレポートの有効な優先度であることを知る必要がある場合はどうなりますか?)、バッチジョブには有効な値に関する情報が含まれます。

仮に、あなた「私たちは単一言語のショップです-すべてがJavaで書かれている」と言い、この情報を含む単一の.jarを持っています-しかし、今ではapplicationsは互いに密接に結合されており、その.jarはデータを含んでいます。変更があるたびに、レポート部分とバッチ更新部分をWebアプリケーションと一緒にリリースする必要があります-そして、hopeそのリリースが進むことすべてのパーツをスムーズに。

上司が別の優先順位を求めている場合はどうなりますか?

あなたの上司が今日来ました。新しい優先度があります-CEO。次に、すべてのコードを変更して、再コンパイルして再デプロイする必要があります。

「テーブル内の列挙」アプローチでは、列挙リストを更新して、新しい優先度を設定します。リストを取得するすべてのコードは、データベースからリストを取得します。

データが単独で存在することはほとんどありません

優先度を使用すると、ワークフローはotherテーブルへのキーとなり、ワークフローに関する情報、または誰がこの優先度を設定できるかなどを含むことができます。

少し質問で述べたように、性別に戻ります。性別には、使用中の代名詞he/his/himshe/hers/her...へのリンクがあり、それをコード自体にハードコーディングすることは避けたい。そして、そしてあなたの上司がやって来て、あなたはあなたが'OTHER'性別を持っていることを追加する必要があります(簡単にするために)そしてあなたはする必要がありますこの性別をthey/their/themに関連付けます。上司はFacebookの内容を確認します...そうですね。

列挙型テーブルではなく文字列型のデータビットに制限することで、データと他のビットの間のこの関係を維持するために、その文字列を他のテーブルの束に複製する必要があります。

他のデータストアはどうですか?

これをどこに保存しても、同じ原則が存在します。

  • 優先度のリストを含むファイルpriorities.propを作成できます。プロパティファイルからこのリストを読み取ります。
  • enumsのエントリを持つドキュメントストアデータベース( CouchDB など)を作成できます(次に JavaScriptで検証関数を記述 ):

    {
       "_id": "c18b0756c3c08d8fceb5bcddd60006f4",
       "_rev": "1-c89f76e36b740e9b899a4bffab44e1c2",
       "priorities": [ "critical", "high", "medium", "low" ],
       "severities": [ "blocker", "bad", "annoying", "cosmetic" ]
    }
    
  • 少しスキーマを含むXMLファイルを作成できます。

    <xs:element name="priority" type="priorityType"/>
    
    <xs:simpleType name="priorityType">
      <xs:restriction base="xs:string">
        <xs:enumeration value="critical"/>
        <xs:enumeration value="high"/>
        <xs:enumeration value="medium"/>
        <xs:enumeration value="low"/>
      </xs:restriction>
    </xs:simpleType>
    

基本的な考え方は同じです。データストア自体は、有効な値のリストを格納して適用する必要がある場所です。ここに配置することで、コードとデータについてより簡単に推論できます。あなたは自分が何であるかを知っているので、毎回持っているものを守備的にチェックすることを心配する必要はありません(それは大文字ですか、それとも小文字ですか?なぜこの列にchritical型があるのですか?など)。データストアからの取得は、データストアが送信を期待しているものとまったく同じです。有効な値のリストについてデータストアにクエリを実行できます。

お持ち帰り

有効な値のセットはdataであり、コードではありません。あなたdo[〜#〜] dry [〜#〜] コードを求めて努力する必要がありますが、重複の問題はdataをその場所をデータとして尊重し、データベースに保存するのではなく、コード内で複製していること。

これにより、データストアに対する複数のアプリケーションの記述が容易になり、データ自体に密接に結合されているすべてのものをデプロイする必要があるインスタンスを回避できます-しないコードをデータに結合しました。

CEO優先度が追加されたときにアプリケーション全体を再テストする必要がないため、アプリケーションのテストが簡単になります。優先度の実際の値を気にするコードがないためです。

コードとデータを互いに独立して推論できるため、メンテナンスの際にバグを見つけて修正することが容易になります。

77
user40980

クエリを読み取るときに間違いを起こしやすいのは、次のうちどれですか。

select * 
from Person 
where Gender = 1

または

select * 
from Person join Gender on Person.Gender = Gender.GenderId
where Gender.Label = "Female" 

SQLで列挙型のテーブルを作成するのは、後者の方が読みやすく、SQLの書き込みと保守のエラーが少なくなるためです。

Personで性別を直接文字列にすることもできますが、その場合は大文字小文字を区別して適用する必要があります。また、DBが最適化にどれほど優れているかによっては、文字列と整数の違いにより、テーブルのストレージヒットとクエリ時間が増加する場合があります。

21
Telastyn

人々がこれについてまだ言及しなかったなんて信じられません。

外部キー

データベースにenumを保持し、enum値を含むテーブルに外部キーを追加することにより、ensureはコードがその列に誤った値を入力することはありません。これはデータの整合性を助け、列挙型のテーブルがIMOに必要な最も明白な理由です。

13

私はあなたに同意するキャンプにいます。コードにGender列挙型を、データベースにtblGenderを保持すると、メンテナンス時に問題が発生する可能性があります。これら2つのエンティティは同じ値を持つ必要があることを文書化する必要があります。したがって、一方に加えた変更は、もう一方にも加える必要があります。

次に、列挙型の値を次のようにストアドプロシージャに渡す必要があります。

create stored procedure InsertPerson @name varchar, @gender int
    insert into tblPeople (name, gender)
    values (@name, @gender)

しかし、これらの値をデータベーステーブルに保持している場合、これをどのように行うかを考えてください。

create stored procedure InsertPerson @name varchar, @genderName varchar
    insert into tblPeople (name, gender)
    select @name, fkGender
    from tblGender
    where genderName = @genderName --I hope these are the same

確かに、リレーショナルデータベースは結合を考慮して構築されていますが、どのクエリが読みやすいですか?


次に、別のクエリ例を示します。

create stored procedure SpGetGenderCounts
    select count(*) as count, gender
    from tblPeople
    group by gender

これとこれを比較してください:

create stored procedure SpGetGenderCounts
    select count(*) as count, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender
    group by genderName --assuming no two genders have the same name

次に、さらに別のクエリの例を示します。

create stored procedure GetAllPeople
    select name, gender
    from tblPeople

この例では、結果の性別セルをintからenumに変換する必要があることに注意してください。ただし、これらの変換は簡単です。これとこれを比較してください:

create stored procedure GetAllPeople
    select name, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender

Enum定義をデータベースから除外するという考えに沿った場合、これらのクエリはすべて小さくなり、保守しやすくなります。

7
user2023861

データ分析に使用できるという理由で、性別テーブルを作成します。データベース内のすべての男性または女性を検索して、レポートを生成できます。データを表示する方法が多いほど、トレンド情報を見つけやすくなります。明らかに、これは非常に単純な列挙ですが、複雑な列挙(世界の国や州など)の場合は、特殊なレポートを生成しやすくなります。

1
zackery.fix

コードでビジネスロジックを駆動するために使用されるコード列挙がある場合でも、上/下で詳しく説明する多くの理由により、DB内のデータを表すテーブルを作成する必要があります。 DB値がコード値と常に同期していることを確認するためのヒントをいくつか紹介します。

  1. テーブルのIDフィールドをIdentity列にしないでください。 IDと説明をフィールドとして含めます。

  2. 値が半静的であるか、コード列挙に関連付けられていることを開発者が理解できるように、表で別のことを行います。他のすべてのルックアップテーブル(通常、ユーザーが値を追加できる場所)では、通常、LastChangedDateTimeとLastChangedByを持っていますが、列挙型関連テーブルにそれらがないことは、それらが開発者によってのみ変更可能であることを思い出すのに役立ちます。これを文書化します。

  3. 列挙型の各値が対応するテーブルにあり、それらの値のみが対応するテーブルにあることを確認する確認コードを作成します。ビルド後に実行する自動化されたアプリケーション「ヘルステスト」がある場合は、そこにあります。そうでない場合、アプリケーションがIDEで実行されているときはいつでも、コードがアプリケーションの起動時に自動的に実行されるようにします。

  4. 同じことを行いますが、DB内からSQLスクリプトを提供する本番環境を作成します。正しく作成されていれば、環境の移行にも役立ちます。

1
Paul Schirf

最初に、データベースが1つのアプリケーションでのみ使用されるのか、それとも複数のアプリケーションで使用される可能性があるのか​​を決定する必要があります。場合によっては、データベースはアプリケーションのファイル形式にすぎません(この点でSQLiteデータベースを使用できることがよくあります)。この場合、列挙型定義をテーブルとしてビット複製することは、多くの場合うまくいく可能性があり、より理にかなっています。

ただし、データベースにアクセスする複数のアプリケーションの可能性を検討するようになり次第、列挙型のテーブルは非常に理にかなっています(他の答えは、詳細に説明されています)。考慮すべき他の事柄は、あなたや他の開発者が生のデータベースデータを見たいと思うでしょう。その場合、これは別のアプリケーションの使用と見なすことができます(ラボゲージが生のSQLである場合のみ)。

コード内で定義された列挙型(よりクリーンなコードとコンパイル時間のチェック用)とデータベース内のテーブルがある場合、ユニットテストを追加して、2つが同期していることを確認することをお勧めします。

1
Eric Johnson

データにアクセスするユーザーにも依存します。 1つのアプリケーションしかない場合は問題ないかもしれません。データウェアハウスまたはレポートシステムを追加する場合。彼らは、そのコードの意味、人間が編集可能なコードのバージョンを知る必要があります。

通常、型テーブルはコードの列挙型として複製されません。キャッシュされたリストにタイプテーブルをロードできます。

Class GenderList

   Public Shared Property UnfilteredList
   Public Shared Property Male = GetItem("M")
   Public Shared Property Female = GetItem("F")

End Class

多くの場合、タイプは来たり来たりします。新しいタイプが追加された日付が必要です。特定のタイプがいつ削除されたかを知る。必要なときにだけ表示します。クライアントが性別として「トランスジェンダー」を望んでいるが、他のクライアントは望んでいない場合はどうなりますか?この情報はすべてデータベースに保存するのが最適です。

0
the_lotus