Postgres ENUM数据类型还是CHECK CONSTRAINT?

pun*_*ish 44 postgresql postgresql-9.1

我一直在将MySQL数据库迁移到Pg(9.1),并且已经通过在Pg中创建新数据类型来模拟MySQL ENUM数据类型,然后将其用作列定义.我的问题 - 我可以使用CHECK CONSTRAINT而且会更好吗?实现MySQL ENUM类型以强制行中的特定值条目.可以用CHECK CONSTRAINT完成吗?如果是,它会更好(或更糟)吗?

pun*_*ish 72

根据这里的评论和答案,以及一些初步的研究,我有以下摘要来提供Postgres-erati的评论.非常感谢您的投入.

有三种方法可以限制Postgres数据库表列中的条目.考虑一个表来存储"颜色",您只希望"红色","绿色"或"蓝色"成为有效条目.

  1. 枚举数据类型

    CREATE TYPE valid_colors AS ENUM ('red', 'green', 'blue');
    
    CREATE TABLE t (
        color VALID_COLORS
    );
    
    Run Code Online (Sandbox Code Playgroud)

    优点是类型可以定义一次,然后根据需要在尽可能多的表中重用.标准查询可以列出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
    
    Run Code Online (Sandbox Code Playgroud)

    缺点是,ENUM类型存储在系统目录中,因此需要上述查询来查看其定义.查看表定义时,这些值不明显.并且,由于ENUM类型实际上是与内置NUMERIC和TEXT数据类型分开的数据类型,因此常规数字和字符串运算符和函数不起作用.所以,不能做一个像查询

    SELECT FROM t WHERE color LIKE 'bl%'; 
    
    Run Code Online (Sandbox Code Playgroud)
  2. 检查约束

    CREATE TABLE t (
        colors TEXT CHECK (colors IN ('red', 'green', 'blue'))
    );
    
    Run Code Online (Sandbox Code Playgroud)

    两个优点是,一,"你看到的就是你得到的",也就是说,列的有效值记录在表定义中,两个,所有本地字符串或数字运算符都有效.

  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)
    );
    
    Run Code Online (Sandbox Code Playgroud)

    本质上与创建ENUM类型相同,除了本机数字或字符串运算符工作,并且不必查询系统目录来发现有效值.需要连接才能链接color_id到所需的文本值.

  • 此外,使用另一个表和外键,您可以将颜色管理委派给普通用户,而不必让他们更改表. (5认同)
  • 从*抽象角度*我看不到 1) 和 3) 之间的差异。想法是相同的,差异在于 PG 创建了 REF 表。无论哪种方式,我都可以轻松地从该 REF 表中选择/查看/更改有效选项。不要忘记,根据数据库中的可选值在 UI 中动态创建有效选项列表会很巧妙。2) 就不那么简单了。非常简单,1)..或3)使用类似的东西: CREATE VIEW vw_enums AS SELECT t.typname, e.enumlabel, e.enumsortorder FROM pg_enum e JOIN pg_type t ON e.enumtypid = t.oid; 为了可移植性,此视图可以轻松转储以用作 REF 表。 (2认同)
  • 尝试:SELECT FROM lower(t :: text)WHERE color LIKE'b1%'; 这会将列值转换为文本以进行搜索. (2认同)

Gor*_*hen 17

正如其他答案所指出的,检查约束具有灵活性问题,但是在整数id上设置外键需要在查找期间加入.为什么不在参考表中使用允许的值作为自然键?

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
);
Run Code Online (Sandbox Code Playgroud)

值与检查约束情况一样以内联方式存储,因此没有连接,但可以轻松添加新的有效值选项,并且可以通过更新现有值实例ON UPDATE CASCADE(例如,如果确定"红色"实际应该是"红色",则更新valid_colors相应地,变化自动传播).

  • 是的,存储所有这些值需要空间,这可能会导致性能问题。然而,由于其简单性,它仍然是一个不错的选择。基本上,它是选项 3“外键”,但不使用 id 进行引用。杰出的! (2认同)

小智 5

外键与检查约束的一大缺点是任何报告或 UI 显示都必须执行连接以将 id 解析为文本。

在一个小型系统中,这不是什么大问题,但是如果您正在使用具有很多小型查找表的 HR 或类似系统,那么这可能是一个非常大的问题,因为为了获取文本而进行了大量连接。

我的建议是,如果值很少且很少更改,则对文本字段使用约束,否则对整数 id 字段使用查找表。