【问题标题】:Flags in a database rows, best practices数据库行中的标志,最佳实践
【发布时间】:2010-09-12 14:18:32
【问题描述】:

我问这个是出于好奇。基本上我的问题是当你有一个数据库需要一个行条目来拥有像标志一样的东西时,最佳实践是什么?一个很好的例子是堆栈溢出的标记,或者 bugzilla 中的操作系统字段。可以为给定条目设置标志的任何子集。

通常,我从事 c 和 c++ 工作,所以我的直觉是使用无符号整数字段作为一组可以翻转的位...但我知道这不是一个好的解决方案,原因有几个。其中最明显的是可扩展性,我可以拥有多少个标志会有一个硬性上限。

我还可以想到一些其他解决方案,它们可以更好地扩展,但会出现性能问题,因为它们需要多次选择才能获取所有信息。

那么,这样做的“正确”方法是什么?

【问题讨论】:

    标签: sql database flags


    【解决方案1】:

    如果您确实需要从一组封闭的标志(例如 stackoverflow 徽章)中进行无限选择,那么“关系方式”将是创建一个标志表和一个单独的表,将这些标志与您的目标实体相关联。因此,用户、标志和 usersToFlags。

    但是,如果空间效率是一个严重的问题而查询能力不是,那么无符号掩码几乎也可以工作。

    【讨论】:

    • 只是对未签名掩码的警告。如果您必须编写对设置了特定位的行进行过滤的查询,那么当行数变大时,您的性能将受到严重影响,因为 where 子句中的逻辑和/或操作无法有效地使用索引。
    【解决方案2】:

    在很多情况下,这取决于很多事情——比如您的数据库后端。例如,如果您使用的是 MySQL,SET datatype 正是您想要的。

    基本上,它只是一个位掩码,为每个位分配值。 MySQL 最多支持 64 位值(意味着 64 种不同的切换)。如果你只需要 8 个,那么每行只需要一个字节,这是非常棒的节省。

    如果您在一个字段中确实有超过 64 个值,那么您的字段可能会变得更加复杂。您可能希望随后扩展到 BLOB 数据类型,这只是 MySQL 没有固有理解的一组原始位。使用它,您可以创建任意数量的位字段,MySQL 乐于将其视为二进制、十六进制或十进制值,但您需要。如果您需要超过 64 个选项,请根据您的应用程序创建尽可能多的字段。缺点是很难使该字段具有人类可读性。 BIT datatype 也限制为 64 个。

    【讨论】:

    • 不是我会做的,但它是位掩码解决方案的一个很好的实现。
    • 链接到SET datatype 已损坏。这是关于 SET for MySQL 8.0 的文档的链接:dev.mysql.com/doc/refman/8.0/en/set.html
    【解决方案3】:

    一般来说,我避免使用位掩码字段。它们将来很难阅读,并且需要对数据有更深入的了解才能理解。

    之前已经提出了关系解决方案。鉴于您概述的示例,我将创建类似这样的内容(在 SQL Server 中):

    
    CREATE TABLE Users (
      UserId INT IDENTITY(1, 1) PRIMARY KEY,
      FirstName VARCHAR(50),
      LastName VARCHAR(50),
      EmailAddress VARCHAR(255)
    );
    
    CREATE TABLE Badges (
      BadgeId INT IDENTITY(1, 1) PRIMARY KEY,
      [Name] VARCHAR(50),
      [Description] VARCHAR(255)
    );
    
    CREATE TABLE UserBadges (
      UserId INT REFERENCES Users(UserId),
      BadgeId INT REFERENCES Badges(BadgeId)
    );
    

    【讨论】:

    • 我很想看看这个设置的插入和选择示例
    【解决方案4】:

    如果标志不止几个,或者将来可能会这样,我将使用单独的标志表和它们之间的多对多表。

    如果有一些标志并且我永远不会在 WHERE 中使用它们,我将使用 SET() 或位域或其他任何东西。它们易于阅读且更紧凑,但查询起来很痛苦,有时使用 ORM 更让人头疼。

    如果只有几个标志 - 并且只有 成为几个标志 - 那么我将只创建几个 BIT/BOOLEAN/etc 列。

    【讨论】:

      【解决方案5】:

      如果标志具有非常不同的含义并且直接在 SQL 查询或 VIEWS 中使用,那么使用 BOOLEAN 类型的多个列可能是个好主意。

      将每个标志放在一个额外的列中,因为无论如何您都会单独阅读和修改它们。如果你想对标志进行分组,只需给它们的列名一个公共前缀,即,而不是:

      CREATE TABLE ... (
          warnings INTEGER,
          errors   INTEGER,
          ...
      )
      

      你应该使用:

      CREATE TABLE ... (
          warning_foo BOOLEAN,
          warning_bar BOOLEAN,
          warning_...
          error_foo   BOOLEAN,
          error_bar   BOOLEAN,
          error_...   BOOLEAN,
          ...
      )
      

      虽然 MySQL 没有 BOOLEAN 类型,但您可以为此使用准标准 TINYINT(1),并将其设置为 0 或 1。

      【讨论】:

        【解决方案6】:

        如果您的数据库支持,我建议使用 BOOLEAN 数据类型。

        否则,最好的方法是使用 NUMBER(1) 或等效值,并在列上设置一个检查约束,将有效值限制为 (0,1),如果需要,可能为 NULL。如果没有内置类型,则使用数字比使用字符列更容易产生歧义。 (true的值是多少?“T”或“Y”或“t”)

        这样做的好处是您可以使用 SUM() 来计算 TRUE 行的数量。

        SELECT COUNT(1), SUM(ActiveFlag)
        FROM myusers;
        

        【讨论】:

          【解决方案7】:

          一种非常相关的方法

          对于没有设置类型的数据库,您可以打开一个新表来表示设置了每个标志的实体集。

          例如对于表“Students”,您可以有表“RegisteredStudents”、“SickStudents”、TroublesomeStudents 等。每个表只有一列:student_id。如果您只想知道哪些学生“已注册”或“生病”,这实际上会非常快,并且在每个 DBMS 中的工作方式都相同。

          【讨论】:

          • 当你要查询 SickStudents 并且生病的学生很少时,这确实非常快。如果您正在执行诸如水合实体对象之类的操作,这是一个额外的连接。
          【解决方案8】:

          当我考虑在数据库中存储位掩码标志(类似于 OP 最初使用整数)的最佳方式时,遇到了这个问题。

          其他答案都是有效的解决方案,但我认为值得一提的是,如果您选择将位掩码直接存储在数据库中,您可能不必屈服于可怕的查询问题。

          如果您正在开发一个使用位掩码的应用程序,并且您确实希望将它们作为一个整数或字节列存储在数据库中,那么请继续这样做。以后,您可以自己编写一个小实用程序,该实用程序将从您的主工作表中的位掩码生成另一个标志表(以您选择的任何行/列模式)。然后,您可以对该计算/派生表执行普通 SQL 查询。

          这样,您的应用程序就可以方便地仅读取/写入位掩码字段/列。但是,如果以后有必要,您仍然可以使用 SQL 真正深入研究您的数据。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-05-28
            • 2010-09-30
            • 2014-11-12
            • 2012-09-16
            • 2011-03-31
            • 1970-01-01
            • 2016-03-04
            相关资源
            最近更新 更多