SQL调优学习之——sqlserver分页从低效到高效

背景

  以前都是使用mysql和oracle,对sqlserver的使用不多。最近因项目原因,要读取其他项目的数据库,取出某个门的开关历史记录,而对方使用的是sqlserver,所以研究起了sqlserver的分页,经过几次实践,慢慢的从低效的分页写到了高效的分页。

表结构

history表

历史记录ID:id(唯一引索)

操作时间:time

开门或关门:flag

由谁操作:user_Id

属于哪个设备:device_id

下面我们先介绍四种分页的方法,以【一种低效】——【两种较高效】——【一种高效】的顺序进阶的介绍,在最后再附上测试结果。

低效的sql

思路:

最里层:先从history表根据时间倒序查出前50010条记录

中间层:从以上的查询结果中根据时间正序查出前10条记录,一正一反刚好就拿出了第10000条到10010条记录了。

最外层:根据时间倒序拿出以上的查询结果

SQL代码:

select * from
(
    select top 10 * from
    (
        select top 50010 * from history
        order by
        time desc
    ) h
    order by
    h.time asc
) hh
order by
hh.time desc

经下面的检验,这种查询效率比较低下。

其原因是因为每一层的查询都使用了select * ,即扫描所有的这段,但是 “最里层” 和 “中间层” 根本就没必要select * ,这两层目的只是为了把最后一层的搜索范围定位在第10000-10010条之间,所以,在这两层里,我们只要拿出关键字ID和排序字段time就好。

较高效的SQL(1)————使用where ... =

最里层:先从history表根据时间倒序查出前50010条记录,只拿出id和time

中间层:从以上的查询结果中根据时间正序查出前10条记录,只拿出id和time,一正一反刚好就拿出了第10000条到10010条记录了。

最外层:根据时间倒序拿出以上的查询结果,select *拿出所有字段,查询范围用where ... = ... 来匹配。

SQL代码:

select * from history hh,
(
    select top 10 id ,time from
    (
        select top 50010 id ,time from history
        order by
        time desc
    ) h
    order by
    h.time asc
) hhh
where hhh.id = hh.id
order by
hhh.time desc

经检验,这种分页 方法比上一种快一点点。

主要原因在与“最里层”和“中间层”的两次查询,都只是查出id和time,而不是select * ,从这个角度讲提升了效率。但最后又用了where...=语法,比起上一种分页方法,又降低了一点效率。但是where...=语法速度很快,所以总体上还是这种分页方法更快一些。

较高效的分页(2)————使用where...in

最里层:先从history表根据时间倒序查出前50010条记录,只拿出id和time

中间层:从以上的查询结果中根据时间正序查出前10条记录,只拿出id和time,一正一反刚好就拿出了第10000条到10010条记录了。

最外层:根据时间倒序拿出以上的查询结果,select *拿出所有字段,查询范围用where...in ()来匹配

SQL代码:

select * from history hh
where id in
(
    select top 10 id from
    (
        select top 50010 id ,time from history
        order by
        time desc
    ) h
    order by
    h.time asc
)
order by
hh.time des

这种分页方法,与上一种分页方法比起来,区别是这种使用了where...in(),而不是where...=,原理上差别不大,但可能是SQLServer内部优化的原因,使用where...in比使用where...=要快一些。具体在下面的计较表格可以看出。

高效的分页————使用row_number() over

最里层:查询出前50010条数据,只拿出ID字段,同时使用row_number() over 语法,增加一个n字段,代表该条数据时第几行。

最外层:根据where...=来匹配id,同时直接拿出 n>50000 的数据。

SQL代码:

select hhh.n , hh.* from history hh ,
(
    select top 50010 row_number() over
    (
        order by
        time desc
    ) n,id
    from history
) hhh
where hhh.id = hh.id
and hhh.n > 50000
order by
hhh.n desc

这种分页方法,首先只是两次查询,这无非提高了效率。最里层查询出来的“虚列”——n,sqlserver不知道会不会为其加上索引,个人认为会,但想不出什么验证的方法。假如有加入索引的话,那在这个地方,使用 “n >某个数字” 的查询方法,又比前几次查询快了一点。

几种分页方法速度比较

以下进行两种测试,第一种是查询出1000-1010条数据,第二种是查询出第50000-50010条数据。记录的秒数是查询50次总共的用时,每组测试5次,最后取平均值。

【查询1000-1010条数据】 第一次 第二次 第三次 第四次 第五次 平均
低效的分页 6.344s 5.687s 5.797s 5.704s 5.641s 5.835s
较高效的分页(1)——where...= 4.485s 5.281s 5.094s 5.281s 5.313s 5.091s(胜出)
较高效的分页(1)——where...in 5.093s 5.328s 5.14s 5.406s 5.297s 5.253s
高效的分页——row_number() over 5.437s 5.39s 5.156s 5.016s 5.344s 5.269

从中可以看出,在查询的行数较少时,使用“较高的分页(1)——where...=”是最快的一种分页方法。

【查询50000-50010条数据】 第一次 第二次 第三次 第四次 第五次 平均
低效的分页 10.844s 9.985s 10.172s 10.0s 10.297s 10.260s
较高效的分页(1)——where...= 9.625s 9.469s 9.14s 9.171s 9.219s 9.325s
较高效的分页(1)——where...in 9.156s 9.61s 9.187s 9.218s 9.219s 9.278s
高效的分页——row_number() over 7.844s 6.765s 6.422s 7.359s 6.875s   7.053s(胜出)

