【问题标题】:Postgres ENUM data type or CHECK CONSTRAINT?Postgres ENUM 数据类型还是 CHECK CONSTRAINT?
【发布时间】:2012-06-10 23:59:54
【问题描述】:

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

【问题讨论】:

  • 为什么不用PG的枚举类型呢?为什么你认为你需要“模仿”一个?

标签: postgresql postgresql-9.1


【解决方案1】:

在我看来,给定一组相同的值

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

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

【讨论】:

    【解决方案2】:

    根据此处的 cmets 和答案,以及一些初步研究,我有以下摘要可提供给 Postgres-erati 的 cmets。非常感谢您的意见。

    有三种方法可以限制 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 链接到所需的文本值。

    【讨论】:

    • 另外,使用另一个表和外键,您可以将颜色管理委托给普通用户,而无需让他们更改表。
    • 从抽象的角度我看不到 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 表。
    • 尝试:SELECT FROM lower(t::text) WHERE color LIKE 'bl%';这会将列值转换为文本以进行搜索。
    • 我认为选项(2)的缺点是在更新有效枚举值(例如,添加有效值)时需要表锁。
    • @EugenePakhomov 是的,你是对的。更改枚举值非常困难,尤其是重命名和删除值。只有运行良好的操作是附加的。外键没有这样的问题...
    【解决方案3】:

    正如其他答案所指出的,检查约束存在灵活性问题,但在整数 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,更改会自动传播)。

    【讨论】:

    • 这看起来确实是一个非常有趣的方法。有人知道这方面是否有任何已知的缺点吗?
    • 是的,存储所有这些值需要空间,这可能会导致性能问题。但是,由于其简单性,它仍然是一个不错的选择。基本上,它是选项 3“外键”,但不使用 id 进行引用。太棒了!
    • 如果我做对了,请告诉我,它将“值”存储在表 t 中,因此在查找期间无需进行连接,但它也连接到 valid_colors,如果颜色更新、拼写更改,它'd 还用新的拼写更新表 t 中的所有行?
    • 没错 - 如果颜色名称不断变化(因为 t 中的行也将不断更新),那么这并不理想,但如果您正在处理一组小的静态值,那应该不是问题。
    【解决方案4】:

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

    在小型系统中,这没什么大不了的,但如果您在 HR 或类似系统上工作,其中包含非常多的小型查找表,那么这可能是一个非常大的问题,因为为了获取文本而发生了大量的连接。

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

    【讨论】:

      【解决方案5】:

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

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

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

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

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

      顺便说一句,我注意到 MySQL 枚举不需要这种类型的转换,所以根据我的经验,这是 PostgreSQL 特有的。

      【讨论】:

      • ENUM 类型的另一个问题是字符串运算符不起作用。例如,如果我有一个带有文本选项的 ENUM 字段,我不能在其上使用 LIKE。另一方面,使用 CHECK CONSTRAINTS 文本仍将被视为文本。
      • 再次为 PostgreSQL - 同意。我也看到了这一点,应该考虑到我的答案。我也只是在 MySQL 中进行了测试,并且 MySQL 再次没有施加这种限制——你可以使用 LIKE 和枚举。所以我对 PostgreSQL 的投票是避免使用枚举,除非有其他我不知道的充分理由使用它们。
      • 原始声明似乎​​不再有效:::myenum 后缀未在当前 postgres 文档中提及:postgresql.org/docs/current/static/datatype-enum.html
      【解决方案6】:

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

      【讨论】:

      • 我个人更喜欢对这类事情使用检查约束(甚至是带有 FK 约束的查找表)。它使以后更容易更改它们。
      猜你喜欢
      • 2010-10-06
      • 1970-01-01
      • 2012-03-21
      • 2022-12-04
      • 1970-01-01
      • 1970-01-01
      • 2017-07-22
      • 1970-01-01
      • 2012-01-16
      相关资源
      最近更新 更多