【100分】真正的疑难问题,相同的语句,效率天差地别!!!!!!!!!!!

丰云 2012-05-22 11:31:40
如题,下面的是相关说明:

--视图V_test:
select a.1,a.2,a.3,b.1,b.2,b.3 from a inner join b on a.0=b.0

--语句1:
Select top 15 * FROM
(
select ROW_NUMBER() Over(order by id) as rowId, * from V_test
where type=123 and zt='测试'
) as tempTable where rowId > 0

--语句2:
Select top 15 * FROM
(
select ROW_NUMBER() Over(order by id) as rowId, * from V_test
where type=123 and zt='测试'
) as tempTable where rowId > 15
--说明,所有相关子段都加了索引,视图数据总量277万。
--语句1瞬间就出来了,语句2接近一分钟才出结果
--查看数据库记录,语句2逻辑读非常高
--为什么?两个语句完全一样,除了最后那个分页起始条件不同!
--求教各路高手不吝赐教啊
...全文
134 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
阿宣22 2012-09-11
  • 打赏
  • 举报
回复
为什么这样就会快了呢?…… 逻辑上不都是先生成中间表吗?
能不能把修改后的执行计划图贴出来
  • 打赏
  • 举报
回复

SQL Server的查询优化器是一个基于成本的优化器。它为一个给定的查询分析出很多的候选的查询计划,并且估算每个候选计划的成本,从而选择一个成本最低的计划进行执行。实际上,因为查询优化器不可能对每一个产生的候选计划进行优化,所以查询优化器会在优化时间和查询计划的质量之间进行一个平衡,尽可能的选择一个“最优”的计划。
  所以,查询优化器成为SQL Server中最重要的一个组件,并且影响着SQL Server的性能。选择正确或错误的执行计划意味着查询执行时间可能存在着毫秒的,几分钟,甚至几个小时之间的差异。
  了解查询优化的内部机制,可以帮助DBA和开发人员能够编写更好的查询,或者给查询优化器提供信息使得它可以产生有效的执行计划。本系列文章讲述的查询优化器的内部运作的知识,此外,还会告诉你如何使用查询优化器的相关信息进行性能诊断。
  下面,我们首先来看看:查询优化器是如何工作的。
  在SQL Server数据库引擎的核心是两个主要部分组成:存储引擎和查询处理器(也被称为关系引擎)。存储引擎负责在磁盘和内存之间以最优化的方式读取数据,同时维护数据的完整性。查询处理器,顾名思义,接受提交给SQL Server所有的查询,并且为产生他们的最佳执行计划,然后执行该计划,并提供所需的结果。
  我们将查询以T-SQL的形式提交给SQL Server。因为SQL语句是一个高层抽象的声明性的语言,它仅仅只是定义了要从数据库中获取什么样的数据,而没有告诉如何去获取这些数据(或者说,没有定义获取数据的方法和步骤)。所以,对于SQL Server所接受到的每一个查询,查询处理器的首要任务就是产生一个计划,这个计划就描述了如何去执行查询,之后就由存储引擎去执行这个计划了。
  为了确保已经达到在查询处理器认为是最好的计划执行查询,查询处理器执行不同的步骤,整个查询处理过程如图所示:


  当然,上面的图只是一个最简单的示例图,下面,给大家看另外一个图,体会一下一个查询处理的过程:

  我们在后续的文章中会看到每一个步骤的详细讲解与应用,下面我们就简单的介绍图中的一些步骤(为了简单起见,我们以第一幅图为例子)
  1. Parsing 和Binding(解析与绑定):在一个查询提交给了数据库之后,首先就要被进行语法的解析,如果这个查询的语法是没有问题的,那么这个Parsing过程的输入结果就是一个逻辑树,在这个逻辑树种每一个节点都表示了这个查询进行的每个操作,例如读取某个表,进行inner join等。
  下面,给大家看一个逻辑树的例子,对于下面的查询:
  产生的逻辑树如下:


  这个过程就是编译原理的一个文法词法的解析。
  谈完了Parsing,之后的操作就是Binding了,这个操作现在改名字为Algebrizer。这个操作主要就是检查解析产生的逻辑树中的对象是否存在,例如Customer是否是数据库中的表,CustomerID字段是否在Customer表中等。
  经过了这个Binding之后,就会产生另外一个树形的数据结构,传递给下一个步骤。
  2. 查询优化。这个过程主要是使用上述过程中的Algebrizer Tree进行优化的处理过程,我们这里大体的可以将这个优化的处理过程分为两个步骤:
  a. 产生执行计划。在这个过程中,查询优化器会使用之前的树,产生执行计划。这个过程主要是将树上的逻辑操作转换为物理操作(其实就是存储引擎可以调用的方法,这些方法就是实实在在的去读取数据的)。
  b. 估算每个执行计划的成本。一个逻辑操作可以有很多的物理操作与其对应,而每个物理操作的成本不一样,同时,也没用所谓的“什么物理操作比其他的物理操作更优” ,一切视情况可认定。在这个过程中产生很多的候选执行计划,并且查询优化器会综合考虑很多的情况,选择一个它认为“比较优”的计划,传递给存储引擎。
  3. 查询的执行与计划的缓存。这个过程比较简单了,主要是存储引擎去执行执行计划,同时为了避免相类似的SQL查询重新编译,使用过的执行计划会被缓存在计划缓存池中。
  基本是,我们可以看出,查询优化的过程就是一个将逻辑操作映射为物理操作的过程。
