【问题标题】:How to handle enumerations without enum fields in a database?如何处理数据库中没有枚举字段的枚举?
【发布时间】:2009-04-17 16:46:38
【问题描述】:

如何在不支持枚举的数据库中实现枚举字段? (即 SQLite)

需要使用“field = ?”轻松搜索这些字段所以使用任何类型的数据序列化都是一个坏主意。

【问题讨论】:

  • 如果这是重复的,请善待。在我问之前,我确实在 StackOverflow 上寻找过答案。

标签: database-design enums


【解决方案1】:

查找表使用外键是我使用的方法。事实上,即使我使用支持 ENUM 的数据库(例如 MySQL),我也会使用它。

为简单起见,我可能会跳过查找表中一直存在的“id”,而只使用主表中我需要的实际值作为查找表的主键。这样您就不需要进行连接来获取值。

CREATE TABLE BugStatus (
  status            VARCHAR(20) PRIMARY KEY
);

INSERT INTO BugStatus (status) VALUES ('NEW'), ('OPEN'), ('FIXED');

CREATE TABLE Bugs (
  bug_id            SERIAL PRIMARY KEY,
  summary           VARCHAR(80),
  ...
  status            VARCHAR(20) NOT NULL DEFAULT 'NEW',
  FOREIGN KEY (status) REFERENCES BugStatus(status)
);

诚然,存储字符串比 MySQL 的 ENUM 实现占用更多空间,但除非相关表有数百万行,否则几乎没有关系。

查找表的其他优点是您可以使用简单的INSERTDELETE 在列表中添加或删除值,而使用ENUM 您必须使用ALTER TABLE 重新定义列表。

还可以尝试在ENUM 中查询当前允许值列表,例如在您的用户界面中填充选择列表。这是一个很大的烦恼!使用查找表,很容易:SELECT status from BugStatus

如果需要,您还可以将其他属性列添加到查找表中(例如,标记仅对管理员可用的选项)。在ENUM 中,您不能注释条目;它们只是简单的值。

除了查找表之外的另一个选项是使用 CHECK 约束(前提是数据库支持它们——MySQL 直到版本 8.0.16 才支持 CHECK):

CREATE TABLE Bugs (
  bug_id            SERIAL PRIMARY KEY,
  summary           VARCHAR(80),
  ...
  status            VARCHAR(20) NOT NULL
    CHECK (status IN ('NEW', 'OPEN', 'FIXED'))
);

但是CHECK 约束的这种使用具有与ENUM 相同的缺点:没有ALTER TABLE 难以更改值列表,难以查询允许值列表,难以注释值。

PS:SQL 中的相等比较运算符是单个=。双 == 在 SQL 中没有任何意义。

【讨论】:

  • 感谢 = vs. == 事情 :) 我已经有一段时间没有直接使用 SQL 了。
  • 一个有点老的问题,但答案很棒。投票。
  • 值得注意的一点是,如果您愿意/有兴趣使用 INNODB 而不是 MyISAM,这仅适用于 MySQL。
  • @mOrloff:是的,InnoDB 支持外键约束,但 MyISAM 不支持。出于多种原因,使用 InnoDB 代替 MyISAM 绝对是最佳实践,这只是其中之一。
  • 最佳答案,10 年后,来自我的 +1
【解决方案2】:

为了限制可能的值,我将对包含枚举项的表使用外键。

如果您不想 JOIN 进行搜索,则将键设为 varchar 如果 JOINS 没有问题,然后将键设为 INT 并且不要加入,除非您需要在该字段上进行搜索。

请注意,将您的枚举放在数据库中会排除在编译时检查代码中的值(除非您在代码中重复枚举。)我发现这是一个很大的缺点。

【讨论】:

    【解决方案3】:

    你基本上有两种选择:

    • 使用整数字段

    • 使用 varchar 字段

    我个人会提倡使用 varchars,因为如果你改变你的枚举,你不会破坏任何东西 + 字段是人类可读的,但是 int 也有一些优点,即性能(数据的大小是显而易见的例子)

    【讨论】:

      【解决方案4】:

      这是我最近做的

      在我的休眠映射 POJO 中,我将成员的类型保持为 String,它在数据库中是 VARCHAR。

      这个设置器需要一个枚举 还有另一个 setter 接受 String- 但这是私有的(或者您可以直接映射该字段 - 如果您愿意的话。)

      现在,我使用 String 的事实已被全部封装。对于应用程序的其余部分——我的域对象使用枚举。 就数据库而言——我使用的是字符串。

      如果我错过了您的问题,我深表歉意。

      【讨论】:

      • 这是有道理的。 Ruby on Rails 将允许使用验证作为奖励的相同行为。
      【解决方案5】:

      如果你使用 Spring JPA 2.1 或更高版本和 hibernate,你可以实现你自己的 AttributeConverter 并定义你的枚举如何映射到列值

      @Converter(autoApply = true)
      public class CategoryConverter implements AttributeConverter<Category, String> {
       
          @Override
          public String convertToDatabaseColumn(Category category) {
              if (category == null) {
                  return null;
              }
              return category.getCode();
          }
      
          @Override
          public Category convertToEntityAttribute(String code) {
              if (code == null) {
                  return null;
              }
      
              return Stream.of(Category.values())
                .filter(c -> c.getCode().equals(code))
                .findFirst()
                .orElseThrow(IllegalArgumentException::new);
          }
      }
      

      请参阅文章Persisting Enums in JPA中的第4个解决方案。代码sn-p也来自那里。

      【讨论】:

        【解决方案6】:

        我会使用 varchar。这不适合您吗?

        【讨论】:

        • 使用 VARCHAR 会导致数据非规范化。如果要更改枚举值的名称,则需要更新整个表,而不是仅更改引用表中的单行。
        猜你喜欢
        • 2014-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多