【发布时间】:2016-11-18 10:49:05
【问题描述】:
我有一个应用程序,它有一个大的版本记录表——也就是说,一条记录有一个在所有版本之间共享的 GUID(所以根本不是 GUID)和一个整数版本号。 GUID 和版本号一起是特定行的复合键。
业务逻辑表明,处理此表时最常见的操作是检索其中一条或多条记录的最新版本。现有代码以似乎效率最低的方式执行此操作 - 对于每条记录,它执行子查询以查找最大版本号,然后选择具有该版本号的记录。
伪代码:
currentRecord = record where record.ID == "{{guid}}"
and record.versionNumber ==
MAX(record.versionNumber where record.ID == "{guid}")
我真的很想对此进行优化,但我对 SQL 缺乏经验,我不知道该怎么做。比我更聪明的人已经尝试在这种设计的约束下进行优化,例如已经有我们可以创建的所有索引。是的,这种操作的低效率是一个重大问题,最终会影响我们的用户。
到目前为止,我有一个想法,我打算在有时间的时候尝试一下,类似于链表。除了版本号(仍然需要向用户显示)我想添加一个 true GUID versionID,然后每当我们创建记录的新版本时,我们都会指向一个previousVersion 列在上一个版本,并更新上一个版本的nextVersion 列指向新插入的行。这将允许将最新版本的检索简化为
currentRecord = record where record.ID == "{{guid}}"
and record.NextVersion = NULL
这是个好主意吗?从我公认的有限理解来看,它应该将此操作从 O(N^2) 改进到 O(N),对吧?它不会以任何方式改变我们想要记录的所有版本的情况。检索比插入更常见得多,因此它需要插入和更新来添加记录而不是仅仅插入这一事实不应该有任何明显的影响。
注意:有a question already from someone with the same problem,以及其他几个类似的方法,但没有人建议这种链表样式的方法——但他们确实提出了一种最终允许相同的空检查找到最新版本的方法,但是它使用了开始和结束日期,这在我的特定问题空间中会令人困惑(记录已经有开始和结束日期,含义完全不同)。我怀疑如果这是一个好主意,有人会在回答另一个问题时提出它,但这个想法让我很烦,所以我仍然希望有人解释它为什么很糟糕。
如果相关,我正在使用 SQL Server。
【问题讨论】:
-
您可以使用
row_number()合理有效地过滤到最新版本。或者您可以简单地在行中添加一个位标志 -IsLatestVersion,将其添加到您的索引中,并使其保持最新状态。 -
表的完整 DDL,包括所有索引,肯定会有所帮助。至少我们可以删掉您已有的解决方案。
-
我很感激,但是对于一个这个问题的一般版本已经被问过多次了,所以我想我应该专注于区分点(链表的想法),另外我不确定在这种情况下我可以发布任何看起来太像生产代码的东西(我是新手,比抱歉更安全)
-
您基本上是在写入时将元数据写入行。最简单的形式是@Blorgbeard 建议的 - 只需标记一个“当前行”位。然后,您可以创建在此位中过滤的索引。不过,这里有相当多的写入开销。你能在一夜之间完成一项大工作吗?
-
好的,所以链表构成了逻辑的一部分——您不只是在寻找最新的。一项改进性能的建议是不要使用 GUID,而是使用 IDENTITY (INT)。您可以在网上找到很多原因,为什么 INT 比 GUID 更适合键。 GUID 的唯一原因是该记录必须是真正全球唯一的,即您有多个要合并的断开连接的数据库。当然,元数据绝不会污染记录——元数据是个好主意。
标签: sql sql-server optimization linked-list