【问题标题】:Modulo operation on primary key column is too slow主键列的取模操作太慢
【发布时间】:2013-07-05 12:59:18
【问题描述】:

我在 SQL Server 2008 中有一个包含数千万条记录的表。 第一列 (ID) 是主键。其他列不可索引。

需要列出所有ID为30的倍数的记录。所以我写道:

SELECT ID,... FROM table1 WHERE (ID between value1 AND value2) AND (ID % 30 = 0)

这个查询慢了,即使结果只包含 100 条记录。 因此,除非我找到更快的解决方案,否则我的数据库毫无用处。

我的数据库的特殊功能是:

  1. 我从不更新此表中的值。只有 INSERT 和 SELECT。

  2. 每条新记录的 ID (INSERT) 大于表中所有先前现有的 ID。 (ID 以升序排列)。

【问题讨论】:

    标签: sql-server sql-server-2008 query-optimization


    【解决方案1】:

    您可以使用的只有索引。所以你必须用 B 树、键顺序和范围来表达问题。要减少执行时间,您必须减少查询扫描的行范围。有几种可能的方法:

    • Filtered indexes。您可以在 ID 上为谓词 ID % 30 = 0 创建一个过滤索引,并包括投影列。这适用于WHERE ID % 30 = 0
    • Persisted Computed Columns。您可以为ID % 30 创建一个持久计算列,并将其添加为聚簇索引中最左边的键(由于选择性低)。这适用于WHERE ID % 30 =0,但也适用于WHERE ID % 30 = 1WHERE ID % 30 = 29。但不适用于WHERE ID % 29=0

    如您所见,选项并不是那么好。该要求非常不寻常,我实际上怀疑它是否来自一个严肃的业务要求,它似乎更像是为更通用的要求选择实现的工件。如果您能说明您的业务需求,我们可能会提供更好的解决方案。

    【讨论】:

    • 亲爱的 Remus,我的应用程序每秒向该表插入一些输入值。 ID 实际上是一个时间值(自 2000 年第一刻以来经过的总秒数)。其他列是输入值。通过上面的查询,我想找到每半分钟的输入值。
    • 如何将相关条目保存到单独的表中,每隔 30 秒、10 分钟或其他时间?预聚合是一种众所周知的技术。
    • 如果 value2 和 value1 相距不是太远,您可以计算它们之间所有可被 30 整除的可能数字。然后,您可以使用查询 WHERE ID in (n1, n2, n3,n4 ...等)
    • 在 value1 和 value2 之间可能有大约 1K 项可以被 30 整除。顺便说一下,Darius 的解决方案似乎是迄今为止最适用的技术。
    • 这个表不能预先聚合,因为有时我们必须选择 ID % 240 = 0 的记录,有时 ID % 1200 = 0 等等!也许 SQL Server 根本不适合我的应用程序!!?
    猜你喜欢
    • 2017-05-19
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2012-06-27
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    • 2015-09-03
    相关资源
    最近更新 更多