丰云 2012-05-22
  • 打赏
  • 举报
回复

--刚才发的有错,重发
IF OBJECT_ID (N'tempdb.dbo.#tempTable', N'U') IS NOT NULL
DROP TABLE #tempTable;
select ROW_NUMBER() Over(order by id) as rowId, * into #tempTable from V_test where type= @type and zt = @zt;
select * FROM #tempTable where rowId > @start and rowId <= @end;
丰云 2012-05-22
  • 打赏
  • 举报
回复

--在公司DB的建议下,改成用临时表,问题解决
--但心里还是有一些怪怪的,
--sql server 内部优化到底干什么了,感觉有些乱。。。。
IF OBJECT_ID (N'tempdb.dbo.#tempTable', N'U') IS NOT NULL
DROP TABLE #tempTable;
select ROW_NUMBER() Over(order by id) as rowId, * from V_test where type= @type and zt = @zt;
select * FROM #tempTable where rowId > @start and rowId <= @end;
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 的回复:]

第一个优化器根本就不考虑where rowId > 0,因为rowId永远大于0,第二个就不同了
[/Quote]

对,第一个语句实际上是查找所有的数据,第二个语句实际上只是查找满足条件的那15条数据,
这个差别肯定很大。而且你的rowid上没索引,肯定会全表扫面,那样的话I/O开小会很大了
快溜 2012-05-22
  • 打赏
  • 举报
回复
第一个优化器根本就不考虑where rowId > 0,因为rowId永远大于0,第二个就不同了
丰云 2012-05-22
  • 打赏
  • 举报
回复
图太大,不好贴,我把参数发出来:

--select开销 0%
--前几行(top) 0%
--筛选器(filter) 0%
--序列射影(compute scalar) 0%
--计算标量(compute scalar) 0%
--段(segment) 0%
--排序 sort 4%
--嵌套循环(inner join) 1%
--表扫描 0%
--索引查找 1%
--聚集索引查找 94%

感觉这些数字比较正常。。。。。
  • 打赏
  • 举报
回复
还得考虑缓存。你贴出来看看嘛
丰云 2012-05-22
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 的回复:]

楼主最好把你的执行计划图贴出来,看看哪个部分有问题,可以优化
[/Quote]
两个语句的执行计划图完全一样
  • 打赏
  • 举报
回复
楼主最好把你的执行计划图贴出来,看看哪个部分有问题,可以优化
lxig20077 2012-05-22
  • 打赏
  • 举报
回复
顶楼上 很可能是这样的
  • 打赏
  • 举报
回复
[Quote=引用楼主 的回复:]
如题,下面的是相关说明:
SQL code

--视图V_test:
select a.1,a.2,a.3,b.1,b.2,b.3 from a inner join b on a.0=b.0

--语句1:
Select top 15 * FROM
(
select ROW_NUMBER() Over(order by id) as rowId, * from V_test
where ……
[/Quote]

一楼的你考虑一下。另外你这个rowid上面是没有索引的,想办法把子查询的语句的结果放到临时表,在给rowid加索引试试
丰云 2012-05-22
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 的回复:]

rowId > 15的记录是不是很多?如果很多的话可能会导致索引失效。
[/Quote]
rowId > 15很多的话,那rowId > 0应该更多才对啊,为什么语句1那么快呢
--小F-- 2012-05-22
  • 打赏
  • 举报
回复
rowId > 15的记录是不是很多?如果很多的话可能会导致索引失效。

22,207

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 疑难问题
社区管理员
  • 疑难问题社区
  • 尘觉
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