92

我一直在将 MySQL db 迁移到 Pg (9.1),并通过在 Pg 中创建新的数据类型,然后将其用作列定义来模拟 MySQL ENUM 数据类型。我的问题——我可以使用 CHECK CONSTRAINT 代替吗?实现 MySQL ENUM 类型以强制行中的特定值条目。这可以通过检查约束来完成吗?如果是的话,会更好(或更糟)吗?

4

6 回答 6

153

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

有三种方法可以限制 Postgres 数据库表列中的条目。考虑一个存储“颜色”的表,您希望仅将“红色”、“绿色”或“蓝色”作为有效条目。

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

    两个优点是,第一,“所见即所得”,即列的有效值直接记录在表定义中,第二,所有本机字符串或数字运算符都有效。

  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到所需的文本值。

于 2012-06-11T17:42:16.803 回答
40

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

朋克的回答中调整架构:

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因此,更改会自动传播)。

于 2017-01-14T21:39:03.920 回答
5

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

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

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

于 2015-05-23T18:47:40.697 回答
3

PostgreSQL 有enum types,可以正常工作。我不知道枚举是否比约束“更好”,它们都可以工作。

于 2012-06-08T05:01:28.257 回答
3

从我的角度来看,给定一组相同的值

  1. 如果您将在多列上使用枚举,那么它是一个更好的解决方案
  2. 如果您想限制应用程序中只有一列的值,检查约束是更好的解决方案。

当然,还有很多其他参数可能会在您的决策过程中蔓延(通常是内置运算符不可用的事实),但我认为这两个是最普遍的参数。

于 2019-09-16T15:07:50.983 回答
2

我希望有人能从 PostgreSQL 数据库方面给出一个很好的答案,说明为什么一个可能比另一个更可取。

从软件开发人员的角度来看,我稍微倾向于使用检查约束,因为 PostgreSQL 枚举需要在您的 SQL 中进行强制转换才能进行更新/插入,例如:

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

其中“myenum”是您在 PostgreSQL 中指定的枚举类型。

这当然使 SQL 不可移植(这对大多数人来说可能没什么大不了的),但也是您在开发应用程序时必须处理的另一件事,所以我更喜欢使用带有检查的 VARCHAR(或其他典型原语)约束。

作为旁注,我注意到 MySQL 枚举不需要这种类型的强制转换,所以根据我的经验,这是 PostgreSQL 特有的。

于 2012-06-09T17:54:29.707 回答