web-dev-qa-db-ja.com

enumデータ型を使用する代わりに新しいデータベーステーブルを作成するのは無駄ですか?

私が提供しているサービスには、次の4つのタイプがあるとします(頻繁に変更されることはほとんどありません)。

  • テスト中
  • 設計
  • プログラミング
  • その他の

60〜80の実際のサービスがあり、それぞれが上記のカテゴリのいずれかに該当するとします。たとえば、「サービス」は「テクニックAを使用したテストプログラム」で、タイプは「テスト」です。

それらをデータベースにエンコードしたい。私はいくつかのオプションを考え出しました:

オプション0:

VARCHARを直接使用して、サービスタイプを文字列として直接エンコードします

オプション1:

データベースenumを使用します。しかし、 enum is evil

オプション2:

2つのテーブルを使用します。

service_line_item (id, service_type_id INT, description VARCHAR);
service_type (id, service_type VARCHAR);

参照整合性を楽しむこともできます。

ALTER service_line_item 
    ADD FOREIGN KEY (service_type_id) REFERENCES service_type (id);

いいですね。

しかし、私はまだ物事をエンコードして整数を処理する必要があります。つまり、テーブルにデータを入力するときです。または、テーブルを作成または処理するときに、手の込んだプログラミングまたはDB構造を作成する必要があります。つまり、データベースを直接処理する場合、またはプログラミング側で新しいオブジェクト指向エンティティを作成する場合にJOINを実行し、それらが正しく動作することを確認します。

オプション3:

enumを使用せず、2つのテーブルを使用せず、整数列を使用する

service_line_item (
    id,
    service_type INT,        -- use 0, 1, 2, 3 (for service types)
    description VARCHAR
);

これは、「偽の列挙型」のようなもので、コード側でより多くのオーバーヘッドを必要とします。つまり、{2 == 'Programming'}を認識して適切に処理するなどです。

質問:

現在、私はそれをオプション2を使用して実装しました。

  1. 列挙型を使用しない(オプション1)
  2. データベースをスプレッドシートとして使用しないでください(オプション0)

しかし、プログラミングと認知のオーバーヘッドの点で私にとっては無駄だと思わざるを得ません。2つのテーブルに注意し、1つではなく2つのテーブルを扱う必要があります。

「無駄の少ない方法」として、私はOption 3を見ています。 ITはより軽く、操作には基本的に同じコード構造が必要です(わずかな変更を加えますが、複雑さと構造は基本的に同じですが、単一のテーブルを使用します)

私は理想的にはそれが常に無駄であるとは限らないと思います、そしてどちらのオプションにも良いケースがありますが、いつオプション2を使うべきか、そしてオプション3をいつ使うべきかについて良いガイドラインはありますか?

2種類のみの場合(バイナリ)

この質問にもう少し追加します...同じ場所で、「標準」または「例外」サービスのバイナリオプションがあります。これは、サービスラインアイテムに適用できます。 オプション3を使用してエンコードしました。

値{"Standard"、 "Exception"}を保持するためだけに新しいテーブルを作成しないことにしました。したがって、私の列は{0、1}を保持し、列名はexceptionと呼ばれ、コードは{0, 1} => {STANDARD, EXCEPTION}(プログラミング言語で定数としてエンコードしました)からの変換を行っています

これまでのところ、その方法を好まない.....(オプション2もオプション3も好まない)。オプション2は3より優れていますが、オーバーヘッドが大きく、2と3のどちらのオプションを使用しても、整数としてエンコードすることはできません。

