将业务语句翻译成SQL语句不仅是一门技术,还是一门艺术。

下面拿我们程序开发工程师最常用的ROW_NUMBER()分页作为一个典型案例来说明。

先来看看我们最常见的分页的样子:

WITH CTE AS(
SELECT ROW_NUMBER() OVER ( ORDER BY (A.CreateTime ) AS OrderNo ,
Table_A.ID , --主键
Table_A.其它字段
FROM Table_A WITH ( NOLOCK )
WHERE RecID = 220051
)
SELECT * FROM CTE
WHERE OrderNo BETWEEN 1 AND 50;

的确,这样的写法很符合我们的思维逻辑,并且我们在RecID上建立非聚集索引,那么它的效率看上去也是不错的。当然根据这条SQL,最佳索引实践应该是:

CREATE INDEX IX_Table_A_RecID_CreateTime_Inc
ON Table_A(RecID,CreateTime)
INCLUDE(Table_A.其它字段)

但是,这真的是最佳的了吗?当SQL的Where条件变多,Table_A.其它字段变得越来越多,OVER()子句中的OrderBy字段越来越多或者变成Order By ColumnA/ColumnB这样的计算表达式,这条语句变得越来越不堪重负,最终性能问题凸现出来,另外,作为DBA,我们总是尽量维持索引的简单性、可重用度,而不想建立成为某个语句专用的索引。举例来说,在Include中,我们总不能把Table_A.其它字段中的所有字段都放进去吧,个数少还行,如果遇上几十个字段或者有大容量字符字段,维护成本将大大增加,那将是我们不愿意看到的。

 
这个时候就要求我们看看是否能对语句做出一些优化了。
 
在上面的SQL中,我们看它的执行计划,我已经建立了索引,该索引并未Include SELECT列表中的其它字段:
CREATE INDEX IX_Table_A_RecID_CreateTime_Inc
ON Table_A(RecID,CreateTime)
 

根据上图的执行计划,可以看到,WHERE条件走的是我刚刚建立的索引,下面的键查找与其并行,我们先不讨论该执行计划的具体细节,下面我们来设想几个问题:

在WHERE条件简单,并且索引合适,统计信息正确的前提下,SQL Server可以很容易获得那50行,并且回到聚集索引中找到属于它的其它字段的数据,这是SQL Server的智能编译的结果,也是我们希望看到的返回方式。

但是,在WHERE条件较为复杂,多个WHERE条件均为范围字段或者状态字段时,执行计划也许并没有我们想象的那么智能了,比如它可能采用这样的方式:

当SQL Server无法准确的取出你要的那些行时,那么它便会取回全部的行数后,再去聚集索引中找回属于它的其它字段的数据,当where条件可以返回几十万数据时,你可以想象它的效率有多低,它会仍然使用上文中类似的执行计划,这显然不是我们希望看到的。

我们想看到的是什么?

1、根据WHERE条件和排序规则,先取出那50条数据所属的主键。

SELECT ROW_NUMBER() OVER ( ORDER BY A.CreateTime ) AS OrderNo ,
Table_A.ID --主键

INTO #1
FROM Table_A WITH ( NOLOCK )
WHERE RecID = 220051

2、利用上个步骤中返回的主键,去原始表取回这50条记录的其它字段数据。

SELECT B.*,A.其它字段 FROM Table_A A WITH ( NOLOCK )
INNER JOIN #1 B ON A.ID=B.ID
WHERE B.OrderNo BETWEEN 1 AND 50;

那么,上面两个步骤合在一起:

WITH CTE AS(
SELECT ROW_NUMBER() OVER ( ORDER BY A.CreateTime ) AS OrderNo ,
Table_A.ID --主键
FROM Table_A WITH ( NOLOCK )
WHERE RecID = 220051
)
SELECT CTE.*,A.其它字段 FROM Table_A A WITH ( NOLOCK )
INNER JOIN CTE ON A.ID=CTE.ID
WHERE CTE.OrderNo BETWEEN 1 AND 50;

很好,现在我们再来看一下这个SQL的执行计划:

Binggo!这才是我们理想中的样子!

针对这个SQL,我们只需要建立一个合适的索引,而不用顾忌SELECT列表中那些烦人的其它列,因为他们回聚集索引取数据,也不过几百个IO而已(需要返回的行数*Index_Level)。它不需要再为过期的统计信息或者错误的执行计划而付出沉重的代价!

总结:SQL优化,是一门艺术。

SQL优化案例—— RowNumber分页的更多相关文章

  1. C# SQL优化 及 Linq 分页

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不 ...

  2. mysql的sql优化案例

    前言 mysql的sql优化器比较弱,选择执行计划貌似很随机. 案例 一.表结构说明mysql> show create table table_order\G***************** ...

  3. SQL 优化案例 1

    create or replace procedure SP_GET_NEWEST_CAPTCHA( v_ACCOUNT_ID in VARCHAR2, --接收短信的手机号 v_Tail_num i ...

  4. SQL 优化案例

    create or replace procedure SP_GET_NEWEST_CAPTCHA( v_ACCOUNT_ID in VARCHAR2, --接收短信的手机号 v_Tail_num i ...

  5. SQL夯实基础(四):子查询及sql优化案例

    首先我们先明确一下sql语句的执行顺序,如下有前至后执行: (1)from  (2) on   (3) join  (4) where  (5)group by  (6) avg,sum...  (7 ...

  6. [MySQL优化案例]系列 — 分页优化

    通常,我们会采用ORDER BY LIMIT start, offset 的方式来进行分页查询.例如下面这个SQL: SELECT * FROM `t1` WHERE ftype=1 ORDER BY ...

  7. 数栈SQL优化案例:隐式转换

    MySQL是当下最流行的关系型数据库之一,互联网高速发展的今天,MySQL数据库在电商.金融等诸多行业的生产系统中被广泛使用. 在实际的开发运维过程中,想必大家也常常会碰到慢SQL的困扰.一条性能不好 ...

  8. sqlserver sql优化案例及思路

    始sql: SELECT TOP 100 PERCENT ZZ.CREW_NAME AS 机组, ZZ.CREW_ID, AA.年度时间, CC.当月时间, DD.连续七天时间 AS 最近七天 FRO ...

  9. 百倍性能的PL/SQL优化案例(r11笔记第13天)

    我相信你是被百倍性能的字样吸引了,不过我所想侧重的是优化的思路,这个比优化技巧更重要,而结果嘛,其实我不希望说成是百倍提升,“”自黑“”一下. 有一个真实想法和大家讨论一下,就是一个SQL语句如果原本 ...

随机推荐

  1. 读书笔记:《HTML5开发手册》--HTML5新的结构元素

    读书笔记:<HTML5开发手册> (HTML5 Developer's CookBook) 虽然从事前端开发已有很长一段时间,对HTML5标签也有使用,但在语义化上面理解还不够清晰.之前在 ...

  2. C# 注册 Windows 热键

    闲扯: 前几日,一个朋友问我如何实现按 F1 键实现粘贴(Ctrl+V)功能,百度了一个方法,发给他,他看不懂(已经是 Boss 的曾经的码农),我就做了个Demo给他参考.今日得空,将 Demo 整 ...

  3. JavaScript中fn()和return fn()

    看博客时,注意到return的重要性 参考:http://www.cnblogs.com/raoyunxiao/p/5644032.html 看似反常的例子: var i = 0; function ...

  4. Hyper-V 激活Windows系统重启后黑屏的解决方法 + 激活方法

    异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 服务器相关的知识点:http://www.cnblogs.com/dunitia ...

  5. 使用Oracle官方巡检工具ORAchk巡检数据库

    ORAchk概述 ORAchk是Oracle官方出品的Oracle产品健康检查工具,可以从MOS(My Oracle Support)网站上下载,免费使用.这个工具可以检查Oracle数据库,Gold ...

  6. vmware上网的方式

    vmware上网设置 vmware虚拟机上网设置 我的一些心得,如下: 如何使vmware虚拟机中的操作系统能够上网? 第一种情况: 主机使用PPPOE拨号上网 方法一:NAT方式 1.先关闭虚拟机中 ...

  7. 微框架spark--api开发利器

    spark简介 Spark(注意不要同Apache Spark混淆)的设计初衷是,可以简单容易地创建REST API或Web应用程序.它是一个灵活.简洁的框架,大小只有1MB.Spark允许用户自己选 ...

  8. 终端mysql Operation not permitted错误解决方案

    前言 前段时间装mysql,就遇到了ln: /usr/bin/mysql: Operation not permitted的错误,网上好多方法都过时了,下边是我的解决方法 原因 这是因为苹果在OS X ...

  9. html中返回上一页的各种写法【转】

    超链接返回上一页代码: <a href="#" onClick="javascript :history.back(-1);">返回上一页</ ...

  10. iOS之应用版本号的设置规则

    版本号的格式:v<主版本号>.<副版本号>.<发布号>  版本号的初始值:v1.0.0 管理规则: 主版本号(Major version) 1.  产品的主体构件进 ...