【问题标题】:How to store 60 Booleans in a MySQL Database?如何在 MySQL 数据库中存储 60 个布尔值?
【发布时间】:2016-05-25 12:10:30
【问题描述】:

我正在构建一个移动应用程序,我使用 PHP 和 MySQL 编写后端 - REST API。

如果我必须将大约 50-60 个布尔值存储在我的移动应用程序中名为“报告”的表中(用户必须在表单中检查内容),我将值 (0/1) 存储在一个简单的数组中。在我的 MySql 表中,我应该为每个布尔值创建一个不同的列,或者如果我只是使用字符串或 Int 将其存储为“110101110110111 ...”之类的“数字”就足够了吗?

我使用 JSON 获取和放置数据。

更新 1:我所要做的就是检查一切是否为 1,如果其中一个为 0,那么这是一个“问题”。 2 年后,该表将有大约 15.000-20.000 行,它必须非常快并且尽可能节省空间。

更新 2:就速度而言,哪种解决方案更快?制作单独的列与将其存储在字符串/二进制类型中。如果我必须检查哪些是 0 怎么办?如果我将它作为“数字”存储在一个列中,如果它不是“111..111”然后将其作为 JSON 发送到移动应用程序,我在其中解析值并在用户设备上分析它,这是一个很好的解决方案吗?假设我必须处理 50K 行。

提前致谢。

【问题讨论】:

  • 如果您需要搜索(使用 WHERE bool_a AND NOT bool_b 之类的东西)这些标志的值,这会促使您将它们存储在它们自己的列中。但是您还没有告诉我们您的应用程序需要如何使用这些数据。
  • 你是对的。我所要做的就是检查一切是否为 1,如果其中一个为 0,那么这是一个“问题”。在 2 年内,该表将有大约 15.000-20.000 行,它必须非常快并且尽可能节省空间。
  • 如果您将每个布尔值放在其自己的列中,您的应用程序将需要在每次添加新报告时更改数据库(假设您将添加新报告)。或许您应该考虑以长而窄的格式存储数据(请参阅statmethods.net/management/reshape.html 以查看同一数据集的“宽”与“长”格式)。
  • 15-20k 行是什么都没有。如果索引良好,MySQL 可以快速处理数十亿行。
  • 我建议您拆分表格并将每个布尔字段存储在单独的字段中。它将更具可读性,并让您免于以后出现问题。不用担心 20 000。我们使用具有 40 到 2 亿条记录的表,而 mysql 可以很好地处理它们。

标签: php mysql database


【解决方案1】:

在搜索时,每个值单独列更灵活。

如果不同的行有不同的布尔值集合,单独的键/值表会更灵活。

如果

  1. 您的布尔值列表或多或少是静态的
  2. 您的所有行都包含所有这些布尔值
  3. 您的性能关键搜索是查找其中任何值为 false 的行

然后使用诸如“1001010010”等文本字符串是存储它们的好方法。你可以这样搜索

 WHERE flags <> '11111111'

找到您需要的行。

您可以使用一个 BINARY 列,每个标志位一个位。但是,如果您使用文本,您的表格将更容易用于临时查询和眼球检查。在您开始存储数百万行之前,使用 BINARY 而不是 CHAR 节省的空间不会很大。

edit 不得不说:每次我用布尔属性数组构建类似的东西时,我后来都对它变得多么不灵活感到失望。例如,假设它是一个灯泡目录。在千禧年之交,布尔标志可能是

screw base
halogen
mercury vapor
low voltage

然后,事情发生了变化,我发现自己需要更多的布尔标志,比如,

LED
CFL 
dimmable
Energy Star

等等。突然之间,我的数据类型不够大,无法容纳我需要它们容纳的东西。当我写“你的布尔值列表或多或少是静态的”时,我的意思是你不合理地期望在你的应用程序的生命周期内有像灯泡特性这样的变化。

因此,单独的属性表可能是更好的解决方案。它会有这些列:

   item_id           fk to item table         -- pk
   attribute_id      attribute identifier     -- pk
   attribute_value   

这最终是灵活的。您可以添加新标志。您可以在应用程序的生命周期中随时将它们添加到现有项目或新项目。而且,每个项目都不需要相同的标志集合。你可以写“哪些物品有任何虚假属性?”像这样查询:

 SELECT DISTINCT item_id FROM attribute_table WHERE attribute_value = 0

