Oracle SQL优化过程一则以及group by少见用法报错点

news/2025/1/9 10:59:16/文章来源:https://www.cnblogs.com/PiscesCanon/p/18661733

 

Oracle SQL优化过程一则以及group by少见用法报错点

 

业务让帮忙优化一条sql,sql文本如下(脱敏):

select to_char(t.create_time, 'yyyy-mm') 月份,count(*) 总数,(select count(v.seq_no)from zkm.test vwhere to_char(v.create_time, 'yyyy-mm') = to_char(t.create_time, 'yyyy-mm')and v.result_flag = 11) 通过数from zkm.test twhere t.create_time >= to_date('20240101', 'yyyymmdd')group by to_char(t.create_time, 'yyyy-mm')order by to_char(t.create_time, 'yyyy-mm') desc;

这条SQL每次执行需要10s左右,让帮忙看能不能优化。

首先看了下,表就一个主键带的索引,create_time字段是没有索引的,从表t的条件看,查询了从20240101以来将近1年的数据,数据量太大占比接近表40%的数据量(验证过程略),并不太适合使用索引。

但是!!!!因为select、group by、order by中并没有使用其他字段,那么建索引是有用的,因为不需要回表。

另外,不需要看执行计划,从标量子查询的自关联(表v和表t是同一张表)可以看出,由于无索引可用导致表v会被多次全表扫描,有点类似filter的方式。

 

考虑创建索引的话,排除数据量因素的影响的话,另外还要看看这个字段作为谓词条件被使用的多不多。

PS:dba_tab_modifications视图本身是没有的,文档 ID 400214.1提供了常见的脚本。

可以参考我另外的博文杂谈:Oracle问题:一张表推荐创建多少索引合适,里边有提供创建脚本。

select * from dba_column_usage where owner=upper('zkm') and name=upper('test') order by 4; OWNER   NAME   COLUMN_NAME                    EQUALITY_PREDS EQUIJOIN_PREDS NONEQUIJOIN_PREDS RANGE_PREDS LIKE_PREDS NULL_PREDS TIMESTAMP
------- ------ ------------------------------ -------------- -------------- ----------------- ----------- ---------- ---------- -------------------
ZKM     TEST   COL1                                        0              0                 0           2          0          0 2025-01-03 12:05:41
ZKM     TEST   CREATE_TIME                                 1              0                 0        2341          0          0 2025-01-08 17:03:27
ZKM     TEST   COL2                                        2              0                 0           0          0          0 2023-02-08 15:27:40
ZKM     TEST   COL3                                        5              0                 0           0          0          0 2023-11-02 12:52:29
ZKM     TEST   COL4                                       12              0                 0           0          0          0 2024-01-09 08:58:50
ZKM     TEST   COL5                                       26              0                 0           0          3          3 2025-01-07 09:09:33
ZKM     TEST   COL6                                      105              0                 0           0          0          0 2024-10-31 15:15:20
ZKM     TEST   COL7                                     4987              0                 0           0          0          0 2025-01-08 17:03:27
ZKM     TEST   RESULT_FLAG                              5094              0                 0           0          0          0 2025-01-08 17:03:27
ZKM     TEST   SEQ_NO                                  14767            105                 0           0          0          0 2025-01-08 16:55:57

综合整体看使用频率,2341次也还行,另外业务人员也提到了以后随着业务这个字段也用的会比较多,Good!

后续看了数据选择性,也比较合适创建索引。

到这里都是为表t做考虑的。

我们先给create_time加上索引,注意加online。

create index zkm.idx_z_create_time on zkm.test(create_time) online;

由于表v在标量子查询做了自关联,关联条件正好也是create_time,但此处对create_time做了to_char处理会导致无法使用索引。

那么还要考虑对关联条件进行改写,其中关联条件是to_char(v.create_time, 'yyyy-mm') = to_char(t.create_time, 'yyyy-mm'),要使用索引取访问表v,可以将条件改为如下:

v.create_time >= trunc(t.create_time, 'mm') and v.create_time < add_months(trunc(sysdate, 'mm'), 1)

最终如下:

select to_char(t.create_time, 'yyyy-mm') 月份,count(*)                         总数,(select count(*)from zkm.test vwhere 1 = 1and v.create_time >= trunc(t.create_time, 'mm')and v.create_time <  add_months(trunc(sysdate, 'mm'), 1)and v.result_flag = '11')                                通过数from zkm.test twhere 1 = 1and t.create_time >= to_date('20240101', 'yyyymmdd')group by to_char(t.create_time, 'yyyy-mm')order by to_char(t.create_time, 'yyyy-mm') desc;

这里直接跑这条SQL会遇到另外一个问题,而且是语法问题:ERROR at line 6:ORA-00979: not a GROUP BY expression

到这里就纳闷了,不应该了,上边的条件改写应该是等价的没错啊。

后边又换成另外一种写法:

