【问题标题】:SQL Table Design QuestionSQL 表设计题
【发布时间】:2011-03-02 02:55:46
【问题描述】:

我正在考虑两种桌子设计方案,但我不确定每种方案的优缺点。

下面是对我的情况的一些抽象的描述:

我正在跟踪一些数据点(category_id, point_id, value)。 大多数时候,我只对数据点的当前值感兴趣。但是每当有变化时,我都需要记录所有历史值。

有时我可能想查看特定点的历史值,但如果这些查询有点慢也没关系。最重要的是我能尽快得到所有点的当前值,或者某个特定类别的所有点的当前值。

我正在考虑的两种(可能还有三种)方法:

  1. 使用两个单独的表,一个current_vales 和一个history 表,并带有一个触发器,每次current_values 中的某些内容发生更改时,该触发器将在历史表中插入一行。
  2. 仅使用一个表,每行带有布尔标志isCurrent。每当值更改时,将该行标记为不再是当前行,并使用更新后的值插入一个新的当前行。
  3. (在每一行上只使用一个带有时间戳的表——那么特定 id 的当前值就是带有最新时间戳的行。但这似乎很难表达为查询,特别是如果我想获得所有特定类别的当前值,甚至不确定在没有子查询或性能的情况下如何表达)

一次只会有大约 3,000-5,000 个当前点,但值的变化足够频繁,其中多达一半每天都会发生变化,因此最终会有数十万行历史记录。

上述每种方法的优缺点是什么(或者还有其他我没有提到的更好的方法)?鉴于我的目标是尽快获得当前的一组点,并且可以接受较慢的历史查询,哪一个最好?

【问题讨论】:

    标签: mysql sql database-schema database-design


    【解决方案1】:

    选项 1 和 2 将具有相似的性能 - 选项 1 中的数据的手动“分区”也可以使用聚集索引来管理,其中 IsCurrent 作为选项 2 中的第一列。您始终可以查看仅提供的视图当前并且在某些方面,这在性能上将非常相似,因为更改 IsCurrent 将物理移动旧行(由于集群)并添加新行,就像您的触发器将删除并插入两个表一样。

    您也可以使用 MySQL 的分区功能。

    拥有单独的表或单个表的分区的一大好处是以更细粒度的方式控制数据的备份(并可能清除)。

    选项 1 的真正好处是您确实保存了那个小列,这在您处理数十亿行时可能会有所帮助。

    选项 2 的维护好处是架构始终相同(不必保持同步更改),因为只有一个表。

    选项 3 不会表现得那么好,因为当前值的前沿更难找到 - 即变化(尽管通过标识符和时间戳 DESC 的索引来提高性能并非不可能)

    【讨论】:

      【解决方案2】:

      选择当前值
      选项 1 与具有正确索引的选项 2 大致相同。
      选项 1 的示例索引(category_id、point_id) 选项 2 的示例索引(isCurrent、category_id、point_id)

      当您使用选项 2 表进行选择时,您需要在 where 过滤器中包含 IsCurrent。数据库引擎将知道索引具有分组的当前记录,可以这么说,甚至不需要查看非当前记录。将表格想象成电话簿,如果没有索引,您将需要检查每一页以找到您所追求的业务。但是使用索引,您查看索引,它说以“H”开头的业务在第 348 页。所以您直接转到第 348 页并忽略其他业务。它与数据库的概念相同,只有您能够创建嵌套级别的索引。 对咆哮感到抱歉,但有些人表示选项 2 慢得多,这是不正确的,它们将大致相同。

      插入/更新。
      这是我看到选项 1 和 2 之间的主要区别的地方。 选项 1,当您对现有记录进行更新时,这些是我看到的步骤

      • 你点击磁盘读取现有的 当前记录
      • 然后你点击磁盘 将该记录添加到历史记录 表
      • 然后你再次敲击磁盘 删除现有的当前记录
      • 然后你会点击磁盘添加 新的当前记录

      选项 2,当您更新现有记录时,这些是我看到的步骤

      • 您点击磁盘更新现有的当前记录以将其设置为非当前记录
      • 然后您将点击磁盘以添加新的当前记录

      你敲击磁盘的次数越多,速度就越慢。在您每分钟进行大量更新之前,它可能并不大,但数据库需要做的工作越少越好。

      我没有选择选项 3,因为它不容易在不破坏磁盘的情况下获取当前记录。我认为无论您选择哪个选项,您都需要时间戳,但仅在查询历史数据时需要时间戳,例如一个值有 20 个历史值,您会显示 20 个中的哪一个?

      这是我的两分钱。

      【讨论】:

        【解决方案3】:

        我会选择选项#1。它允许轻松检索 current_values,历史记录表将保存大量事务,但如有必要,可能会清除较旧的历史记录。

        选项 #2 会使您对 current_values 的更常规查询返回更慢。

        【讨论】:

          【解决方案4】:

          选项 1 似乎是正确的方法,因为移出历史记录的更改更多的是审核。它还可以更好地分离数据。

          选项 2:当表示的行是唯一的时,使用同一个表中的多行。因此,鉴于更改将发生在一个独特的实体上,从设计的角度来看,最好不要进行更改。

          选项 3:选项 2 中的 cmets 再次适用,并且查询也可能陷入混乱。

          【讨论】:

          • 另外,选项 2 和 3 的速度要慢得多。
          猜你喜欢
          • 2010-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-18
          • 2012-05-22
          • 2010-12-01
          • 1970-01-01
          相关资源
          最近更新 更多