4

我正在阅读有关如何有效地翻阅大型数据集的信息,因为我不满意Row_Number并且Fetch最糟糕。

这是文章: http ://www.4guysfromrolla.com/webtech/042606-1.shtml

现在这篇文章有这段代码:

CREATE  PROCEDURE [dbo].[usp_PageResults_NAI] 
(
    @startRowIndex int,
    @maximumRows int
)
AS

DECLARE @first_id int, @startRow int

-- A check can be added to make sure @startRowIndex isn't > count(1)
-- from employees before doing any actual work unless it is guaranteed
-- the caller won't do that

-- Get the first employeeID for our page of records
SET ROWCOUNT @startRowIndex
SELECT @first_id = employeeID FROM employees ORDER BY employeeid

-- Now, set the row count to MaximumRows and get
-- all records >= @first_id
SET ROWCOUNT @maximumRows

SELECT e.*, d.name as DepartmentName 
FROM employees e
   INNER JOIN Departments D ON
       e.DepartmentID = d.DepartmentID
WHERE employeeid >= @first_id
ORDER BY e.EmployeeID

SET ROWCOUNT 0

GO 

此演示代码看起来不错(与您看到的其他演示一样 :))。上面的代码之所以有效,只是因为他使用的是Order By employeeidin SELECT @first_id = employeeID FROM employees ORDER BY employeeid

假设我有一个名为的字段FirstName,并希望按此排序。那我怎么写上面的程序呢?上面的过程显然是行不通的,因为这样我们就不能写了,因为如果我们按名称订购WHERE employeeid >= @first_id就不能得到。first_id这是因为where在之前执行order by

如果我们将上述查询更改为:

Select * From (SELECT e.*, d.name as DepartmentName 
FROM employees e
   INNER JOIN Departments D ON
       e.DepartmentID = d.DepartmentID
ORDER BY e.EmployeeID) v WHERE employeeid >= @first_id

那么它会起作用,但这意味着,上面的查询会在更大的数据集上给出极差的性能。

那么,我们如何将上面的演示代码投入生产使用呢?任何帮助表示赞赏。

4

2 回答 2

1

您的问题似乎是您想使用另一种方法,因为ROW_NUMBER在对大型数据集中的非唯一字段进行排序时效果不佳。但是问题是,在对大型数据集中的非唯一字段进行排序时,您要使用的方法效果不佳,那么您可以做些什么来纠正它?

答案是,在非唯一字段上对大型数据集进行排序无论怎么做都不会很好,除非你很好地使用了索引。

只是为了证明这ROW_NUMBER比您在按主键排序的简单情况下给出的方法更好,我从您的链接创建了架构并添加了以下过程:

CREATE PROCEDURE dbo.usp_PagedResults_RowNumber
(
   @startRowIndex int,
   @maximumRows int
)
AS
WITH Emp AS
(   SELECT e.*, rn = ROW_NUMBER() OVER(ORDER BY e.EmployeeID)
    FROM employees e
)
SELECT  TOP (@MaximumRows) 
        EmployeeID, 
        LastName, 
        FirstName, 
        e.DepartmentID, 
        Salary,
        HireDate, 
        d.Name AS DepartmentName
FROM    Emp e
        INNER JOIN Departments D ON
           e.DepartmentID = d.DepartmentID
WHERE   rn >= @startRowIndex
ORDER BY EmployeeID;

然后我比较了两个查询:

EXECUTE usp_PageResults_NAI 4500, 20;
EXECUTE usp_PagedResults_RowNumber 4500, 20;

首先查看IO统计,您发布的过程如下:

Table 'Employees'. Scan count 1, logical reads 48
(1 row(s) affected)

(20 row(s) affected)
Table 'Departments'. Scan count 1, logical reads 41
Table 'Employees'. Scan count 1, logical reads 2

(1 row(s) affected)

所有统计数据都以结尾,physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.因此我将其从所有统计数据中删除以提高可读性。

使用 ROW_NUMBER() 时的统计信息:

(20 row(s) affected)
Table 'Departments'. Scan count 1, logical reads 41
Table 'Employees'. Scan count 1, logical reads 48

(1 row(s) affected)

两者之间没有太多的区别,但是 ROW_NUMBER 稍微好一点,索引扫描少了 1 次,逻辑读取次数少了 2 次。

接下来看执行计划。使用 Rowcount 大约占查询成本的 84%:

在此处输入图像描述

而 ROW_NUMBER 只占区区 16%。

在此处输入图像描述

就实际执行时间而言,鉴于数据的大小,我无法得出合理的结论,因为两者都执行得如此之快。我只能假设更简单的计划,更低的 IO 也使 ROW_NUMBER 更快。

因此,与其试图将方形钉钉入圆孔并使用这种 ROWCOUNT 方法对 FirstName 等字段进行分页,不如在 FirstName 上创建索引以在进行排序时辅助 ROW_NUMBER。例如

CREATE NONCLUSTERED INDEX IX_Employees_FirstName ON dbo.Employees (FirstName ASC) INCLUDE (DepartmentID);

为了证明我运行了这个查询的区别:

SELECT  EmployeeID, 
        DepartmentID, 
        RowNumber = ROW_NUMBER() OVER(ORDER  BY FirstName, EmployeeID)
FROM    Employees;

在添加索引之前:

Table 'Employees'. Scan count 1, logical reads 501

在此处输入图像描述

Table 'Employees'. Scan count 1, logical reads 249

在此处输入图像描述

这表明排序能够使用将逻辑读取次数减半的索引。

于 2013-10-01T13:51:34.143 回答
0

我尝试了多种不同的 SP 分页方式,但没有什么能有效地为我工作。部分原因是在我的情况下,如果我使用所有标准、条件、排序、过滤等,SP 非常复杂并且运行缓慢 - 所以我无法在每个新页面请求中重复所有这些。

我实现它的方式 - 我确实使用所有标准、条件、排序、过滤等运行它,但我只运行一次 - 并检索所有行,但我没有获取所有列,而是只检索主键(顺便说一句,与所有列 rereival 相比,它加快了运行速度)。

在我的 .NET 代码中,我将该列表存储在Generic List (of integer). 每次用户请求一个新页面时 - 我都会将一个 ID 列表(一次说 50 个)传递给 SP。然后,SP 不会对条件、排序、过滤等执行完全搜索,而是简单地对该列表进行 INNER JOIN。

这种方法非常有效,因为所有条件(包括记录顺序)都已保存在 ID 列表中。它也很灵活,我可以随时通过简单地一次提供不同数量的 ID 轻松更改“每页行”。

于 2013-10-01T13:56:15.210 回答