select to_char(t.create_time, 'yyyy-mm') 月份,count(*)                         总数,(select count(*)from zkm.test vwhere 1 = 1and v.create_time >= to_date(to_char(t.create_time, 'yyyy-mm'), 'yyyy-mm')and v.create_time <  add_months(to_date(to_char(t.create_time, 'yyyy-mm'), 'yyyy-mm'), 1)and v.result_flag = '11')                                通过数from zkm.test twhere 1 = 1and t.create_time >= to_date('20240101', 'yyyymmdd')group by to_char(t.create_time, 'yyyy-mm')order by to_char(t.create_time, 'yyyy-mm') desc;

 

这次又能正常跑了而且飞快。。。为啥?

到这里,这条SQL就是最终改写的SQL了,0.2s左右可以跑完,执行计划如下:

Plan hash value: 1567763871----------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name              | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                   |      1 |        |     13 |00:00:00.17 |    1069 |       |       |          |
|   1 |  SORT AGGREGATE               |                   |     13 |      1 |     13 |00:00:00.21 |   28508 |       |       |          |
|*  2 |   FILTER                      |                   |     13 |        |    171K|00:00:00.20 |   28508 |       |       |          |
|*  3 |    TABLE ACCESS BY INDEX ROWID| TEST              |     13 |    995 |    171K|00:00:00.18 |   28508 |       |       |          |
|*  4 |     INDEX RANGE SCAN          | IDX_Z_CREATE_TIME |     13 |   1794 |    171K|00:00:00.03 |     483 |       |       |          |
|   5 |  SORT GROUP BY                |                   |      1 |    132K|     13 |00:00:00.17 |    1069 |  2048 |  2048 | 2048  (0)|
|*  6 |   INDEX FAST FULL SCAN        | IDX_Z_CREATE_TIME |      1 |    133K|    171K|00:00:00.05 |    1069 |       |       |          |
----------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------2 - filter(ADD_MONTHS(TO_DATE(:B1,'yyyy-mm'),1)>TO_DATE(:B2,'yyyy-mm'))3 - filter("V"."RESULT_FLAG"='11')4 - access("V"."CREATE_TIME">=TO_DATE(:B1,'yyyy-mm') AND "V"."CREATE_TIME"<ADD_MONTHS(TO_DATE(:B2,'yyyy-mm'),1))6 - filter("T"."CREATE_TIME">=TO_DATE(' 2024-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

 

 

 

 

回到还没解决的ORA-00979,观察两次改写的条件的其中一部分,trunc(t.create_time, 'mm')和to_date(to_char(t.create_time, 'yyyy-mm'), 'yyyy-mm')不就是同一个结果么...怎么会一个可以一个不行。

select trunc(sysdate, 'mm') trunc_result,to_date(to_char(sysdate, 'yyyy-mm'), 'yyyy-mm') other_result from dual where trunc(sysdate, 'mm') = to_date(to_char(sysdate, 'yyyy-mm'), 'yyyy-mm');TRUNC_RESULT        OTHER_RESULT
------------------- -------------------
2025-01-01 00:00:00 2025-01-01 00:00:00

 

 

 

估计会有人看蒙蔽了。

不过到这里结合正常跑完SQL的执行计划的谓词信息,我大概能猜测到,原因是因为select部分的标量子查询里边的trunc(t.create_time, 'mm')并不存在于group by子句中,因此报语法错误。

想要验证很简单,只要下边的SQL能正常跑就是了。

select to_char(t.create_time, 'yyyy-mm') 月份,count(*)                         总数,(select count(*)from zkm.test vwhere 1 = 1and v.create_time >= trunc(t.create_time, 'mm')and v.create_time <  add_months(trunc(sysdate, 'mm'), 1)and v.result_flag = '11')                                通过数from zkm.test twhere 1 = 1and t.create_time >= to_date('20240101', 'yyyymmdd')group by to_char(t.create_time, 'yyyy-mm'),trunc(t.create_time, 'mm')order by to_char(t.create_time, 'yyyy-mm') desc;

 

测试了下,确实可以正常跑了。

看执行计划id为4的谓词信息,to_char(t.create_time, 'yyyy-mm')是作为一个整体的值:B1代入做判断了,并没有去看t.create_time的值,那么除了to_char(t.create_time, 'yyyy-mm')以外都应该进行分组。

而trunc(t.create_time, 'mm')尽管从结果上等价于to_date(to_char(t.create_time, 'yyyy-mm'), 'yyyy-mm'),但形式上还是不同的。

 

至此。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/866477.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

可视化工具在UI/UX设计中的应用与管理策略

一、设计行业的项目管理挑战 设计项目通常具有较强的创意性、变化性和协作性。与其他行业的项目管理不同,设计行业的项目管理面临一些独特的挑战: 1.1 多轮设计迭代和客户反馈 设计项目通常不是一次性完成的,而是通过多个设计迭代不断完善。每个设计阶段结束后,团队通常需要…

《安富莱嵌入式周报》第348期:开源低功耗测试仪,开源创意万用表,续航100-300小时,开源PCB电机,自制shell和网络协议栈,开源水培自动化系统

周报汇总地址:http://www.armbbs.cn/forum.php?mod=forumdisplay&fid=12&filter=typeid&typeid=104 视频版: https://www.bilibili.com/video/BV1Tzr9Y3EQ7/目录: 1、开源低功耗测试仪 2、开源创意万用表,续航100-300小时 3、低级编程和优化实现 4、资讯 (1…

16C++循环结构-(do-while循环)1——教学

1、do-while 语句; 2、各数位之和; 3、纯小数变整数; 4、数学计算一、do-while 语句 问题:每次测试后狐狸老师总会把成绩输入计算机,进行处理分析。但输入时有时会出错,如当满分为100分时,输入小于0或大于100的数,表示输入有误。试编一程序,输入某一位同学成绩时,自动检查…

南京芯麒电子-基于6U VPX的TMS320C6678+XCVU9P的高性能处理平台

概述 该平台是由16nm工艺的的XCUV9P FPGA和TI公司高性能数字信号处理器TMS320C6678构建的一款标准6U VPX高性能数据处理平台,VPX P1上定义4个x4 GTY,P2上1路PCIe x16接口、P3~P6上引出了大量GTY/GTH以及RS422/GPIO信号。板卡提供2个FMC+接口、可搭配我司各类FMC子卡使用,实现…

XTR105 XTR105UA/2K5规格书具有传感器激励和线性化的 4mA 至 20mA 电流变送器芯片

XTR105 是一款带有两个精准电流源的单片 4mA 至20mA、2 线制电流发送器。该器件在一个单集成电路上提供针对铂 RTD 温度传感器和桥、仪表放大器以及电流输出电路的完整电流激励。多用途线性化电流提供一个对 RTD 的第二阶修正,通常可以实现一个 40:1 的线性改进。仪器放大器增…

[rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(03):图像的导入、显示、调整(暨image部件的使用介绍)

前言 本文是关于iced库的部件介绍,iced库是基于rust的GUI库,作者自述是受Elm启发。 iced目前的版本是0.13.1,相较于此前的0.12版本,有较大改动。 本合集是基于新版本的关于分部件(widget)的使用介绍,包括源代码介绍、实例使用等。 环境配置 系统:window10 平台:visual…

PyTorch团队为TorchAO引入1-8比特量化,提升ARM平台性能

在深度学习模型部署和优化领域,计算效率与资源消耗的平衡一直是一个核心挑战。PyTorch团队针对这一问题推出了创新性的技术方案——在其原生低精度计算库TorchAO中引入低位运算符支持。这一技术突破不仅实现了1至8位精度的嵌入层权重量化,还支持了具有8位动态量化激活的线性运…

设计团队管理的关键因素:如何确保成员高效协作

一、设计团队的组织架构 一个高效的设计团队通常有一个明确的组织架构,合理的分工与协作关系能够最大程度地提高团队的工作效率。在设计团队的组织架构中,一般包括项目经理、设计师、协调员以及可能的开发人员和客户代表。 1.1 项目经理(PM) 项目经理是设计团队的核心角色之…

欧阳的2024年终总结,迷茫,重生与失业

前言 这是欧阳第一次写年终总结,今年发生的事情还挺多的值得写篇文章记录一下。立个flag,以后每年都写一篇年终总结文章,5年后35岁再来看这些文章不知道是什么感觉。今年发生的事情可以总结为三个词语:迷茫、重生、失业。 欧阳也在找工作,坐标成都求内推! 事件 今年发生的…

JS将docx转为html代码--Vue3(简易版)

这两天突然接了一个把节气文章转成html页面的需求,本来只是需要多按几下ctrl+c,ctrl+v能解决的事,但是想想后续一年24个节气,就做个自动转换的工具吧。由于做软件还涉及到其他语言和配置环境,所以还是选择了web。 首先创建一个vue3项目,我用的vite搭建的,不会的请自行移步…

大语言模型提示技巧(八)-防止胡说八道

自然语言往往充满歧义和模糊性,模型在学习时可能会产生误解或错误理解一些概念,导致生成不准确的信息。为了尽量减少这些问题,研究人员和工程师会使用更大规模、更多样化和更准确的训练数据,调整模型架构,以及使用更先进的训练技术。但是自然语言本身就是复杂且充满挑战的…

使用API方式远程调用ollama模型

在有GPU的环境启动一个ollama大模型,非常简单:注意,ollama启动时默认监听在127.0.0.1:11434上,可以通过配置OLLAMA_HOST环境变量修改 `export OLLAMA_HOST="0.0.0.0:11434" ollama serve& ollama run qwen2.5:7b-instruct`然后就可以在远端访问: `curl http…