但是,您必须小心,因为“哪些项目缺少属性”的查询很难编写。

【讨论】:

  • 用 BIT(N) 代替字符串怎么样?
  • 感谢您的回答。 “每次我用布尔属性数组构建类似的东西时,我都感到很失望”你能给我一个更好的解决方案吗?我乐于学习新事物。
  • 绝对是一个新表,它也被规范化了。 en.wikipedia.org/wiki/…
【解决方案2】:

对于您的特定目的,当任何零标志是一个问题(例外)并且大多数条目(如 99%)将是“1111...1111”时,我认为没有任何理由将它们全部存储。我宁愿创建一个单独的表,只存储未经检查的标志。该表可能如下所示:unkeked_flags (user_id, flag_id)。在另一个表中,您存储您的标志定义:flags (flag_id, flag_name, flag_description)

那么你的报告就像SELECT * FROM unchecked_flags一样简单。

更新 - 可能的表定义:

CREATE TABLE `flags` (
    `flag_id` TINYINT(3) UNSIGNED NOT NULL AUTO_INCREMENT,
    `flag_name` VARCHAR(63) NOT NULL,
    `flag_description` TEXT NOT NULL,
    PRIMARY KEY (`flag_id`),
    UNIQUE INDEX `flag_name` (`flag_name`)
) ENGINE=InnoDB;

CREATE TABLE `uncheked_flags` (
    `user_id` MEDIUMINT(8) UNSIGNED NOT NULL,
    `flag_id` TINYINT(3) UNSIGNED NOT NULL,
    PRIMARY KEY (`user_id`, `flag_id`),
    INDEX `flag_id` (`flag_id`),
    CONSTRAINT `FK_uncheked_flags_flags` FOREIGN KEY (`flag_id`) REFERENCES `flags` (`flag_id`),
    CONSTRAINT `FK_uncheked_flags_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`)
) ENGINE=InnoDB;

【讨论】:

    【解决方案3】:

    可能对每个布尔值使用专用列进行更好的搜索,但基数很差,即使您为每个列编制索引,也会涉及相当多的遍历或扫描。

    如果您只是在寻找 HIGH-VALUES 0xFFF.... 那么绝对是位图,这可以解决您的基数问题(每个 OP 更新)。这不像您正在检查奇偶校验...但是,如果这是正常的,那么树将严重偏向 HIGH-VALUES,并且可能会在插入时创建一个易于节点分裂的热点。

    位映射和使用按位运算符掩码将节省空间,但需要与一个字节对齐,因此可能会有未使用的“提示”(可能是为将来的字段提供),因此掩码必须保持长度或字段用 1s 填充。

    它还会增加您的架构的复杂性,这可能需要定制编码、定制标准。

    您需要对任何搜索的重要性进行分析(您通常不希望搜索所有。甚至任何离散字段)。

    这是对数据进行非规范化以及调整特定客户端的服务请求的一种非常常见的策略。 (对于同一事务,某些响应比其他响应更胖)。

    【讨论】:

      【解决方案4】:

      案例 1:如果“问题”很少见。

      有一个带有 ID 的表 Problems 和一个带有问题值 (50-60) 的 TINYINT。使用该表上的合适索引,您可以查找所需的任何内容。

      案例 2:很多项目。

      使用BIGINT UNSIGNED 最多可保存 64 0/1 值。使用 1 &lt;&lt; n 之类的表达式为第 n 个(从 0 开始计数)位构建掩码。例如,如果您知道正好有 55 位,那么全 1 的值就是(1&lt;&lt;55)-1。然后你可以通过WHERE bits = (1&lt;&lt;55)-1找到有“问题”的项目。

      Bit Operators and functions

      案例 3:您有问题的名称。

      SET ('broken', 'stolen', 'out of gas', 'wrong color', ...)
      

      这将为每个问题(逻辑上)构建一个 DATATYPE。另请参阅函数 FIND_IN_SET() 作为检查问题的一种方式。

      案例 2 和 3 大约需要 8 个字节来解决全部问题——非常紧凑。您可能执行的大多数SELECT 都会扫描整个表,但 20K 行不会花费很长时间,并且比每个问题有 60 列或一行要快得多。

      【讨论】:

        猜你喜欢
        • 2011-03-10
        • 2015-08-25
        • 2010-09-22
        • 1970-01-01
        • 2016-03-21
        • 2019-02-10
        相关资源
        最近更新 更多