返回顶部
首页 > 资讯 > 数据库 >Rownum和Order By的执行顺序造成的影响
  • 765
分享到

Rownum和Order By的执行顺序造成的影响

2024-04-02 19:04:59 765人浏览 泡泡鱼
摘要

6月的某天下午,某用户反馈,医生站首页中病案附加项目的顺序乱了,影响医生的正常工作。 从跟踪出来的sql看到,执行得到的数据没有按预期的方式排序,但是,相同的SQL在测试库运行却能得到正常排序的

6月的某天下午,某用户反馈,医生站首页中病案附加项目的顺序乱了,影响医生的正常工作。

从跟踪出来的sql看到,执行得到的数据没有按预期的方式排序,但是,相同的SQL在测试库运行却能得到正常排序的结果。

 

院方近期没有做什么调整,怀疑跟一个月前我们转移历史数据之前的一系列性能优化调整有关。

经过一番分析,最终,通过重新收集该SQL涉及到的表的统计信息后,问题得到解决。

感觉有点儿不可思议吧?

统计信息收集不正确,还会影响产品功能的正常性?

 

有些事情的真相并不是我们看到那样,就像鸡血在磨心上点了几下之后,小孩的肚子就不痛了,如果你相信这样的巫术,可能会影响到你对很多事情的判断,有时,我们看到的并不一定是真相,倒不是因为对未知的知识缺乏敬畏,而是基本的逻辑推理问题,以及探寻真相的钻研精神。

 

统计信息收集确实是解决很多性能问题的一副灵药,但对这个问题来说,收集统计信息只是其中的一种临时解决办法,根本的原因还是SQL书写方面存在问题。

下面我们来一步一步解开事情的真相。

SQL语句如下:

select rownum as 序号,编码,名称,内容 from 病案项目 order by 编码

 

多么简单的SQL,不像那些需要翻几页才能看得完整的变态SQL,这种简单的SQL除了理解起来更节约时间之外,还减少了很多可能导致验证结果偏差的干扰。

开发人员的预期,先排序再对结果集进行编号,Rownum作为oracle下特有的“伪列”,作用就是根据数据结果行数产生一个从1开始递增的行号。

 

有一定基础的同学可能一下就看出的不对之处:

Order by和Rownum在同一层次时,排序是最后执行的,先取行号自然就无法得到想要的按排序结果递增的行号,所以,如下所示,序号列就是“乱序”的。

 

那么,问题来了,既然这个SQL有问题,为什么在测试库运行又是正常的呢?

近期产品中的这条SQL也没有做过修改,为什么以前是正常的,现在却突然出现问题了呢?

没有骗你哟,用户发来了测试库上执行后结果正确的截图。

 

刚开始看到这个现象,也是很纳闷,会不会是数据库的什么参数影响了排序,像经常遇到的"_gby_hash_aggregation_enabled"这个参数对Group By排序的影响(后续可能会写一个这方面的案例),问题是这个SQL里没有Group by子句。


数据库的基础理论方面想了想,影响排序的还有哪些因素呢?

如果是有索引,那么索引本身就是排了序的,读取数据时就不需要排序了,再用Rownum取值,是不是就可以取到预期的结果了呢?

也就是说,变相实现了先排序,后取序号的作用。

 

为了证实这一点,在公司的测试库上做了一个验证:

1.     相同的SQL,执行结果跟用户测试库的结果是一样的,序号正常排序。

2.     禁用主键“病案项目_PK”(以“编码”字段为索引)后,执行结果跟用户正式库的结果一样,序号排序就乱了,再现了问题。

alter table 病案项目 disable constraint 病案项目_PK;

恢复主键后,序号就正常排序了。

alter table 病案项目 enable constraint 病案项目_PK;

 

是不是用户生产库的主键被禁用了,索引丢了?

转移历史数据期间,的确会禁用一些约束,但是这张表并不是转出相关的表呀!而且转完数据后,我们恢复约束后也做过检查。

会不会是用户后期运行过程中,某种原因导致该索引无效了呢?

马上查询用户的生产库,主键是有效的,索引也是有效的。

 

眼看找到一条路,没想到走到底发现是个死胡同,不要懈气,既然问题再现了,原理也清楚了,顺着这条路,仔细找找,一定有出路。

 

在测试环境,对比分析一下,禁用主键(删除“编码”字段的索引)前后的执行计划。

对比发现了差异:

有索引时,执行计划包含” INDEX FULL SCAN”,没有排序操作。

没有索引时,执行计划包含” TABLE ACCESS FULL”,有排序操作“SORT ORDER BY”。

用户生产库的执行计划是什么呢?

 

一查询,结果跟我在这边测试环境下的执行计划一样,走了全表扫描。

为什么没有走索引全扫呢?

会不会是统计信息收集有问题,导致成本评估时,认为全表扫描的成本更低,所以选择了它呢。

 

在用户生产库收集了一下统计信息,结果就正常了,执行计划变成了” INDEX FULL SCAN”。

表的统计信息丢失的原因,不得而知。

其实解决问题的办法还有其他的,例如:通过Sql Profile加提示字指定索引。

 

