【问题标题】:What's the optimal way to store binary flags / boolean values in each database engine?在每个数据库引擎中存储二进制标志/布尔值的最佳方式是什么?
【发布时间】:2010-12-26 22:41:31
【问题描述】:

我看到了一些可能的方法(在一些数据库引擎中,其中一些是同义词):

  1. TINYINT(1)
  2. 布尔
  3. BIT(1)
  4. 枚举(0,1)
  5. CHAR(0) NULL

PHP支持的所有主要数据库引擎都应该注明,但作为参考,如果还注明其他引擎就更好了。

我要求的是最适合阅读的设计。 例如使用 WHERE 条件中的标志字段或 GROUP BY 标志进行选择。 性能比存储空间重要得多(除非大小对性能有影响)。

还有更多细节:

在创建表时,我不知道它是否会稀疏(如果大多数标志都打开或关闭),但我可以稍后更改表,所以如果我知道有什么可以优化的,应该注意的。

此外,如果每行只有一个(或几个)标志,而不是许多(或很多)标志,则应该注意。

顺便说一句,我在某处读过以下内容:

使用布尔值可能会做同样的事情 使用 tinyint,但是它有 语义传达的优势 你的意图是什么,那就是 物有所值。

嗯,在我的情况下,它没有任何价值,因为每个表都由我的应用程序中的一个类表示,并且所有内容都在该类中明确定义并且有据可查。

