除了“create_at”时间戳列之外,使用“most_recent”布尔列来跟踪记录的最新版本是不是不好的做法?

Hen*_*nry 11 best-practices sorting data-versioning

该表看起来像这样,它是 SCD 类型 2:

+-----------+------------------+------------------------+
| id (text) | version (serial) | created_at (timestamp) |
+-----------+------------------+------------------------+
Run Code Online (Sandbox Code Playgroud)

对于 99% 的查询,我们将搜索整个表并按附加列和连接表进行过滤。对于这些查询,我们只对每个唯一 ID 的记录的最新版本感兴趣。我们还将按created_at和其他列进行排序。

为了方便查找最新记录,我正在考虑添加一most_recent (boolean)列,如此处答案中所述:

/sf/ask/2414683561/#34495621

然而我意识到我们已经有了created_at告诉我们这些信息的列 - 我们可以在搜索查询中使用 DISTINCT 子句并按创建日期排序,如 @Svet 的答案所述:

/sf/ask/1212893041/

但是,我们随后必须按我们实际想要用来显示数据的列对结果重新排序。

从长远来看,添加额外的“当前”字段似乎更简单,并且性能会更高,但这也是不好的做法吗?

Eri*_*ing 11

这是一个很好的做法

将记录标记为最新记录或取消标记为最新记录非常简单。使用一个简单的位字段将您指向您关心的行比查找每个组的最新日期要容易得多。特别是当数据变得越来越大时,你会因为没有这个而后悔自己。

您尚未标记您在此处使用的 RDBMS,但所有最常用的 RDBMS 都支持某种风格的过滤索引,这将允许您仅保留最新的行索引并易于访问。

在 SQL Server 中,这看起来像:

CREATE INDEX 
    whatever
ON dbo.some_table
    (key columns)
INCLUDE
    (is_most_recent)
WHERE
    (is_most_recent = 1);
Run Code Online (Sandbox Code Playgroud)

当然,您确实需要一种方法来保证每个组只允许一行处于活动状态(最新)。实现这一目标的最安全方法是使用唯一索引(有点像上面的索引)。

CREATE UNIQUE INDEX 
    uniquely
ON dbo.some_table
    (id)
INCLUDE
    (is_most_recent)
WHERE
    (is_most_recent = 1);
Run Code Online (Sandbox Code Playgroud)

另请参阅:确保时态数据库设计中唯一条目的正确方法是什么?


Dav*_*oft 9

SCD 的另一个常见模式是有一对日期时间列effective_dateend_effective_date。这允许识别当前版本(使用null或一些固定的未来日期,例如12-31-2099),并允许您使用业务密钥和日期加入,例如用于构建事实表

select d.surrogate_key dim_bar_key, sf.* 
from fact_stage_foo sf
join dim_bar d
  on sf.business_key = d.business_key
 and sf.tran_date between d.effective_date and d.end_effective_date
Run Code Online (Sandbox Code Playgroud)


Qua*_*noi 5

从长远来看,添加额外的“当前”字段似乎更简单,并且性能会更高,但这也是不好的做法吗?

如果您的表绝对必须是 SCD,那么这是一个好主意。

对于 99% 的查询,我们将搜索整个表并按附加列和连接表进行过滤。对于这些查询,我们只对每个唯一 ID 的记录的最新版本感兴趣。我们还将按created_at和其他列进行排序。

如果是这种情况,您可能会认真考虑仅在 OLTP 数据库/表中保留最新记录,并将 SDC 移至数据仓库(或至少另一个表)。

埃里克的回答建议创建一个过滤索引,从本质上讲,它就是这样做的:创建数据子集的影子副本,垂直(仅键和包含的列)和水平(过滤条件)对其进行切片。

如果您需要按created_at和其他列排序,您可能需要创建额外的索引来帮助您按created_at和其他列排序。所有这些索引(也是数据子集的卷影副本)都需要包含布尔标志作为过滤条件。

好处是所有这些卷影副本都将由数据库自动维护。

这里有一些不太好的事情:

  1. 您的旧记录仍然存在,占用堆/聚集索引和缓冲池中的空间。如果您的查询最终无法完全通过筛选索引提供服务,则它将不得不花费 IOPS、RAM 和 CPU 周期来读取和筛选历史数据。

  2. 您必须将过滤条件包含到您编写的每个查询中。在某些数据库中,可以通过使用视图来消除这种情况,但并非所有数据库都支持更新视图。

  3. 优化器不太可能提出有效的计划。即使确实如此,制定这些计划也需要更多时间。如果您使用实体框架或其同类产品,或者以其他方式生成大量动态 SQL,您的系统将遭受额外的性能损失,这种损失可能可以忽略不计,也可能严重,但始终存在。

  4. 在某些数据库(如旧版本的 SQL Server)中,过滤索引的实现存在很多错误,特别是当您将它们与语句MERGE、索引视图等结合使用时。

即使所讨论的表很小,最后三个要点也很重要。

当然,将表一分为二的缺点是,您必须创建额外的 ETL 管道或触发器,或者以其他方式保持这些表同步。好处是您的 OLTP 表将更小、更简单并且更易于每个人(您和优化器)使用。

如果您已经有一个用于分析工作负载的单独数据源,并且对历史数据的查询不需要事务一致性,那么从长远来看,将历史数据移出 OLTP 数据库很可能会让您的生活变得更轻松。