【问题标题】:SQL: Advantages of an ENUM vs. a one-to-many relationship?SQL:ENUM 与一对多关系的优势?
【发布时间】:2011-05-16 15:52:15
【问题描述】:

我很少看到 ENUM 数据类型被广泛使用;开发人员几乎总是只使用如下所示的辅助表:

CREATE TABLE officer_ranks (
id int PRIMARY KEY
,title varchar NOT NULL UNIQUE);
INSERT INTO officer_ranks VALUES (1,'2LT'),(2,'1LT'),(3,'CPT'),(4,'MAJ'),(5,'LTC'),(6,'COL'),(7,'BG'),(8,'MG'),(9,'LTG'),(10,'GEN');

CREATE TABLE officers (
solider_name varchar NOT NULL
,rank int NOT NULL REFERENCES officer_ranks(id) ON DELETE RESTRICT
,serial_num varchar PRIMARY KEY);

但同样的事情也可以使用用户定义的类型 / ENUM 来显示:

CREATE TYPE officer_rank AS ENUM ('2LT', '1LT','CPT','MAJ','LTC','COL','BG','MG','LTG','GEN');
    
CREATE TABLE officers (
solider_name varchar NOT NULL
,rank officer_rank NOT NULL
,serial_num varchar PRIMARY KEY);

(示例使用 PostgreSQL,但其他 RDBMS 的语法类似)

我看到使用 ENUM 的最大缺点是从应用程序中更新更加困难。它还可能会使习惯于将 SQL DB 仅用作比特桶的经验不足的开发人员感到困惑。

假设信息大部分是静态的(工作日名称、月份名称、美国陆军军衔等),使用 ENUM 是否有任何优势?

【问题讨论】:

标签: sql database-design postgresql


【解决方案1】:

示例使用 PostgreSQL,但其他 RDBMS 的语法类似

这是不正确的。它不是 ISO/IEC/ANSI SQL 要求,因此商业数据库不提供它(您应该提供查找表)。小镇的小端执行各种“附加”,但不执行小镇的更严格的要求或咕噜声。

我们也没有 ENUM 作为 DataType 的一部分,这太荒谬了。

ENUM 的第一个缺点是它非标准,因此不可移植。

ENUM 的第二大缺点是数据库已关闭。可以在数据库(独立于应用程序)上使用的数百个报告工具无法找到它们,因此无法投影名称/含义。如果你有一个普通的标准 SQL 查找表,这个问题就解决了。

第三个是,当你改变值时,你必须改变 DDL。在普通标准 SQL 数据库中,您只需在查找表中插入/更新/删除一行。

最后,你不能轻易得到 ENUM 的内容列表;您可以使用查找表。更重要的是,您有一个向量来执行任何 Dimension-Fact 查询,无需从大型 Fact 表和 GROUP BY 中进行选择。

