值班期间研发同事打来电话,说应用有超时,上服务器上检查发现有SQL大批量地执行,该SQL消耗IO资源较多,导致服务器存在IO瓶颈,细看SQL,发现自己都被整蒙了,不知道这SQL是要干啥,处理完问题赶紧研究下。

SQL类似于:

WITH T1 AS
(
SELECT TOP ( 100 )
ID ,
ROW_NUMBER() OVER ( ORDER BY C1 ) AS RID
FROM [dbo].[TB002]
)
SELECT *
FROM T1
WHERE T1.RID > (1-1)*2147483647
AND T1.RID < 1*2147483647

第一赶脚是写这代码的研发同事想分页,但是这每页的数据量有点吓人啊(是我太胆小么?)

再仔细看下,赶脚又不是分页,上面还有TOP(100)呢?

如果把TOP(100) 放到CTE外面,很容易理解,根据RID列过滤完后再取前100行数据。

对于上面的TOP(100) 在CTE内部SQL执行步骤如下

1>对表TB002中C1列排序计算每行的RID值,得到临时结果集T1

2>对临时结果集T1中数据“随机”取100条(注意:因为CTE中TOP(100) 没有对应ORDER BY 子句,因此无法保证返回的100条数据是有序的,即使在不少场景下返回的数据是按RID排序的) 得到临时结果集T2

3>将临时结果集T2的数据按照T1.RID > (1-1)*2147483647 AND T1.RID < 1*2147483647 的条件过滤,得到最终结果集T3

4>强最终结果集T3返回给客户端

--=========================华丽分割线=======================================--

在SQL SERVER 世界里,ROW_NUMBER函数已经有些泛滥成灾,很多不明真相的群众磕着瓜子就把ROW_NUMBER函数写到应用查询中,甚至不少研发同事(抱歉有些人躺枪了)把ROW_NUMBER函数用到登峰造极的程度,当看到一条SQL里使用到N多ROW_NUMBER函数和子查询再加N多大表关联查询,我都对自己DBA的身份表示怀疑,完全看不懂啊!!!

--=========================华丽分割线=======================================--

回归正题,ROW_NUMBER函数的引入是为了更简单地实现分页,SQL SERVER 查询引擎会将CTE外部的条件引入到CET内部,以避免CTE内部语句执行时访问“无用”数据,如对下面的语句

;WITH T1 AS
( SELECT ID ,
ROW_NUMBER() OVER ( ORDER BY ID ) AS RID
FROM [dbo].[TB002]
)
SELECT *
FROM T1
WHERE T1.RID > 10
AND T1.RID < 30

由于表TB002上ID有索引,因此查询会利用索引访问前30条记录,丢弃不满足RID>10的第1到10条数据。

由于这种优化的存在,使得查询无需先执行

SELECT  ID ,ROW_NUMBER() OVER ( ORDER BY ID ) AS RID FROM  [dbo].[TB002]

然后再执行WHERE  T1.RID > 10 AND T1.RID < 30 的过滤操作。

但如果CTE内部加入TOP子句,就使得CTE外部的T1.RID > 10 AND T1.RID < 30条件不能引入到CET内部(查询优化器首先得保障返回结果集的正确性,然后才考虑执行的高效性)。对于研发同事也一样,他们首先关注查询结果是否正确,然后才考虑查询效率是否高效,那么引入TOP是否能保证数据正确呢?

为了掩饰,我们将查询做轻微调整如下:

;WITH T1 AS
(
SELECT TOP(10) ID ,
ROW_NUMBER() OVER ( ORDER BY ID ) AS RID
FROM [dbo].[TB002]
)
SELECT *
FROM T1
WHERE T1.RID > 10
AND T1.RID < 30

我们会悲哀地发现,查询返回结果为空,这显然不是一个好兆头,为什么会返回空呢?

轻轻推敲一下,我们就会发现,CTE内部的执行结果总是“巧合”地返回RID为1到10的数据,而外部条件RID>10又将这10条数据过滤掉,SO返回为空。

PS: 查询优化器真的是“顺手”返回前10条数据,因为恰好这10条数据“在手边”,不能保证其他场景下也是返回RID为1到10的数据,当然也不是查询优化器故意“坑人”哈

--=========================华丽分割线=======================================--

至此,我总算明白为啥要将写SQL的那位兄弟要传入入2147483647 这么大一个页数量,估计是传小了查不出数据,所以一劳永逸传个最大值,想想也是醉了!

--=========================华丽分割线=======================================

总结:

编写SQL的目的在于实现业务需求,而不是显示个人SQL能力,也没有“一招鲜吃遍天”可以秒杀所有问题的写法,在尊重业务需求的前提下,依据业务场景,考虑数据分布和当前以及未来的数据量,用尽可能简单的SQL地实现业务需求才是王道。

其实写博客的目的是发图,你们懂的!

图片来源网络,勿求粽子!

