web-dev-qa-db-ja.com

Postgres ENUMデータ型またはCHECK CONSTRAINT?

MySQL dbをPg(9.1)に移行し、Pgで新しいデータ型を作成し、それを列定義として使用して、MySQL ENUMデータ型をエミュレートしました。私の質問-私は代わりにCHECK CONSTRAINTを使用する方が良いでしょうか? MySQL ENUMタイプは、行の特定の値エントリを強制するために実装されています。 CHECK CONSTRAINTでそれを実行できますか?そして、はいの場合、それはより良い(または悪い)でしょうか?

40
punkish

ここでのコメントと回答、およびいくつかの初歩的な研究に基づいて、Postgres-eratiからのコメントを提供するために、次の要約を用意しています。あなたの入力に本当に感謝します。

Postgresデータベーステーブルの列のエントリを制限するには、3つの方法があります。 「赤」、「緑」、または「青」のみを有効なエントリにする「色」を保存するテーブルを考えてください。

  1. 列挙データ型

    CREATE TYPE valid_colors AS ENUM ('red', 'green', 'blue');
    
    CREATE TABLE t (
        color VALID_COLORS
    );
    

    利点は、タイプを一度定義してから、必要な数のテーブルで再利用できることです。標準クエリは、ENUMタイプのすべての値をリストでき、アプリケーションフォームウィジェットの作成に使用できます。

    SELECT  n.nspname AS enum_schema,  
            t.typname AS enum_name,  
            e.enumlabel AS enum_value
    FROM    pg_type t JOIN 
            pg_enum e ON t.oid = e.enumtypid JOIN 
            pg_catalog.pg_namespace n ON n.oid = t.typnamespace
    WHERE   t.typname = 'valid_colors'
    
     enum_schema | enum_name     | enum_value 
    -------------+---------------+------------
     public      | valid_colors  | red
     public      | valid_colors  | green
     public      | valid_colors  | blue
    

    欠点は、ENUMタイプがシステムカタログに保存されるため、その定義を表示するには上記のクエリが必要になることです。これらの値は、テーブル定義を表示するときに明らかではありません。また、ENUM型は実際には組み込みのNUMERICおよびTEXTデータ型とは別のデータ型であるため、通常の数値および文字列演算子と関数は機能しません。そのため、次のようなクエリを実行することはできません

    SELECT FROM t WHERE color LIKE 'bl%'; 
    
  2. チェック制約

    CREATE TABLE t (
        colors TEXT CHECK (colors IN ('red', 'green', 'blue'))
    );
    

    2つの利点は、1つは「表示されるものを取得する」ことです。つまり、列の有効な値はテーブル定義に直接記録され、2つはすべてのネイティブ文字列または数値演算子が機能します。

  3. 外部キー

    CREATE TABLE valid_colors (
        id SERIAL PRIMARY KEY NOT NULL,
        color TEXT
    );
    
    INSERT INTO valid_colors (color) VALUES 
        ('red'),
        ('green'),
        ('blue');
    
    CREATE TABLE t (
        color_id INTEGER REFERENCES valid_colors (id)
    );
    

    基本的にENUMタイプの作成と同じですが、ネイティブの数値演算子または文字列演算子が機能し、有効な値を検出するためにシステムカタログを照会する必要はありません。 color_idを目的のテキスト値にリンクするには、結合が必要です。

67
punkish

他の回答が指摘しているように、チェック制約には柔軟性の問題がありますが、整数IDに外部キーを設定するには、ルックアップ中に参加する必要があります。許可された値を参照テーブルの自然キーとして使用しないのはなぜですか?

punkish's answer からスキーマを適応させるには:

CREATE TABLE valid_colors (
    color TEXT PRIMARY KEY
);

INSERT INTO valid_colors (color) VALUES 
    ('red'),
    ('green'),
    ('blue');

CREATE TABLE t (
    color TEXT REFERENCES valid_colors (color) ON UPDATE CASCADE
);

値はチェック制約の場合のようにインラインで格納されるため、結合はありませんが、新しい有効な値オプションを簡単に追加でき、既存の値インスタンスはON UPDATE CASCADEを介して更新できます。 「赤」、valid_colorsを適宜更新すると、変更が自動的に反映されます)。

13
Gord Stephen

PostgreSQLには enum types があり、正常に機能します。列挙型が制約よりも「優れている」かどうかはわかりませんが、両方とも機能します。

3
Frank Heikens

外部キーとチェック制約の大きな短所の1つは、レポートまたはUIディスプレイが結合を実行してIDをテキストに解決する必要があることです。

小規模なシステムではこれは大したことではありませんが、非常に多くの小さなルックアップテーブルを持つHRまたは同様のシステムで作業している場合、これはテキストを取得するためだけに多くの結合が行われる非常に大きなことです。

私の推奨事項は、値がほとんどなく、ほとんど変更されない場合は、テキストフィールドに制約を使用し、そうでない場合は整数IDフィールドに対してルックアップテーブルを使用することです。

1
tomo7

PostgreSQLデータベース側から、一方が他方よりも好まれる理由についての良い答えを誰かが聞いてくれることを期待しています。

ソフトウェア開発者の観点から見ると、PostgreSQL列挙型では、次のような更新/挿入を行うためにSQLでキャストする必要があるため、チェック制約を使用することが少し好みです。

INSERT INTO table1 (colA, colB) VALUES('foo', 'bar'::myenum)

ここで、「myenum」は、PostgreSQLで指定した列挙型です。

これは確かにSQLを移植性のないものにします(ほとんどの人にとっては大したことではないかもしれません)が、アプリケーションの開発中に対処しなければならないもう一つのことでもあるので、チェック付きのVARCHAR(または他の典型的なプリミティブ)を持つことを好みます制約。

補足説明として、MySQL列挙型はこのタイプのキャストを必要としないことに気づいたので、これは私の経験ではPostgreSQLに特有のものです。

0
quux00