【问题讨论】:

    标签: database-design flags bitflags


    【解决方案1】:

    此答案适用于 ISO/IEC/ANSI 标准 SQL,包括更好的免费软件假装 SQL。

    第一个问题是您确定了两个类别,而不是一个,因此无法合理比较它们。

    A.第一类

    (1) (4) 和 (5) 包含多个可能的值并且是一个类别。所有这些都可以在 WHERE 子句中轻松有效地使用。它们具有相同的存储空间,因此存储和读取性能都不是问题。因此,剩下的选择只是基于列的实际数据类型。

    ENUM 是非标准的;更好或标准的方法是使用查找表;那么这些值在表格中是可见的,而不是隐藏的,并且可以被任何报告工具枚举。由于内部处理,ENUM 的读取性能会受到不小的影响。

    B.第二类

    (2) 和 (3) 是二值元素:真/假;男/女;死/活。该类别与第一类不同。它在您的数据模型和每个平台中的处理方式都是不同的。 BOOLEAN 只是 BIT 的同义词,它们是一回事。从法律上讲(SQL 方面),所有符合 SQL 的平台都采用相同的处理方式,并且在 WHERE 子句中使用它没有问题。

    性能差异取决于平台。 Sybase 和 DB2 将最多 8 个 BIT 打包到一个字节中(这里的存储并不重要),并动态映射 2 的幂,因此性能非常好。 Oracle 在每个版本中做不同的事情,我看到建模者使用 CHAR(1) 而不是 BIT 来克服性能问题。 MS 在 2005 年之前还不错,但他们在 2008 年打破了它,因为结果无法预测;所以简短的回答可能是将其实现为 CHAR(1)。

    当然,假设您不会做一些愚蠢的事情,例如将 8 个单独的列打包到一个 TINYINT 中。这不仅是一个严重的规范化错误,而且是编码人员的噩梦。保持每列离散且数据类型正确。

    C.多指标和可空列

    这与(A)和(B)无关,并且独立于(A)和(B)。列正确的数据类型是什么,与您有多少以及它是否为 Nullable 是分开的。可空意味着(通常)该列是可选的。本质上,您还没有完成建模或规范化练习。功能依赖是模棱两可的。如果你完成了规范化练习,将没有 Nullable 列,没有可选列;它们要么明显存在于特定关系,要么不存在。这意味着使用超类型-子类型的普通关系结构。

    当然,这意味着更多的表,但没有 Null。 Enterprise DBMS 对更多表或更多连接没有问题,这就是它们的优化目标。规范化数据库的性能比非规范化或非规范化的数据库好得多,并且它们可以在不“重构”的情况下进行扩展。您可以通过为每个子类型提供一个视图来简化使用。

    如果您想了解有关此主题的更多信息,请查看此question/answer。如果您在建模方面需要帮助,请提出一个新问题。在你的提问水平上,我建议你坚持 5NF。

    D. Null 的表现

    另外,如果性能对您很重要,则排除 Null。每个 Nullable 列都存储为可变长度;这需要对每一行/列进行额外处理。企业数据库对此类行使用“延迟”处理,以允许日志记录等在不妨碍固定行的情况下移动到队列中。特别是永远不要在索引中使用可变长度列(包括可空列):这需要在每次访问时解包。

    E.投票

    最后,我认为这个问题的意义不是民意调查。公平地说,你会得到技术性的答案,甚至是意见,但民意调查是为了人气竞赛,而 SO 的响应者的技术能力涵盖的范围很广,所以最受欢迎的答案和技术上最正确的答案是在两个不同的光谱的尽头。

    【讨论】:

    • 您能否提供一个链接或更多信息,说明“MS 在 2005 年之前还不错,但他们在 2008 年打破了它,因为结果是不可预测的”以及它是否在 2K8R2 中被打破
    • @RC。我没有链接,我有经验。维基还没有听说过。上述特定上下文以及其他一些(并非所有上下文)的结果是不可预测的;如果您的代码在 2005 年表现良好。我已经发布了其他一些细节:删除溢出页面并损害聚集索引的所有性能等。请随意阅读它们。在第 2 版中未修复。至少在第 4 版之前,这些元素不可能得到修复。 MS 花了 3 年多的时间来修复 2005 年。
    【解决方案2】:

    我知道这不是您想要的答案,但是除了最极端的特殊情况之外,差异实际上可以忽略不计。在每一种特定情况下,简单地切换数据类型并不足以解决性能问题。

    例如,这里有一些替代方案,它们的性能将大大优于任何数据类型更改。当然,每个都有一个缺点。

    如果您有 200 个可选标志,并且您一次最多查询 1-2 个用于许多行,则通过将每个标志放在自己的表中可以获得更好的性能。如果数据真的很稀疏,那就更好了。

    如果您有 200 个强制标志并且只执行单条记录提取,则应将它们放在同一个表中。

    如果您有一小部分标志,则可以使用位掩码将它们打包在一个列中,这在存储方面非常有效,但您将无法(轻松)查询单个标志。当然,当 flags 可以为 NULL 时,这不起作用...

    或者您可以发挥创意并使用“垃圾维度”概念,在其中创建一个单独的表,其中所有 200 个布尔标志表示为列。为每个不同的标志值组合创建一行。每行都有一个自动增量主键,您在主记录中引用该主键。瞧,主表现在包含 1 个 int,而不是 200 列。黑客的天堂,DBA 的噩梦。

    我想说的是,尽管争论哪个是“最好的”很有趣,但还有其他更重要的问题(例如您引用的评论)。仅仅是因为当您遇到真正的性能问题时,数据类型既不是问题也不是解决方案。

    【讨论】:

      【解决方案3】:

      以上任何一项都可以,如果得到适当支持,我个人偏好使用BOOL,因为这最能传达您的意图,但我会避免使用ENUM(0,1)

      ENUM 的第一个问题是它要求它的值是一个字符串。 01 看起来像一个数字,所以程序员倾向于给它发送一个数字。

      ENUM 的第二个问题是,如果你发送一个错误的值,它会默认为第一个枚举,并且在某些数据库中它甚至不会指示错误(我正在查看你的 MySQL)。这使第一个问题变得更糟,因为如果您不小心将其发送到1 而不是"1",它将存储值"0"——非常违反直觉!

      我不认为这会影响所有数据库引擎(不知道,还没有全部尝试过),但它会影响到足够多的引擎,因此我认为避免它是一种好习惯。

      【讨论】:

        猜你喜欢
        • 2013-09-23
        • 1970-01-01
        • 2014-01-23
        • 2010-10-11
        • 1970-01-01
        • 1970-01-01
        • 2021-04-21
        • 1970-01-01
        相关资源
        最近更新 更多