このように、DBに列挙型を保存する方法についてアドバイスを求める質問を数多く目にしました。しかし、なぜそうするのだろうか。したがってPerson
、gender
フィールドとGender
列挙型を持つエンティティがあるとしましょう。次に、個人テーブルには列の性別があります。
正当性を強制する明白な理由に加えてgender
、アプリケーションに既にあるものをマッピングするために余分なテーブルを作成する理由がわかりません。そして、私はその複製を持つことが本当に好きではありません。
このように、DBに列挙型を保存する方法についてアドバイスを求める質問を数多く目にしました。しかし、なぜそうするのだろうか。したがってPerson
、gender
フィールドとGender
列挙型を持つエンティティがあるとしましょう。次に、個人テーブルには列の性別があります。
正当性を強制する明白な理由に加えてgender
、アプリケーションに既にあるものをマッピングするために余分なテーブルを作成する理由がわかりません。そして、私はその複製を持つことが本当に好きではありません。
回答:
構想や期待に満ちた別の例を見てみましょう。ここに列挙型がありますが、これはバグの優先順位のセットです。
だから、私は保存することができ'C'
、'H'
、'M'
、および'L'
データベースインチ または'HIGH'
など。これには、文字列型データの問題があります。有効な値の既知のセットがあり、そのセットをデータベースに保存していない場合、作業が困難になる可能性があります。
あなたは持っているList<String> priorities = {'CRITICAL', 'HIGH', 'MEDIUM', 'LOW'};
コードまたはその効果に何か。これは、このデータの適切な形式へのさまざまなマッピングがあることを意味します(すべてのキャップをデータベースに挿入していますが、それをとして表示していますCritical
)。コードのローカライズも困難になりました。アイデアのデータベース表現を、コードに保存されている文字列にバインドしました。
このリストにアクセスする必要がある場所ならどこでも、コードの複製または定数の束を持つクラスが必要です。どちらも良い選択肢ではありません。また、このデータを使用する可能性のある他のアプリケーションがあることも忘れてはなりません(他の言語で記述されている可能性があります-Java Webアプリケーションには、Crystal Reportsレポートシステムとデータを供給するPerlバッチジョブがあります)。レポートエンジンは有効なデータのリストを知る必要があり('LOW'
優先順位にマークが付けられていない場合、それがレポートの有効な優先順位であることを知る必要がある場合はどうなりますか)、バッチジョブには有効なデータに関する情報が含まれます値は。
仮に、「私たちは単一言語のショップ-すべてがJavaで書かれている」と言って、この情報を含む単一の.jarを持っているかもしれませんが、アプリケーションは互いに密接に結合され、データ。変更があるたびに、レポート部分とバッチ更新部分をWebアプリケーションと一緒にリリースする必要があります。すべての部分でそのリリースがスムーズに進むことを願っています。
あなたの上司が今日来ました。新しい優先度があります- CEO
。次に、すべてのコードを変更して、再コンパイルと再デプロイを行う必要があります。
「enum-in-the-table」アプローチでは、列挙リストを更新して新しい優先順位を設定します。リストを取得するすべてのコードは、データベースからリストを取得します。
優先度を使用すると、データは、ワークフローに関する情報を含む可能性のある他のテーブルにキーを設定したり、この優先度やその他の設定を行うことができるユーザーを決定します。
:性別は、使用中の代名詞へのリンクがあります:ビットのための質問で述べたように、性別に戻ってhe/his/him
とshe/hers/her
...あなたはコード自体にそのコーディングハード避けたいです。そして、あなたの上司がやって来て、あなたが'OTHER'
性別を持っていることを追加する必要があります(シンプルに保つために)、あなたはこの性別を関連付ける必要がthey/their/them
あります...そしてあなたの上司はFacebookが持っているものを見て...ええ、そうです。
列挙テーブルではなく、文字列型のデータに制限することで、データと他のビットとのこの関係を維持するために、他のテーブルにその文字列を複製する必要がありました。
これをどこに保管しても、同じ原則が存在します。
priorities.prop
優先順位のリストを持つファイルを作成できます。このリストは、プロパティファイルから読み取ります。次のエントリを持つドキュメントストアデータベース(CouchDBなど)を持つことができますenums
(そして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
データストアから何が返されるかを知っているので、毎回持っているものを防御的にチェックすることを心配する必要はありません(大文字ですか?それとも小文字ですか?この列にタイプがあるのはなぜですか?など)データストアが送信することを期待しているものとまったく同じです。有効な値のリストをデータストアに照会できます。
有効な値のセットは、コードではなくデータです。あなたはないために努力する必要がDRYコード-しかし、重複の問題は、あなたが複製されていることであるデータをコードではなく、データとしての地位を尊重し、データベースに格納します。
データストアに対して複数のアプリケーションを簡単に記述でき、コードをデータに結合していないため、データ自体に密接に結合されているすべてをデプロイする必要があるインスタンスを回避できます。
CEO
優先度が追加されたときにアプリケーション全体を再テストする必要がないため、アプリケーションのテストが簡単になります。優先度の実際の値を考慮するコードがないためです。
互いに独立してコードとデータについて推論できるので、メンテナンスを行うときにバグを見つけて修正するのが簡単になります。
これらのうち、クエリを読むときに間違いを起こす可能性が高いと思うものはどれですか?
select *
from Person
where Gender = 1
または
select *
from Person join Gender on Person.Gender = Gender.GenderId
where Gender.Label = "Female"
SQLで列挙テーブルを作成するのは、後者の方が読みやすいため、SQLの作成と保守のエラーが少なくなるためです。
で性別を直接文字列にすることもできますが、Person
大文字と小文字を区別して強制する必要があります。また、文字列と整数の違いにより、データベースの最適化の程度に応じて、テーブルのストレージヒットとクエリ時間を増やすことができます。
人々がまだこれについて言及していなかったとは信じられません。
データベースに列挙を保持し、列挙値を含むテーブルに外部キーを追加することにより、コードがその列に誤った値を入力しないようにします。これはデータの整合性に役立ち、列挙型のテーブルが必要な最も明白な理由です。
私はあなたに同意するキャンプにいます。コードに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
これらのクエリはすべて、データベースから列挙定義を保持するという考えに沿った場合、より小さく、より保守しやすくなります。
データ分析で使用できるという理由で、Gendersテーブルを作成します。データベース内のすべての男性または女性を検索して、レポートを生成できます。データを表示する方法が多ければ多いほど、トレンド情報を見つけやすくなります。明らかに、これは非常に単純な列挙ですが、複雑な列挙(世界の国や州など)の場合、特殊なレポートを簡単に生成できます。
最初に、データベースが1つのアプリケーションでのみ使用されるのか、または複数のアプリケーションがデータベースを使用する可能性があるのかを判断する必要があります。場合によっては、データベースはアプリケーションのファイル形式にすぎません(この点でSQLiteデータベースを使用できることがよくあります)。この場合、enum定義をテーブルとしてビット複製することは、多くの場合うまくいく可能性があり、より意味があります。
ただし、複数のアプリケーションがデータベースにアクセスする可能性を検討するとすぐに、enumのテーブルは非常に理にかなっています(他の答えはなぜより詳細になります)。考慮すべきもう1つのことは、あなたや他の開発者が生のデータベースデータを見ることです。その場合、これは別のアプリケーションの使用と見なすことができます(ラボゲージが生のSQLである場合のみ)。
コードで定義された列挙型(よりクリーンなコードとコンパイル時のチェック用)およびデータベース内のテーブルがある場合、ユニットテストを追加して、2つが同期していることを確認することをお勧めします。
コード内のビジネスロジックを駆動するために使用されるコード列挙がある場合でも、上/下で詳述する多くの理由で、DB内のデータを表すテーブルを作成する必要があります。DB値とコード値の同期を維持するためのヒントを次に示します。
テーブルのIDフィールドをID列にしないでください。IDと説明をフィールドとして含めます。
テーブルで何か別のことを行うと、開発者が値が半静的/コード列挙に結び付けられていることを知るのに役立ちます。他のすべてのルックアップテーブル(通常はユーザーが値を追加できる場所)には通常、LastChangedDateTimeとLastChangedByがありますが、enum関連のテーブルにこれらを持たないことは、開発者のみが変更できることを思い出すのに役立ちます。これを文書化します。
列挙内の各値が対応するテーブルにあり、それらの値のみが対応するテーブルにあることを確認する確認コードを作成します。ビルド後に実行する自動化されたアプリケーション「ヘルステスト」がある場合は、そこで実行します。そうでない場合は、アプリケーションがIDEで実行されるたびに、アプリケーションの起動時にコードが自動的に実行されるようにします。
本番環境を作成し、同じことを行いますが、DB内からSQLスクリプトを配信します。正しく作成されていれば、環境の移行にも役立ちます。
データにアクセスするユーザーにも依存します。アプリケーションが1つだけの場合は問題ないかもしれません。データウェアハウスまたはレポートシステムに追加する場合。彼らは、そのコードが何を意味するのか、人間がコードを変更できるバージョンは何であるのかを知る必要があります。
通常、型テーブルは、コード内で列挙型として複製されません。キャッシュされたリストにタイプテーブルをロードできます。
Class GenderList
Public Shared Property UnfilteredList
Public Shared Property Male = GetItem("M")
Public Shared Property Female = GetItem("F")
End Class
多くの場合、タイプは行き来します。新しいタイプが追加された日付が必要になります。特定のタイプがいつ削除されたかを知る。必要な場合にのみ表示します。クライアントが性別として「トランスジェンダー」を望んでいるが、他のクライアントは望んでいない場合はどうなりますか?これらの情報はすべてデータベースに最適に保存されます。