当前位置: 首页 > 后端技术 > Java

Oracle数据库业务SQL优化实践-时间间隔查询案例

时间:2023-04-01 16:46:25 Java

后台查询字段其实挺多的,我选择关注瓶颈点,开始吧功能后台简介:我们在入口处架设了一台服务器数据中心记录人员进出的机器由门卫操作,记录人员进出(通过交换通关卡,交换通关卡时记录进入时间,离开退回通关卡时记录时间)。数据中心的人数、次数、具体进入的人员、人员进入的时长等。根据功能后台,我们创建了一张表CREATETABLE"USER"."ENTER_HISTORY"{"ID"NUMBER,"LOGIN_TIME"VARCHAR2(128),"LOGOUT_TIME"VARCHAR2(128),"USER_ID"VARCHAR2(128),主键(“ID”)};对“用户”列的评论。“ENTER_HISTORY”。“ID”是“id”;对“用户”列的评论。“ENTER_HISTORY”。“USER”列。“ENTER_HISTORY”。“LOGOUT_TIME”是“离开时间”;在“USER”列上发表评论。“ENTER_HISTORY”。“USER_ID”是“用户ID”;我们于2021-12-0100:00到达:00以2022-01-0100:00:00时间为例,统计此时数据中心的人数(出入人数和停留人数)SELECTSUM(DISTINCTEH.USER_ID)FROMUSER.ENTER_HISTORYEHWHERENOT(EH.LOGOUT_TIME<'2021-12-0100:00:00'OREH.LOGIN_TIME>'2022-01-0100:00:00')SQL语句之所以写在这种方式如下图所示。除去红色的两个区域,其余都是符合条件的记录。问题一:某天,业务反映门卫没有归还每张卡,也就是没有记录出发时间,导致业务在核对人数时造成一些记录干扰。提出的业务,不会有出发时间,记录为(进入时间+1天/或当前时间-时间较近者),考虑到这个数据是同步机数据,如果直接在这里修改,会导致数据不一致,所以考虑在查询上下功夫,考虑查询逻辑。出发时间不存在时,出发时间统一为(入境时间+1天)计算。可改写的SQL语句如下NVL函数语法NVL(eExpression1,eExpression2)如果eExpression1的计算结果为空值,NVL()返回eExpression2如果eExpression1的计算结果为非空值,则返回eExpression1。eExpression1和eExpression2可以是任何数据类型。如果eExpression1和eExpression2都产生空值,则NVL()返回.NULL。SELECTSUM(DISTINCTEH.USER_ID)FROMUSER.ENTER_HISTORYEHWHERENOT(NVL(EH.LOGOUT_TIME,TO_CHAR(TO_DATE(EH.LOGIN_TIME,'yyyy-mm-ddhh24:mi:ss')+1,'yyyy-mm-ddhh24:mi:ss')))<'2021-12-0100:00:00')OREH.LOGIN_TIME>'2022-01-0100:00:00')业务问题2改写语句后,一开始因为功能刚上线,数据不多,所以查询效率被忽略了好几个月,业务反馈查询等待时间太长。让我们看看是否可以让它更快。于是又看了一下这个语句,索引没有建立。第一步:根据这条语句建立索引Query。查看DBEAVER上的执行计划,发现没有使用索引,因为我们还没有创建索引。我们使用的SQL涉及到两个查询字段,而且是同一个级别。我们可以只创建一个字段的索引,也可以创建两个字段的联合索引。这里使用联合索引。建索引语句如下:CREATEINDEXIDX_EH_LOGINOUT_TIMEONUSER.ENTER_HISTORY(LOGIN_TIME,LOGOUT_TIME)建立索引后,查看DBEAVER上的执行计划。发现还是没有使用索引,此时分析没有使用索引的原因。1.查询字段使用函数。2、优化器认为使用全表比使用索引更快。第二步:重写SQL。了解我们的情况NOT(NVL(EH.LOGOUT_TIME,TO_CHAR(TO_DATE(EH.LOGIN_TIME,'yyyy-mm-ddhh24:mi:ss')+1,'yyyy-mm-ddhh24:mi:ss')))<'2021-12-0100:00:00')OREH.LOGIN_TIME>'2022-01-0100:00:00')实际上会被重写为NVL(EH.LOGOUT_TIME,TO_CHAR(TO_DATE(EH.LOGIN_TIME,'yyyy-mm-ddhh24:mi:ss')+1,'yyyy-mm-ddhh24:mi:ss')))>='2021-12-0100:00:00')ANDEH.LOGIN_TIME<='2022-01-0100:00:00',可以排除NOT的影响。对于使用了NVL和TO_DATE的查询列,我做空判断,把计算改成value端的方法改成如下((EH.LOGOUT_TIMEISNOTNULLANDEH.LOGOUT_TIME>='2021-12-0100:00:00')OR(EH.LOGOUT_TIMEISNULLANDEH.LOGIN_TIME>=TO_DATE('2021-12-0100:00:00','yyyy-mm-ddhh24:mi:ss')+1,'yyyy-mm-ddhh24:mi:ss'))))ANDEH.LOGIN_TIME<='2022-01-0100:00:00'改写后再次查看执行计划,发现索引还在不曾用过。第三步:启用ORACLE强制索引。我们判断可能是因为创建的字段是文本值,而ORACLE正在创建执行计划。当优化器不选择这个索引时这个时候,因为我们认为索引的效率可能更高,所以强制SQL使用索引。强制索引的语句如下SELECT/*+index(tpk_emp)*/*FROMEMPT--forcetheindex,/*...*/第一个星后面不能跟空格,内容structure里面是一个加号索引(表名空间索引名)--如果表使用了别名,注释中的表也要使用别名SELECT/*+index(EHIDX_EH_LOGINOUT_TIME)*/SUM(DISTINCTEH.USER_ID)FROMUSER.ENTER_HISTORYEHWHERE(EH.LOGOUT_TIMEISNOTNULLANDEH.LOGOUT_TIME>='2021-12-0100:00:00')OR(EH.LOGOUT_TIMEISNULLANDEH.LOGIN_TIME>=TO_DATE('2021-12-0100:00:00','yyyy-mm-ddhh24:mi:ss')+1,'yyyy-mm-ddhh24:mi:ss'))))和EH.LOGIN_TIME<='2022-01-0100:00:00'此时查看执行计划,发现索引已经被移除。其实我们在创建字段的时候最好考虑字段的作用,设置合适的格式,比如这里用字符串存储时间是不合适的,但是因为我做这个的时候表已经在了功能,我没有做任何改变。第4步:尝试启用ORACLE并行模式。但是由于实际的查询范围,当有些查询时间跨度比较大的时候,其实相当于全表查询,索引的用处不大。例如,该功能于6月推出。如果查询6月至今的数据,相当于全表查询。团队的DBA给了一个建议,说要开启ORACLE的并行模式。但是我实际使用后比较了性能,发现并没有太大的区别。修改后的SQL如下ORACLE并行模式语法/+parallel(table_short_name,cash_number)/ForciblyenableparallelismtoexecutethecurrentSQL,可以强制开启Oracle的多线程处理功能。多核同时工作,提高效率。SELECT/*+parallel(EH,4)*/SUM(DISTINCTEH.USER_ID)FROMUSER.ENTER_HISTORYEHWHERE(EH.LOGOUT_TIMEISNOTNULLANDEH.LOGOUT_TIME>='2021-12-0100:00:00')OR(EH.LOGOUT_TIMEISNULLANDEH.LOGIN_TIME>=TO_DATE('2021-12-0100:00:00','yyyy-mm-ddhh24:mi:ss')+1,'yyyy-mm-ddhh24:mi:ss'))))ANDEH.LOGIN_TIME<='2022-01-0100:00:00'总结其实在真实环境下,所有的数据都是直接找出来的,进行统计程序中out,如下是改造前后的一般性能数据性能对比。表格中的总数是5w3。7月到明年1月查询时,大概有3.8w条数据。优化前:大约需要4s优化后:大约需要4s总的来说,这是一个不成功的优化案例,但是里面的思路、做法和做法值得大家参考。如果您对本文有任何建议或疑问,请在下方留言与我交流。我是Vapire,一个普通的全栈开发者。站在发展的角度看问题,在发展的路上学知识。参考ORACLE的强制index_weixin_30765319的博客-CSDN博客Oracle的NVL函数使用-黄景新-博客园(cnblogs.com)oracle并行模式(Parallel)-欢星宇-博客园(cnblogs.com)OracleHints,Oracle并行模式(Parallel)/+parallel(t,4)/在SQL调优中的重要作用_徐恒的博客-CSDN博客_oracle并行并行