使用链表方法优化SQL中版本记录的检索

使用链表方法优化SQL中版本记录的检索,sql,sql-server,optimization,linked-list,Sql,Sql Server,Optimization,Linked List,我有一个应用程序,它有一个大的版本记录表——也就是说,一个记录有一个在其所有版本之间共享的GUID(所以根本不是GUID)和一个整数版本号。GUID和版本号一起是特定行的复合键 业务逻辑规定,处理此表时最常见的操作是检索一个或多个记录的最新版本。现有代码以一种似乎效率最低的方式执行此操作—对于每个记录,它执行子查询以查找最大版本号,然后选择具有该版本号的记录 伪代码: currentRecord = record where record.ID == "{{guid}}" and rec

我有一个应用程序,它有一个大的版本记录表——也就是说,一个记录有一个在其所有版本之间共享的GUID(所以根本不是GUID)和一个整数版本号。GUID和版本号一起是特定行的复合键

业务逻辑规定,处理此表时最常见的操作是检索一个或多个记录的最新版本。现有代码以一种似乎效率最低的方式执行此操作—对于每个记录,它执行子查询以查找最大版本号,然后选择具有该版本号的记录

伪代码:

currentRecord = record where record.ID == "{{guid}}"
    and record.versionNumber == 
        MAX(record.versionNumber where record.ID == "{guid}")
我真的很想对此进行优化,但我对SQL缺乏经验,也不知道如何进行优化。比我更伟大的头脑已经尝试在这种设计的约束范围内进行优化,例如,我们已经有了所有可以创建的索引。是的,这个操作的低效是一个重要的问题,最终影响到我们的用户

到目前为止,我有一个想法,当我有时间的时候,我打算尝试一下,它类似于一个链表。除了版本号(仍然需要向用户显示)之外,我还考虑添加一个真正的GUID
versionID
,然后每当我们创建一个记录的新版本时,我们都会将一列
previousVersion
指向上一版本,并更新以前版本的
nextVersion
列,以指向新插入的行。这将使最新版本的检索简化为

currentRecord = record where record.ID == "{{guid}}"
    and record.NextVersion = NULL
这是个好主意吗?从我公认的有限理解来看,它应该将这个操作从O(N^2)改进为O(N),对吗?它不会改变我们以任何方式想要记录的所有版本的情况。检索比插入更为常见,因此需要插入和更新才能添加记录,而不仅仅是插入,这一事实不会产生任何明显的影响

注意:有,以及其他一些类似的方法,但是没有人建议这种链表风格的方法——但是他们确实建议了一种最终允许相同的空检查来查找最新版本的方法,但是它使用了开始日期和结束日期,这在我的特定问题空间中会令人困惑(唱片已经有开始日期和结束日期,有完全不同的含义)。我怀疑如果这是一个好主意,有人会在回答另一个问题时提出这个建议,但这个想法困扰着我,所以我仍然希望有人解释为什么它很糟糕


如果相关,我使用的是SQL Server。

您的方法应该可以。您只需在
(id,recordNumber)
上创建索引即可。您可以在数据库中执行以下操作:

create index t_id_recordNumber on t(id, recordNumber);

您的代码应该自动使用此索引。

我尝试了如何使用/执行此索引,并认为我会尽可能回答自己的问题

我的测试代码可以在上查看,但是在这种情况下它有点毫无意义,因为这个站点不显示执行计划/统计数据。如果您足够好奇,您可能必须在SSMS或其他任何地方运行它

到目前为止,我已经创建了两个测试。在第一个测试中,我尝试通过记录ID(在所有版本中共享)检索特定记录的最新版本。而在大多数情况下,旧方法和新方法都会产生完全相同的查询(单个索引搜索),并花费相同的时间返回,其他时间是旧方法(使用子查询)将执行两个索引查找和一个流聚合,最终速度会大大降低

在第二个测试中,我尝试在一个非键列上检索多个记录。我发现新方法的速度始终明显更快。它始终是一个索引查找,而旧方法始终是两个(加上流聚合)。每次在同一批中运行这两个查询时,旧方法占总执行时间的71%,新方法占29%


总的来说,这似乎表明这个想法有一些优点,但我没有资格参与SQL优化,所以我很高兴有人有资格参与进来,解释我在这里搞砸了。

你可以使用
行数()
以合理高效地筛选到最新版本。或者,您可以简单地在行中添加一个位标志-
IslatesVersion
,将其添加到索引中,并使其保持最新。完成表的DDL,包括其所有索引,肯定会有所帮助。至少我们可以删除您已有的解决方案。我欣赏但是,对于其中一个问题,这个问题的一般版本已经被问了很多次,所以我想我应该把重点放在区别点上(链表的想法),另外,我不确定在这种情况下发布任何看起来太像生产代码的东西都能逃脱(我是新手,安全总比抱歉好)您基本上是在写入时将元数据写入行中。最简单的形式是@Blorgbeard建议的形式-只需标记一个“当前行”位。然后您可以创建在该位中过滤的索引。不过这里有相当多的写入开销。您能在一个大的工作中一夜之间完成吗?好的,所以链表构成了逻辑的一部分-您不是不要只是寻找最新的。性能改进的一个建议是不要使用GUID,而是使用IDENTITY(INT)相反,你可以在网上找到很多原因来解释为什么INT比GUID更适合于键。使用GUID的唯一原因是记录必须是真正的全局唯一的,也就是说,你有多个断开连接的数据库想要合并。当然,元数据决不会污染记录-元数据是个好主意。如果我误解了b,很抱歉你是说我们现有的方法只使用一个guid和一个版本号吗?如果是的话,我们已经在这些列上有了一个索引,它仍然是