【讨论】:

    【解决方案2】:

    我认为使用 ENUMS 没有任何优势。

    它们更难维护,并且不提供具有适当外键的常规查找表不允许您做的任何事情。

    【讨论】:

    • 老实说:我不知道。甚至 Postgres 开发人员也经常在邮件列表中承认它们并没有真正的用处
    【解决方案3】:

    使用 ENUM 之类的东西的一个缺点是,如果数据表中不存在所有可用值,则无法获取它们的列表,除非您在某处对可用值列表进行硬编码。例如,如果在您的 OFFICERS 表中您碰巧没有 MG 在帖子中,则无法知道该等级的存在。因此,当 BG Blowhard 被 MG Marjorie-Banks 解除职务时,您将无法进入新军官的军衔——这是一种耻辱,因为他是现代少将的典范。 :-) 当陆军上将(五星上将)出现时会发生什么?

    对于不会改变的简单类型,我已成功使用域。例如,在我的一个数据库中,我有一个 yes_no_domain 定义如下:

    CREATE DOMAIN yes_no_dom
      AS character(1)
      DEFAULT 'N'::bpchar
      NOT NULL
       CONSTRAINT yes_no_dom_check
         CHECK ((VALUE = ANY (ARRAY['Y'::bpchar, 'N'::bpchar])));
    

    分享和享受。

    【讨论】:

    • \dT+ mytype 将显示所有可能性(它们也在系统表中。)
    • 如果您想在 postgresql 中获得更可解析的输出,您可以通过这种方式使用 enum_range:select enum_range(null::your_enum); 这将为您提供作为数组的值。如果您更关心类似记录的输出,请将其与 unnest 结合使用:select unnest(enum_range(null::your_enum));
    • 您可以使用 SELECT enum_range(NULL::myenum) 列出 ENUM 的所有可能值。另请参阅:stackoverflow.com/questions/9540681/list-postgres-enum-type您的域实际上是一个 ENUM .....
    【解决方案4】:

    一个小的优势可能在于,您在创建 ENUM 时有一种 UDT。用户定义的类型可以在许多其他数据库对象中正式重用,例如在视图、其他表、其他类型、存储过程(在其他 RDBMS 中)等。

    另一个优点是记录字段的允许值。例子:

    • 是/否字段
    • 男性/女性领域
    • mr/mrs/ms/dr 字段

    可能是口味问题。对于这些类型的字段,我更喜欢使用 ENUM,而不是使用外键来查找此类简单概念的表。

    另一个优势可能是,当您在 Java 中使用代码生成或 ORM(如 jOOQ)时,您可以使用该 ENUM 从中生成 Java 枚举类,而不是加入查找表,或使用 ENUM 字面量身份证

    事实上,只有少数 RDBMS 支持正式的 ENUM 类型。我只知道 Postgres 和 MySQL。 Oracle 或 DB2 没有。

    【讨论】:

    • 另外,这是stackoverflow.com/questions/2318123/…的副本
    • 在我看来,yes/no 最好使用布尔列。 mr/mrs/dr 之类的东西很可能无论如何都需要本地化,如果使用查找表完成(如果在数据库中完成本地化)会更容易。
    • ENUM 的问题在于正确识别值何时真正是静态的,例如,当需要支持第二语言的输出时,即使是简单的是/否或工作日 ENUM 也会导致不必要的问题。跨度>
    • @ChristofferBubach:我不明白这是个问题。文字只是一种编码。翻译可以很容易地存储在其他任何地方,包括具有枚举主键的查找表。但可以肯定的是,您始终可以返回将代码存储为文本值并使用普通规范化来编码您的枚举。
    【解决方案5】:

    ENUMS 非常非常非常有用!你只需要知道如何使用它们:

    1. 一个 ENUM 只使用 2 个字节的存储空间。
    2. 不需要额外的约束(作为 FK 的替代品)。
    3. 与 FK 中的自然值相比,值的变化更便宜。
    4. 无需额外的 JOIN
    5. ENUM 已订购,例如您可以比较周一

    因此,如果您有一个要使用的固定字符串值列表,那么与查找表相比,ENUM 是更好的解决方案。假设您需要列出产品中的氨基酸及其各自的重量。今天有约 20 种氨基酸。如果您要存储他们的全名,则每次需要更多的空间,然后是 2 个字节。另一种选择是使用人工键并链接到外部表。但是foreign Table 会是什么样子呢?它会有 2 列:ID 和氨基酸名称吗?你每次都会加入那张桌子吗?如果您的主表有 >40 个这样的字段怎么办?查询该表将涉及 >40 个连接。

    如果您的数据库托管 1600 个表,其中 400 个是查找表,它们只是替换了 ENUM,那么您的开发人员将浪费大量时间来浏览它们(除了 JOIN)。是的,您可以使用前缀、模式等......但为什么不直接踢出这些表呢?

    ENUMS 是枚举列表/有序。这意味着,如果您有已排序的值,您实际上省去了维护 3 列查找表的麻烦。

    问题是:那我为什么需要查找表? 嗯,答案很简单:

    1. 当你的价值观经常变化时
    2. 当您需要存储更多附加属性时 --> 查找表对应于完整的数据对象,而不是查找列表。
    3. 当你需要它时又快又脏

    现在有趣的是: 查找表和 ENUMS 不能完全替代彼此!!!! 如果你有一个列表,其中 PK 是单列自然键。列表可以增长或者值可以更改它们的名称(出于某种原因),然后您可以定义一个 ENUM 并将其用于两者:查找中的 PK 和主表中的 FK!

    示例优势: 您必须更改查找键的名称。如果不使用 ENUM,DBMS 将不得不将更改级联到所有表,在这些表中您使用此值,而不仅仅是您的查找表。如果您使用的是 ENUM,那么您只需更改 ENUM 的值,并不会更改数据。

    【讨论】:

      【解决方案6】:

      优点:

      • 存储过程的类型安全:如果无法将参数强制转换为类型,则会引发类型错误。比如:select court_martial('3LT') 会自动引发类型错误。

      • 自定义联盟顺序:在您的示例中,可以在没有排名 ID 的情况下对军官进行排序。

      【讨论】:

        【解决方案7】:

        一般来说,枚举更适合变化不大的事物,并且它使用的资源略少,因为没有 FK 检查或类似在插入时执行的任何操作等。

        使用查找表更优雅或更传统,并且比枚举更容易添加和删除选项。批量更改值也比枚举更容易。

        【讨论】:

          【解决方案8】:

          嗯,你看不到,因为通常开发人员在 Java 等编程语言中使用枚举,而在数据库设计中没有对应的。

          在数据库中,此类枚举通常是文本或整数字段,没有约束。数据库枚举不会被翻译成 Java/C#/等。枚举,因此开发人员对此没有任何收获。

          有很多非常好的数据库功能很少使用,因为大多数 ORM 工具太原始而无法支持它们。

          【讨论】:

          • 我不会确切地说大多数 ORM 太原始而无法支持它们。否则,您将不得不将 Doctrine(作为许多大的示例)称为原始。我宁愿说不支持它们是完全正确的,因为 ENUM 不是 SQL 标准,而且在 MySQL 之外,没有多少其他 DBMS 对它有原生支持。 PostgreSQL、MariaDB 和 Drizzle 是我所知道的仅有的三个。
          【解决方案9】:

          枚举相对于查找表的另一个好处是,当您编写 SQL 函数时,您可以进行类型检查。

          【讨论】:

            猜你喜欢
            • 2013-11-20
            • 2015-11-25
            • 2018-03-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-06-14
            • 2011-06-22
            • 2018-11-02
            相关资源
            最近更新 更多