Top与ROW_NUMBER

论Top与ROW_NUMBER读取第一页的效率问题

前一段时间研究关于分页的问题,由于数据库属于百万级的,考虑了关于优化方面的问题。其中一个考虑是:第一页展现的频率肯定是最高的,所以我想第一页就使用Top N来读取。

这个想法本身是没有错,因为通常我读取某条件下的N条记录我一直都是使用Top N。后面拿Top N和分页读取第一条进行效率比较,发现分页的效率居然还高一些,以下是测试代码:

USE [d_study];
GO

SET STATISTICS IO ON;
SET NOCOUNT ON;
GO

DECLARE @BeginTime datetime;
DECLARE @EndTime datetime;
DECLARE @ExecTime int;
DECLARE @ExecNum int;

SET @ExecNum = 1;
SET @ExecTime = 0;

-- 测试Top读取第一页的执行时间
WHILE @ExecNum <= 30
    BEGIN

        SET @BeginTime = getdate();
        SELECT TOP 30 * FROM users WHERE nID>2000 And nID<50000 ORDER BY nID DESC;
        SET @EndTime = getdate();

        SET @ExecTime = @ExecTime + datediff(ms,@BeginTime,@EndTime);

        SET @ExecNum = @ExecNum + 1;

        CHECKPOINT;  /*写脏的缓冲入磁盘*/
        DBCC FREEPROCCACHE WITH NO_INFOMSGS; /*清除执行计划*/
        DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS; /*清除缓冲数据*/

    END

PRINT ‘TOP平均执行速度:‘ + Cast((@ExecTime / 30) AS varchar(10)) + ‘毫秒‘;

--测试分页读取第一页的执行时间

SET @ExecNum = 1;   --重置执行次数
SET @ExecTime = 0;  --重置记录时间

WHILE @ExecNum <= 30
    BEGIN

        Set @BeginTime = getdate();

        SELECT * FROM (SELECT ROW_NUMBER() OVER(ORDER BY nID asc) AS rownum,* FROM users Where nID>2000 And nID<50000 ) AS D
        WHERE rownum>0 AND rownum<31;

        Set @EndTime = getdate();

        SET @ExecTime = @ExecTime + datediff(ms,@BeginTime,@EndTime);

        SET @ExecNum = @ExecNum + 1;

        CHECKPOINT;  --写脏的缓冲入磁盘
        DBCC FREEPROCCACHE WITH NO_INFOMSGS; --清除执行计划
        DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS; --清除缓冲数据

    END

Print ‘分页类似于TOP效果:‘ + Cast((@ExecTime / 30) AS varchar(10)) + ‘毫秒‘;
GO

SET NOCOUNT OFF
SET STATISTICS IO OFF

修改读取的记录数N和修改读取条件的范围值,依然是分页效率更高。

查看了不少的资源和做了各种跟踪,均没有找到满意的答案。

最后我分析了这两条SQL,他们的不同之处在于“提取N条记录”的这个操作,Top N和 WHERE rownum>0 AND rownum<31。

它们具体是如何运行我讲不出理论,但是我可以做个比方:

体育老师让我们跑30步的距离,Top N的做法就是跑30步,自己边跑边数;WHERE rownum>0 AND rownum<31 相当于老师在30步的位置花了个标记,你只管死跑,到了那个标记就相当于跑了30步。我想做了标记死跑这个要快点吧,:)

分类: SQL

时间: 2024-11-07 21:43:40

Top与ROW_NUMBER的相关文章

LINQ to SQL 模拟实现 ROW_NUMBER() OVER(ORDER BY ...) 的功能

