我正在使用SQL Server进行分页,我希望通过计算结果总数作为部分结果集的一部分来避免重复,而不是获取结果集,然后执行单独的查询以获得计数。然而,麻烦的是,它似乎增加了执行时间。例如,如果我查看SET STATISTICS TIME ON
,请执行以下操作:
WITH PagedResults AS (
SELECT
ROW_NUMBER() OVER (ORDER BY AggregateId ASC) AS RowNumber,
COUNT(PK_MatrixItemId) OVER() AS TotalRowCount,
*
FROM [MyTable] myTbl WITH(NOLOCK)
)
SELECT * FROM PagedResults
WHERE RowNumber BETWEEN 3 AND 4810
......或者这个(执行计划相同):
SELECT * FROM (
SELECT TOP (4813)
ROW_NUMBER() OVER (ORDER BY AggregateId ASC) AS RowNumber,
COUNT(PK_MatrixItemId) OVER() AS TotalRowCount,
*
FROM [MyTable] myTbl WITH(NOLOCK)
) PagedResults
WHERE PagedResults.RowNumber BETWEEN 3 AND 4810
...似乎平均CPU时间(所有查询加起来)是此时间的1.5到2倍:
SELECT * FROM (
SELECT TOP (4813)
ROW_NUMBER() OVER (ORDER BY AggregateId ASC) AS RowNumber,
*
FROM [MyTable] myTbl WITH(NOLOCK)
) PagedResults
WHERE PagedResults.RowNumber BETWEEN 3 AND 4810
SELECT COUNT(*) FROM [MyTable] myTbl WITH(NOLOCK)
显然我宁愿使用前者而不是后者,因为后者冗余地重复FROM
子句(并且如果我有的话会重复任何WHERE
子句),但它的执行时间是如此之多我真的必须使用它。有没有办法可以让前者的执行时间缩短?
答案 0 :(得分:1)
CTE被内联到查询计划中。它们的执行与派生表完全相同。
派生表与物理操作不对应。它们不会将结果集“具体化”到临时表中。 (我相信MySQL会这样做,但MySQL是最原始的主流RDBMS。)
使用OVER()
确实在查询计划中表现为缓冲到临时表。完全不清楚为什么在这里比仅仅重新阅读基础表更快。缓冲相当慢,因为写入比SQL Server中的读取更加CPU密集。我们可以从原始表中读取两次。这可能是后一种选择更快的原因。
如果要避免重复查询的某些部分,请使用视图或表值函数。当然,这些对于一次性查询来说不是很好的选择。您还可以在应用程序层中生成SQL并重用字符串。 ORM也使这更容易。