从中可用看出,在查询的行数较多时,使用"高效的分页——row_number() over"是最快的一种分页方法,而且快了好几个档次!

时间: 2024-10-12 16:34:30

SQL调优学习之——sqlserver分页从低效到高效的相关文章

数据库学习——SQL调优

一.SQL调优与查询优化器 数据库性能调优一般从发现.分析和解决SQL语句执行中的问题着手,这个过程统称为SQL调优(SQL tuning). 1.了解查询优化器 例如,有些查询优化器对于连接操作,一般按连接对象在FROM子句中出现的先后次序进行连接.SQL程序设计者可以利用此特点将选择性高的小表放在前面,大表放在后面,以尽快淘汰无用的中间结果,从而节省查询处理开销.又如,有些查询优化器中,凡是查询条件用OR连接的,就一概不用索引,因而只能用全表扫描,可以考虑使用UNION ALL代替OR. 2

SQL调优

# 问题的提出 在应用系统开发初期,由于开发数据库数据比较少,对于查询SQL语句,复杂视图的的编写等体会不出SQL语句各种写法的性能优劣,但是如果将应用 系统提交实际应用后,随着数据库中数据的增加,系统的响应速度就成为目前系统需要解决的最主要的问题之一.系统优化中一个很重要的方面就是SQL语句的优 化.对于海量数据,劣质SQL语句和优质SQL语句之间的速度差别可以达到上百倍,可见对于一个系统不是简单地能实现其功能就可,而是要写出高质量的 SQL语句,提高系统的可用性. 在多数情况下,Oracle

基于SQLServer2008的SQL调优(转)

1. 前言 SQL是程序设计人员与数据库进行沟通的标准语言,在数据库应用程序中,使用最多的访问数据库的方法就是SQL语言.SQL性能的调整涉及到SQLServer 2008的方方面面,优化器的选择.内存参数的设定.SQL语句的写法等.本文主要从SQL语句的优化方面进行阐述,给出了一些可行的调整数据库应用性能的策略与方法,并以SQLServer 2008为平台对列举规则给予了验证. 2. 实验环境 硬件环境:Inter(R) Core(TM)2 Duo CPU P7450 @2.12GHz .2G

MySQL索引和SQL调优

MySQL索引 MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等.为了避免混乱,本文将只关注于BTree索引,因为这是平常使用MySQL时主要打交道的索引. MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构.提取句子主干,就可以得到索引的本质:索引是数据结构. MySQL索引原理 索引目的 索引的目的在于提高查询效率,可以类比字典,如果要查“mysql”这个单词,

SQL调优日志--内存问题

SQL调优日志--内存问题排查入门篇 概述 很多系统的性能问题,是由内存导致的.内存不够会导致页面频繁换入换出,IO队列高,进而影响数据库整体性能. 排查 内存对数据库性能非常重要.那么我当出现问题的时候,我们怎么排查性能问题呢? 存在问题 主要查看2个部分.页生命周期 Page Life Expectancy,和  lazy writer /sec. 页生命周期 的参考值在很久很久以前,很多同学可能看到过,建议值是300s.但是这是基于32位操作系统,最大只能使用4GB内存给出的. Jonat

Oracle SQL 调优健康检查脚本

Oracle SQL 调优健康检查脚本 我们关注数据库系统的性能,进行数据库调优的主要工作就是进行SQL的优化.良好的数据架构设计.配合应用系统中间件和写一手漂亮的SQL,是未来系统上线后不出现致命性能问题的有力保证. 在CBO时代,一个SQL的执行计划是多样的.影响执行计划的因素也从过去RBO时代的SQL书写规则变为综合性因素.这为我们生成更加优秀执行计划提供了基础,同时也给我们进行调优带来的很多麻烦. 目前我们通常的做法,是通过AWR报告或者调试手段,发现某某SQL有问题,之后从Librar

11g新特性-自动sql调优(Automatic SQL Tuning)

11g新特性-自动sql调优(Automatic SQL Tuning) 在Oracle 10g中,引进了自动sql调优特性.此外,ADDM也会监控捕获高负载的sql语句. 在Oracle 11g中,通过运行sql tuning advisor加强了自动sql调优功能.默认情况下是每天夜间通过自动任务的方式运行"自动sql调优". 自动sql调优的过程: 1.识别需要调优的sql语句  根据AWR中的CPU和I/O负载来识别 2.调优,生成新的sql profile 在维护窗口(mai

SQL调优常用方法

在使用DBMS时经常对系统的性能有非常高的要求:不能占用过多的系统内存和 CPU资源.要尽可能快的完成的数据库操作.要有尽可能高的系统吞吐量.如果系统开发出来不能满足要求的所有性能指标,则必须对系统进行调整,这个工作被称为调优.绝定DBMS的性能的因素有两个因素:硬件和软件.使用频率高的CPU.使用多处理器.加大内存容量.增加Cache.提高网络速度等这些都是非常有效的硬件调优方式,不过对硬件进行调优对系统性能的提高是有限的,如果有非常好的硬件条件但是如果编写的SQL质量非常差的话系统的性能并不

oracle sql调优集

************************************************************ 1.新建调优集对象 ************************************************************ ---授权 grant ADMINISTER ANY SQL TUNING SET to scott; ---删除存在的STS BEGIN DBMS_SQLTUNE.DROP_SQLSET( sqlset_name => 'OCPYAN