web-dev-qa-db-fra.com

Postgres ENUM type de données ou CHECK CONSTRAINT?

J'ai migré une base de données MySQL vers Pg (9.1) et j'ai émulé les types de données MySQL ENUM en créant un nouveau type de données dans Pg, puis en l'utilisant comme définition de colonne. Ma question - pourrais-je, et serait-il préférable d'utiliser à la place une CONTROLE DE VERIFICATION? Les types MySQL ENUM sont implémentés pour appliquer des entrées de valeurs spécifiques dans les lignes. Cela pourrait-il être fait avec une CONTROLE DE CONTROLE? et, si oui, serait-ce mieux (ou pire)?

40
punkish

Sur la base des commentaires et des réponses ici, et de quelques recherches rudimentaires, j'ai le résumé suivant à offrir pour les commentaires des Postgres-erati. J'apprécierai vraiment votre contribution.

Il existe trois façons de restreindre les entrées dans une colonne de table de base de données Postgres. Considérez un tableau pour stocker les "couleurs" où vous voulez que seuls "rouge", "vert" ou "bleu" soient des entrées valides.

  1. Type de données énuméré

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

    Les avantages sont que le type peut être défini une fois, puis réutilisé dans autant de tableaux que nécessaire. Une requête standard peut répertorier toutes les valeurs d'un type ENUM et peut être utilisée pour créer des widgets de formulaire de demande.

    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
    

    Les inconvénients sont que le type ENUM est stocké dans les catalogues système, donc une requête comme ci-dessus est nécessaire pour afficher sa définition. Ces valeurs ne sont pas apparentes lors de l'affichage de la définition de table. Et, comme un type ENUM est en fait un type de données distinct des types de données NUMERIC et TEXT intégrés, les opérateurs et fonctions numériques et de chaîne standard ne fonctionnent pas. Donc, on ne peut pas faire une requête comme

    SELECT FROM t WHERE color LIKE 'bl%'; 
    
  2. Vérifier les contraintes

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

    Deux avantages sont que, un, "ce que vous voyez est ce que vous obtenez", c'est-à-dire que les valeurs valides pour la colonne sont enregistrées directement dans la définition de la table, et deux, toutes les chaînes natives ou les opérateurs numériques fonctionnent.

  3. Clés étrangères

    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)
    );
    

    Essentiellement identique à la création d'un type ENUM, sauf que les opérateurs numériques ou de chaîne natifs fonctionnent, et il n'est pas nécessaire d'interroger les catalogues système pour découvrir les valeurs valides. Une jointure est requise pour lier le color_id à la valeur de texte souhaitée.

67
punkish

Comme le soulignent d'autres réponses, les contraintes de vérification ont des problèmes de flexibilité, mais la définition d'une clé étrangère sur un identifiant entier nécessite de se joindre lors des recherches. Pourquoi ne pas simplement utiliser les valeurs autorisées comme clés naturelles dans la table de référence?

Pour adapter le schéma de réponse de punkish :

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
);

Les valeurs sont stockées en ligne comme dans le cas de contrainte de vérification, donc il n'y a pas de jointures, mais de nouvelles options de valeurs valides peuvent être facilement ajoutées et les instances de valeurs existantes peuvent être mises à jour via ON UPDATE CASCADE (par exemple, s'il est décidé que "rouge" devrait en fait être "rouge", mettez à jour valid_colors en conséquence et le changement se propage automatiquement).

13
Gord Stephen

PostgreSQL a types enum , fonctionne comme il se doit. Je ne sais pas si une énumération est "meilleure" qu'une contrainte, elles fonctionnent toutes les deux.

3
Frank Heikens

L'un des gros inconvénients des clés étrangères par rapport aux contraintes de vérification est que tout rapport ou affichage d'interface utilisateur devra effectuer une jointure pour résoudre l'ID dans le texte.

Dans un petit système, ce n'est pas un gros problème, mais si vous travaillez sur un système RH ou similaire avec de très nombreuses petites tables de recherche, cela peut être très important avec de nombreuses jointures qui ont lieu juste pour obtenir le texte.

Ma recommandation serait que si les valeurs sont peu nombreuses et changent rarement, alors utilisez une contrainte sur un champ de texte sinon utilisez une table de recherche par rapport à un champ d'ID entier.

1
tomo7

J'espère que quelqu'un répondra avec une bonne réponse du côté de la base de données PostgreSQL pour expliquer pourquoi l'un pourrait être préférable à l'autre.

Du point de vue d'un développeur de logiciels, j'ai une légère préférence pour l'utilisation des contraintes de vérification, car les énumérations de PostgreSQL nécessitent un transtypage dans votre SQL pour effectuer une mise à jour/insertion, comme:

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

où "myenum" est le type d'énumération que vous avez spécifié dans PostgreSQL.

Cela rend certainement SQL non portable (ce qui n'est peut-être pas un gros problème pour la plupart des gens), mais c'est aussi une autre chose à laquelle vous devez faire face lors du développement d'applications, donc je préfère avoir des VARCHARs (ou d'autres primitives typiques) avec vérification contraintes.

En remarque, j'ai remarqué que les énumérations MySQL ne nécessitent pas ce type de conversion, c'est donc quelque chose de particulier à PostgreSQL dans mon expérience.

0
quux00