【发布时间】:2019-02-10 19:32:51
【问题描述】:
我有以下基于各种参数在动态存储过程中生成的 SQL 语句:
SELECT [MetadataId], [DocumentType], InvoiceNumber FROM
(
SELECT [MetadataId], [DocumentType], InvoiceNumber, ROW_NUMBER()
OVER (ORDER BY [MetadataId] Asc)
AS [Row_ID]
FROM [Metadata]
WHERE ([DocumentType] = 'Invoice')
) Wrapper
WHERE Row_ID BETWEEN 999980 AND 1000000
Row_ID 会根据我的网格的当前页面而变化。
当我最初从第 1 页导航到第 2、3、4、5 页等时,上面的查询效果很好,但如果我立即从第 1 页导航到第 50,000 页,我就不能这么说了我的测试数据库中的最后一页,其中包含 100 万张随机生成的发票,其中我的页面大小为 20。
加载大约需要 29/30 秒,我的 SQL Server 实例使用的 RAM 从大约 400MB 变为 1.61GB。
初始延迟过后,立即转到第 49999、49998、49997 等页面,或者在第 1 页和第 50000 页之间来回导航也是即时的。
我只能假设整个数据集以某种方式加载到内存中。
补充说明:
-
MetadataId设置为主键。 -
DocumentType、InvoiceNumber等其他可搜索的列也已编入索引,但不是唯一的。 - 出于各种原因,我需要继续使用动态存储过程,但主要原因是,虽然字段要求因客户而异,但我们的应用程序使用的结果保持不变。
- 使用 SQL Server 2014 Developers 版本进行测试。
所以我的问题是:
谁能向我解释实际发生了什么?所有数据都加载到内存中了吗?
有没有办法改进这个?请注意,我需要
Row_ID由 'ROW_NUMBER() OVER' 生成,因为我的 SQL 语句的WHERE子句部分可能会根据用户搜索的参数而发生很大变化。
谢谢。
UPDATE-1
这是执行计划:
【问题讨论】:
-
你看过执行计划,看看发生了什么吗? Row_ID 不是索引还是主键?
-
当然听起来整个表都已加载到内存中。我怀疑它必须按可能没有被索引的行排序并一直到最后。
-
@RickS,Row_ID 由 Row_Number 函数生成,该函数根据用户在应用程序中选择的内容而有所不同。因此,除非所有可能的排序列都被索引,否则您可能会遇到麻烦。
-
@Thierry,您查看过列存储索引吗?虽然它存在问题,但它可能会解决您的问题,因为基本上您需要在每个(可排序的)列上创建一个索引。否则,请考虑每个可排序列上的非聚集索引并压缩它们以进行存储。
-
@Thierry 和执行计划确实显示出了什么问题。您必须扫描整个表才能生成行号,以便稍后过滤它们。 UnhandledExcepSean 的链接显示了如何解决这个问题。 OFFSET/FETCH 将删除行计算并带来更好的性能。如果您保留当前页面的最后一个 ID 并要求接下来的 N 行大于它,您可以获得更好的性能
标签: sql sql-server tsql stored-procedures sql-server-2012