既然是统计信息没有收集的问题,那是不是可以再现一下问题现象呢?

完全可以。

我们删除统计信息后来看看,是不是就再现用户生产库的场景了呢?

exec dbms_stats.delete_table_stats(ownname => 'ZLHIS',tabname => '病案项目');

再PLSQL中查看刚才那条SQL的执行计划,确实从索引全扫描变成了全表扫描。

执行SQL,查询一下数据,咦,怎么数据不是乱序的?

执行计划不是变了吗?

什么情况?

 

又到强调掌握数据库基础理论的重要性的时候了,如果只学习那些用得到的知识,书到用时方恨少,只有打开坚实的基础,才能在实战中临场应对。

 

清空一下共享池再看看。

alter system flush SHARED_POOL;

再次执行数据查询,哈哈,这次,“编码”字段终于乱序了,一阵窃喜。

数据乱了还要高兴,什么原因?因为它如你所愿,你看到了想要的结果。

 

为什么之前从PLSQL看到执行计划是全表扫描,但查询数据却跟索引全扫描的结果一样呢?

别忘了,PLSQL只是一个工具,它的F5查看执行计划的功能,不知道骗了多少人,坑了多少人。

其实你明白它查看执行计划的原理,就知道它不是真的想骗你,如果你有兴趣,可以通过10046去探个究竟,因为篇幅有限,这里就不再详述。

 

总结:

至此,这个问题搞清楚了,我们再来回看一下SQL,重新梳理一下:

select rownum as 序号,编码,名称,内容 from 病案项目 order by 编码

当Order by和Rownum在同一层次时,排序是最后执行的,先取行号再排序,这样就无法根据排序结果得到正确行号,如果能够避免排序,就能得到期望的结果。

当Order by中的字段是非空索引时(主键索引属于非空索引),如果统计信息收集正确,执行计划评估成本后,就会选择“索引全扫描”,由于索引本身是排了序的,就能避免排序。

当表的统计信息丢失,优化器在评估成本时,由于缺乏统计信息作为成本计算的依据,它就会选择“全表扫描”作为执行计划,然后再排序。

 

为了避免统计信息收集导致的这种问题,在各种用户环境下得到稳定的结果,这个SQL可以改为:

Select Rownum As 序号, 编码, 名称, 内容 From (Select 编码, 名称, 内容 From 病案项目 Order By 编码)

在子查询中先排序,外层查询中再取Rownum,类似的,当我们在写Rownum<5这类SQL时,也需要注意排序的问题,应该先在子查询中写排序,外面再限制返回行数。

 

思考:

为了加深对这个知识的理解,你可以试一下这两个SQL:

Select 姓名 From 人员表 Where Rownum < 2 Order By 建档时间 Desc;

Select 姓名 From 人员表 Where Rownum < 2 Order By ID Desc;

ID是主键,顺序递增,建档时间最大的记录,其ID最大。

为什么两个SQL得到的数据不样呢?对比一下执行计划看看。

 

知识点的扩展应用:

利用这个案例中的知识点:排序字段如果与非空索引的字段顺序相同,则可以利用索引本身已排序的特性来避免排序,这在一些高并发的高频SQL中,对系统的整体性能提升将会起到非常重要的作用,因为排序操作对cpu消耗比较大,特别是那些大的、高频的排序。

特别提醒,两个重要条件:字段顺序相同,非空索引。


------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
公众号:医信系统性能优化
主要写一些日常工作中性能优化方面的案例,包括SQL优化,数据结构设计优化,Oracle系统性能优化。
面向编写SQL及相关脚本的开发人员和技术支持人员,分享一些性能优化的经验。
对性能优化技术学习感兴趣的同学,欢迎订阅,共同学习,相互交流。
Rownum和Order By的执行顺序造成的影响


您可能感兴趣的文档:

--结束END--

本文标题: Rownum和Order By的执行顺序造成的影响