曲演杂坛--当ROW_NUMBER遇到TOP的更多相关文章

  1. 曲演杂坛--一条DELETE引发的思考

    原文:曲演杂坛--一条DELETE引发的思考 场景介绍: 我们有一张表,专门用来生成自增ID供业务使用,表结构如下: CREATE TABLE TB001 ( ID ,) PRIMARY KEY, D ...

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

    使用ROW_NUMBER来分页几乎是家喻户晓的东东了,而且这东西简单易用,简直就是程序员居家必备之杀器,然而ROW_NUMBER也不是一招吃遍天下鲜的无敌BUG般存在,最近就遇到几个小问题,拿出来供大 ...

  3. 曲演杂坛--使用CTE时踩的小坑:No Join Predicate

    在一次系统优化中,意外发现一个比较“坑”的SQL,拿出来供大家分享. 生成演示数据: --====================================== --检查测试表是否存在 IF(O ...

  4. 曲演杂坛--为什么SELECT语句会被其他SELECT阻塞?

    很多刚入门的DBA在捕获阻塞得时候,会问这么一个问题“为什么这个SELECT语句被那个SELECT语句阻塞了,难道不是共享锁么?” 让我们来做个小测试,首先准备一些测试数据: --========== ...

  5. 曲演杂坛--EXISTS语句

    通常在我写EXISTS语句时,我会写成IF EXISTS(SELECT TOP(1) 1 FROM XXX),也没细细考究过为什么要这么写,只是隐约认为这样写没有啥问题,那今天就深究下吧! 首先准备测 ...

  6. 曲演杂坛--特殊字符/生僻字与varchar

    对于中文版的SQL SERVER,默认安装后使用的默认排序规则为Chinese_PRC_CI_AS,在此排序规则下,使用varchar类型来可以“正常存取”存放中文字符以及一些东南亚国家的字符,同时v ...

  7. 曲演杂坛--SQLCMD下执行命令失败但没有任何错误提示的坑

    今天使用SQLCMD导入到SQL SERVER数据库中,看着数据文件都成功执行,但是意外发现有一个文件数据没有成功导入,但执行不报错,很容易导致问题被忽略. 使用存在问题的文件做下测试,从界面上看几行 ...

  8. 曲演杂坛--Update的小测试

    今天偶然想起一个UPDATE相关的小问题,正常情况下,如果我们将UPDATE改写成与之对应的SELECT语句,其SELECT查询结果应与UPDATE的目标表存在一对一的关系,例如: 对于UPDATE语 ...

  9. 曲演杂坛--使用TRY CATCH应该注意的一个小细节

    群里一个朋友遇到一个TRY CATCH的小问题,测试后发现是自己从来没有考虑的情况,写篇blog加深下印象 --============================================ ...

随机推荐

  1. Cesium原理篇:6 Renderer模块(1: Buffer)

    刚刚结束完地球切片的渲染调度后,打算介绍一下目前大家都很关注的3D Tiles方面的内容,但发现要讲3D Tiles,或者充分理解它,需要对DataSource,Primitive要有基础,而这要求对 ...

  2. 【集合框架】JDK1.8源码分析之HashMap & LinkedHashMap迭代器(三)

    一.前言 在遍历HashMap与LinkedHashMap时,我们通常都会使用到迭代器,而HashMap的迭代器与LinkedHashMap迭代器是如何工作的呢?下面我们来一起分析分析. 二.迭代器继 ...

  3. 微信公众账号 token 验证失败 解决办法

    问题:微信公众账号 开发过程中配置  token 提示 验证失败 如下图: 点击修改配置: 填写相关url与token(自定义):点击提交,会出现 出现这种情况,主要是对相关参数不熟悉,要了解url与 ...

  4. C#判断字符串是否是数字

    /// <summary> /// 判断字符串是否是数字 /// </summary> public static bool IsNumber(string s) { if ( ...

  5. C#中IList与List

    C#中IList<T>与List<T>的区别感想 写代码时对: IList IList11 =new List (); List List11 =new List (); 有所 ...

  6. Lind.DDD.ConfigConstants统一管理系统配置

    回到目录 Lind.DDD.ConfigConstants属于新添加的组件,用来帮助我们安全的进行配置消息的管理,我们在开发项目时,有个感觉,你的config配置项会越来越多,越来越难以阅读,随着你引 ...

  7. Xdebug文档(三)堆栈跟踪

    当xdebug激活时,PHP一旦要显示通知.警告或错误时,xdebug 显示堆栈跟踪信息.这个堆栈信息能跟据你的需要来配置显示. Xdebug显示的堆栈跟踪都是以保守数量状态显示信息.因为大量的信息处 ...

  8. [转载]T-SQL(MSSQL)语句查询执行顺序

    注意:笔者经过实验和查阅资料,已在原作基础上做了部分更改.更改不代表原作观点,查看原作请点击下方链接. 原文出处: 作者:张龙豪 链接:http://www.cnblogs.com/knowledge ...

  9. 对js中this的一点点理解

    1 当函数作为对象的方法被调用的时候 this就指向该对象 var o = { prop: 37, f: function() { return this.prop; } }; console.log ...

  10. SharePoint 2013 必备组件之 Windows Server AppFabric 安装错误

    1.如下图,在使用SharePoint2013产品准备工具的时候,网上下载安装Windows Server AppFabric的时候,报错,点击完成重启计算机,重新安装依然报错. 2.无奈之下,只有选 ...