[〜#〜] orm [〜#〜]

いくつかのコンテキストを追加するには、回答を読んだ後、ORMを(最近)使用し始めたところです。私の場合はDoctrine 2.アノテーションを使用してDBスキーマを定義した後、データベースにデータを入力する必要がありました。データセット全体が比較的小さいため、プログラミング構造を使用して、それがどのように機能するかを確認したいと思いました。

実際のスプレッドシートからの既存のリストがあったため、最初にservice_typesを入力し、次にservice_line_itemsを入力しました。したがって、「標準/例外」や「テスト」などはすべてスプレッドシート上の文字列であり、DBに格納する前に適切なタイプにエンコードする必要があります。

私はこれを見つけましたSO答え: doctrine2でENUMの代わりに何を使用しますか? これは、DBの列挙型コンストラクトを使用せず、INTフィールドと、プログラミング言語の「const」構造を使用してタイプをエンコードします。

しかし、上記のSOの質問で指摘されているように、整数を直接使用することを避け、言語構成体(定数)を定義したら使用できます。

しかし、それでも....どのように回しても、stringをタイプとして使用している場合は、ORMを使用している場合でも、最初に適切なタイプに変換する必要があります。

したがって、$str = 'Testing';と言った場合でも、次のような処理を行うブロックが必要です。

switch($str):
{ 
    case 'Testing':  $type = MyEntity::TESTING; break;
    case 'Other':    $type = MyEntity::OTHER; break;
}

良い点は、整数/マジック番号を処理しないことです(代わりに、エンコードされた定数量を処理します)。しかし、悪いことに、この変換手順を実行しないと、データベースに自動的にデータを出し入れできません。知識。

そして、それは、私が「まだ物事をエンコードして整数を処理しなければならない」のようなことを言うことによって部分的に意味したものです。 (ここで、Ocramiusのコメントの後に、私は整数を直接処理する必要はなくなりましたが、名前付き定数と、必要に応じて定数との間の変換を処理します)。

43
Dennis

参照テーブルを使用するオプション#2は、標準的な方法です。何百万人ものプログラマーが使用しており、動作することが知られています。これはパターンなので、あなたの物を見ている人は誰でも何が起こっているのかすぐにわかります。データベースで動作するライブラリとツールが存在し、それを適切に処理するために多くの作業からあなたを救います。それを使用する利点は無数にあります。

もったいない?はい、少しだけです。ある程度まともなデータベースでは、このように頻繁に結合された小さなテーブルが常にキャッシュされているため、通常、無駄はごくわずかです。

MySQLのenumはSQL標準の一部ではないため、あなたが説明した他のすべてのオプションはアドホックでハックです。 (それ以外に、enumで困るのは、MySQLの実装であり、アイデア自体ではありません。ある日、標準の一部としてそれを見て構わないと思います。)

プレーン整数を使用する最後のオプション#3は特に hackyです。すべての世界で最悪の事態が発生します。参照整合性、名前付き値、値の意味に関するデータベース内の決定的な知識はなく、あちこちに投げられた任意の整数です。このトークンにより、コードでの定数の使用をやめて、代わりにハードコードされた値の使用を開始することもできます。 circumference = radius * 6.28318530718;。どのようにそのことについて?

参照テーブルが煩わしい理由を再検討する必要があると思います。私の知る限り、他の誰もそれらを煩わしく感じることはありません。それはあなたがその仕事に適したツールを使っていないからでしょうか?

「物事をエンコードして整数を処理する」、「精巧なプログラミング構造を作成する」、または「プログラミング側で新しいオブジェクト指向エンティティを作成する」必要があるというあなたの文は、おそらくオブジェクトリレーショナルを実行しようとしている可能性があることを示していますオンザフライでマッピング(ORM)をアプリケーションのコード全体に分散させる、または最良のケースでは、Hibernateなどのジョブに既存のORMツールを使用する代わりに、独自のオブジェクトリレーショナルマッピングメカニズムをロールしようとしている可能性があります。これらすべては、Hibernateを使用すると簡単です。習得には少し時間がかかりますが、一度習得すれば、アプリケーションの開発に集中でき、データベース上にあるものを表現する方法の重要なメカニズムを忘れることができます。

最後に、データベースを直接操作するときに生活を楽にしたい場合は、少なくとも2つのことを実行できます。

  1. メインテーブルとそれらが参照する参照テーブルを結合するビューを作成して、各行に参照IDだけでなく、対応する名前も含まれるようにします。

  2. 参照テーブルに整数IDを使用する代わりに、4文字の省略形でCHAR(4)列を使用します。したがって、カテゴリのIDは「TEST」、「DSGN」、「PROG」、「OTHR」になります。 (もちろんdescriptionsは正しい英語の単語のままです。)少し遅くなりますが、私を信じてください。だれも気づかないでしょう。

最後に、タイプが2つしかない場合、ほとんどの人はブール列を使用します。したがって、その「標準/例外」列はブール値として実装され、「IsException」と呼ばれます。

40
Mike Nakis

プログラミング側に定数または列挙型を使用するオプション2。
それは知識を複製しますが、単一の真実の出所の原則に違反しますが、 Fail-fast テクニックを使用することで対処できます。システムがロードされると、enumまたはconst値がデータベースに存在するかどうかがチェックされます。そうでない場合、システムはエラーをスローし、ロードを拒否する必要があります。現時点でこのバグを修正する方が、より深刻な事態が発生した後よりも、通常は安上がりです。

オプション#2は理想的な選択です。オーバーヘッドは、他のオプションを検討する必要があるほどではありません。このオプションを使用すると、データベースは引き続き整理され、理解しやすくなります。

オプション#3はオプション#2より高速ですが、どの整数が何を意味するかを追跡する必要があります。何らかの理由で数値を変更したい場合は、コードの多くの場所でいくつかの変更が必要になる場合があります。プログラマーとして、アーキテクチャーに抜け穴があってはならず、特定のタスクを制御できる最高の場所があるはずです。

0
Ravish

[short]文字列をキーとして使用するのを止めることは何もないので、テーブル内の名前を読みやすくして、意味のないサロゲート番号エンコーディングに頼ることはできません。サービスタイプを説明する別のテーブルがまだあるはずです。たとえば、アプリケーションが国際化するというチャンスはありません。

ユーザーは4つのカテゴリをtheir own言語で表示できますが、データベーステーブルにはyoが読み取ることができる値が含まれており、データベース構造やコードの変更は必要ありません。

table service_type 
( id VARCHAR 
, name VARCHAR 
  primary key ( id ) 
);
table service_line_item 
( id 
, service_type VARCHAR 
, description VARCHAR
  foreign key ( service_type ) references service_type ( id )
);

select * from service_type ; 

+-------------+----------------+
| id          | name           |
+-------------+----------------+
| Testing     | Testen         |
| Design      | Design         | 
| Programming | Programmierung |
| Other       | Andere         |
+-------------+----------------+

または、フランスのお客様の場合...

update services_types set name = 'Essai'         where id = 'Testing'; 
update services_types set name = 'Conception'    where id = 'Design'; 
update services_types set name = 'Programmation' where id = 'Programming'; 
update services_types set name = 'Autre'         where id = 'Other'; 
0
Phill W.