我一直在将 MySQL db 迁移到 Pg (9.1),并通过在 Pg 中创建新的数据类型,然后将其用作列定义来模拟 MySQL ENUM 数据类型。我的问题——我可以使用 CHECK CONSTRAINT 代替吗?实现 MySQL ENUM 类型以强制行中的特定值条目。这可以通过检查约束来完成吗?如果是的话,会更好(或更糟)吗?
6 回答
根据这里的评论和答案,以及一些初步的研究,我有以下摘要供 Postgres-erati 评论。非常感谢您的意见。
有三种方法可以限制 Postgres 数据库表列中的条目。考虑一个存储“颜色”的表,您希望仅将“红色”、“绿色”或“蓝色”作为有效条目。
枚举数据类型
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%';
检查约束
CREATE TABLE t ( colors TEXT CHECK (colors IN ('red', 'green', 'blue')) );
两个优点是,第一,“所见即所得”,即列的有效值直接记录在表定义中,第二,所有本机字符串或数字运算符都有效。
外键
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
到所需的文本值。
正如其他答案指出的那样,检查约束存在灵活性问题,但是在整数 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
因此,更改会自动传播)。
外键与检查约束的一大缺点是任何报告或 UI 显示都必须执行连接才能将 id 解析为文本。
在一个小型系统中,这没什么大不了的,但是如果您正在使用具有很多小型查找表的 HR 或类似系统,那么这可能是一个非常大的问题,因为为了获取文本而发生了很多连接。
我的建议是,如果值很少且很少更改,则对文本字段使用约束,否则对整数 id 字段使用查找表。
PostgreSQL 有enum types,可以正常工作。我不知道枚举是否比约束“更好”,它们都可以工作。
从我的角度来看,给定一组相同的值
- 如果您将在多列上使用枚举,那么它是一个更好的解决方案
- 如果您想限制应用程序中只有一列的值,检查约束是更好的解决方案。
当然,还有很多其他参数可能会在您的决策过程中蔓延(通常是内置运算符不可用的事实),但我认为这两个是最普遍的参数。
我希望有人能从 PostgreSQL 数据库方面给出一个很好的答案,说明为什么一个可能比另一个更可取。
从软件开发人员的角度来看,我稍微倾向于使用检查约束,因为 PostgreSQL 枚举需要在您的 SQL 中进行强制转换才能进行更新/插入,例如:
INSERT INTO table1 (colA, colB) VALUES('foo', 'bar'::myenum)
其中“myenum”是您在 PostgreSQL 中指定的枚举类型。
这当然使 SQL 不可移植(这对大多数人来说可能没什么大不了的),但也是您在开发应用程序时必须处理的另一件事,所以我更喜欢使用带有检查的 VARCHAR(或其他典型原语)约束。
作为旁注,我注意到 MySQL 枚举不需要这种类型的强制转换,所以根据我的经验,这是 PostgreSQL 特有的。