本文链接: https://lsjlt.com/news/49610.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Rownum和Order By的执行顺序造成的影响
    6月的某天下午,某用户反馈,医生站首页中病案附加项目的顺序乱了,影响医生的正常工作。 从跟踪出来的SQL看到,执行得到的数据没有按预期的方式排序,但是,相同的SQL在测试库运行却能得到正常排序的...
    99+
    2024-04-02
  • return与finally的执行顺序的影响(skycto JEEditor)
    对于 try 和 finally 至少一个语句块包含 return 语句的情况:finally 语句块会执行finally 没有 return,finally 对 return 变量的重新赋值修改无效try 和 finally 都包含ret...
    99+
    2023-06-02
  • PHP 函数的执行顺序受哪些因素影响?
    php函数执行顺序受以下因素影响:函数定义:按照出现顺序执行函数调用:触发代码执行函数参数:按顺序传递值全局变量:函数内修改后更新变量值 PHP 函数的执行顺序 PHP 的函数执行顺序...
    99+
    2024-04-17
    php 因素
  • 浅谈@Aspect@Order各个通知的执行顺序
    目录@Aspect@Order各个通知的执行顺序代码小结spring AspectJ order(顺序)@Aspect@Order各个通知的执行顺序 两个切面类:【记录日志】和【判断...
    99+
    2024-04-02
  • PHP 函数的代码位置是否影响执行顺序?
    函数的执行顺序通常受代码位置影响,但有时调用位置也会影响,如使用名称空间或包含文件时;而函数在不同位置定义但使用相同名称的情况,代码位置不影响其执行顺序。 PHP 函数的代码位置是否影...
    99+
    2024-04-17
    php 执行顺序
  • PHP 函数的嵌套调用如何影响执行顺序?
    php 中函数的嵌套调用遵循特定的执行顺序,外部函数首先执行,其次是按定义顺序调用的嵌套函数,避免过度嵌套以确保程序可读性和维护性。 PHP 函数的嵌套调用及其对执行顺序的影响 在 P...
    99+
    2024-04-18
    php 函数嵌套
  • @Aspect@Order各个通知的执行顺序是什么
    小编给大家分享一下@Aspect@Order各个通知的执行顺序是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!@Aspect@Order各个通知的执行顺序两个...
    99+
    2023-06-29
  • PHP 函数的递归调用会如何影响执行顺序?
    php 函数递归调用影响执行顺序,遵循后进先出栈结构:函数递归调用自身时入栈。栈中最后一个函数先执行。函数返回时出栈,调用函数继续执行。 PHP 函数的递归调用如何影响执行顺序 PHP...
    99+
    2024-04-17
    php 递归调用
  • PHP 函数的命名空间是否会影响执行顺序?
    在 php 中,函数命名空间一般不会影响执行顺序。执行顺序通常由文件顺序或函数调用顺序决定。即使函数位于不同的命名空间中,只要它们在同一文件中,它们就会按照定义顺序执行。 PHP 函数...
    99+
    2024-04-17
    php 命名空间
  • PHP 函数的参数传递顺序对执行有影响吗?
    对于 php 函数,传递参数的顺序会影响某些函数的执行。这适用于以下函数:str_split():用于拆分字符串,需要按顺序传递参数(字符串、长度)。shuffle():用于随机排序数组...
    99+
    2024-04-18
    php 参数传递顺序
  • PHP 代码中的 include 和 require 语句如何影响函数执行顺序?
    在 php 中,include 和 require 语句用于包含外文件,区别在于错误处理方式:include:未找到文件时产生警告,继续执行。require:未找到文件时产生致命错误,中...
    99+
    2024-04-17
    php 函数执行顺序
  • java构造器的执行顺序是什么
    这篇文章给大家介绍java构造器的执行顺序是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。1,在没有静态块的情况下,子类的对象创建时,父类的无参构造器-->子类的构造器(产生对象的构造器,如果是无参则执行的是...
    99+
    2023-06-17
  • C#构造函数在基类和父类中的执行顺序
    一、简介 当我们没有在子类构造函数中写上 base(),默认会先调用父类中无参的构造函数,再调用子类。当在有参构造函数后写上base时,只调用子类构造函数中有参的构造函数,隐藏父类无...
    99+
    2024-04-02
  • Kotlin构造函数、成员变量和init代码块执行顺序是什么
    今天小编给大家分享一下Kotlin构造函数、成员变量和init代码块执行顺序是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下...
    99+
    2023-07-04
  • 分组查询GROUP BY的使用与SQL执行顺序的讲解
    在SQL中使用GROUP BY来对SELECT的结果进行数据分组,在具体使用GROUP BY之前需要知道一些重要的规定。 1、GROUP BY子句可以包含任意数目的列。也就是说可以在组里再分组...
    99+
    2024-04-02
  • PHP与MySQL索引的数据查询和数据更新的执行顺序及其对性能的影响
    引言:在开发中,PHP与MySQL是常用的组合,MySQL是一款关系型数据库管理系统,而PHP是一种用于开发Web应用的脚本语言。在处理大量数据时,索引的使用对于查询和更新操作的性能至关重要。本文将讨论PHP与MySQL索引的数据查询和数据...
    99+
    2023-10-21
    MySQL PHP 索引
  • Kotlin构造函数、成员变量和init代码块执行顺序实例分析
    本文小编为大家详细介绍“Kotlin构造函数、成员变量和init代码块执行顺序实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“Kotlin构造函数、成员变量和init代码块执行顺序实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路...
    99+
    2023-07-04
  • 浅谈@Value和@Bean的执行顺序问题
    问题描述 使用@Autowired处理多个同种类型的bean,出现@Value和@Bean的执行顺序问题。 首先使用扫描包+注解的方式注册User类型的不同bean,分别是user、...
    99+
    2024-04-02
  • 关于@PostConstruct、afterPropertiesSet和init-method的执行顺序
    目录@PostConstruct、init-method、afterPropertiesSet() 执行顺序@PostConstruct 标注的方法在何时被谁调用init-metho...
    99+
    2024-04-02
  • Golang 函数 defer 的执行顺序和用途
    defer 函数在 go 语言中用于延迟函数调用到函数返回前执行,按后进先出 (lifo) 的顺序调用。其用途包括释放资源、记录日志和恢复异常。后延迟的函数将在先延迟的函数之前调用。 ...
    99+
    2024-05-24
    golang defer
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作