?  前言 本来是想使用 LINQ 实现类似 SQL: ROW_NUMBER() OVER(ORDER BY -) 的功能,但是貌似 LINQ 不支持,反正没找到解决办法,无奈使用了LINQ Select() 方法实现. 1)   需求,需要实现一下 SQL: SELECT TOP 10 ROW_NUMBER() OVER(ORDER BY T.TotalAmount DESC) AS SN, * FROM ( SELECT T2.Name, SUM(T2.Amount) AS TotalAmo

MySQL、SqlServer、Oracle三大主流数据库分页查询 (MySQL分页不能用top,因为不支持)

一. MySQL 数据库 分页查询MySQL数据库实现分页比较简单,提供了 LIMIT函数.一般只需要直接写到sql语句后面就行了.LIMIT子 句可以用来限制由SELECT语句返回过来的数据数量,它有一个或两个参数,如果给出两个参数, 第一个参数指定返回的第一行在所有数据中的位置,从0开始(注意不是1),第二个参数指定最多返回行数.例如:select * from table WHERE … LIMIT 10; #返回前10行select * from table WHERE … LIMIT

曲演杂坛--蛋疼的ROW_NUMBER函数

使用ROW_NUMBER来分页几乎是家喻户晓的东东了,而且这东西简单易用,简直就是程序员居家必备之杀器,然而ROW_NUMBER也不是一招吃遍天下鲜的无敌BUG般存在,最近就遇到几个小问题,拿出来供大家娱乐下. ---====================================================== 问题1:为什么加WHERE条件就慢,不加反而快? 查询SQL: WITH Temp AS( SELECT * , ROW_NUMBER()OVER(ORDER BY T2.

网上乱七八糟一堆分页测试汇总

网上关于分页有大堆各种各样的,有人说这个这个处理大数据性能好,有人说那个性能好,听别人说没啥用,自己测试下才知道,但是我只测试除了第一个在查询分页后段的时候真的是不行,其他的在下面的测试中是没问题,也许是下面的例子并不能真实的测出,本来是实力有限,欢迎批评指出, 然后有集中分页方式,然后下面有几种分页方式在稍微复杂点的查询面前,你就会想死,所以就算性能再好写我也是不太喜欢用的 首先先创建两张简单测试表,student, class create table student ( sno int ,

SQL Server-聚焦计算列或计算列持久化查询性能(二十二)

前言 上一节我们详细讲解了计算列以及计算列持久化的问题,本节我们依然如前面讲解来看看二者查询性能问题,简短的内容,深入的理解,Always to review the basics. 持久化计算列比非持久化计算列性能要好 我们开始创建两个一样的表并都插入100条数据来进行比较,对于计算列我们重新进行创建计算列和非计算列持久化. CREATE TABLE [dbo].[ComputeColumnCompare] (ID INT, FirstName VARCHAR(100), LastName C

SQL Server的三种物理连接之Hash Join(三)

简介 在 SQL Server 2012 在一些特殊的例子下会看到下面的图标: Hash Join分为两个阶段,分别为生成和探测阶段. 首先是生成阶段,将输入源中的每一个条目经过散列函数的计算都放到不同的Hash Bucket中,其中Hash Function的选择和Hash Bucket的数量都是黑盒,通常来讲,查询优化器都会使用连接两端中比较小的哪个输入集来作为第一阶段的输入源. 接下来是探测阶段,对于另一个输入集合,同样针对每一行进行散列函数,确定其所应在的Hash Bucket,在针对这

常用数据库分页

1.Oracle 分页 第一种:利用分析函数row_number() 方法 select * from(    select t.*,row_number() over (order by t1.id) rowno from TABLE1)where rowno between 21 and 40; 第二种:直接使用rownum 虚列(推荐) select * from(select t.*,rownum as rowno from TABLE1 )where rowno between 10

SQL 分页 SQL SERVER 2008

public IList<UserInfo> GetPageList(int pageSize, int pageIndex, out int totalItemCount) { var totalSql = "SELECT COUNT(1) FROM UserInfo"; Object result = GetScalar(totalSql); totalItemCount = result == null ? 0 : ConvertHandler.ToInt32(res

Oracle、Mysql、Sql Server语句的区别

1.空值的处理——判断是否为空,为空时取一个值,不为空时取另一个值 1).Sql Server 中 ISNULL(check_expression,replacement_value) 解释:如果check_expression值为空,则返回replacement_value,否则直接返回check_expression的值 2).Mysql IFNULL(expr1.expr2) 解释:如果值expr1值为空,则返回expr2,否则直接返回expr1的值 3).Oracle NVL(expr1