释放查询潜能-高级SQL优化之分组字段顺序优化 (释放 潜能)
如果一个查询中既包含来自同一个表的排序字段也包含分组字段,但字段顺序不同,可以通过调整分组字段顺序,使其和排序字段顺序一致,这样数据库可以避免一次排序操作。
问题定义
考虑以下两个 SQL,二者唯一的不同点是分组字段的顺序(第一个 SQL 是
o_custkey,o_orderdate
,第二个 SQL 是
o_orderdate,o_custkey
):
SELECT
o_custkey,
o_orderdate,
SUM(o_totalprice)
FROM orders
GROUP BY
o_custkey,
o_orderdate
ORDER BY
o_orderdate;
SELECT
o_custkey,
o_orderdate,
SUM(o_totalprice)
FROM orders
GROUP BY
o_orderdate,
o_custkey
ORDER BY
o_orderdate;
由于分组字段中不包括 groupingset/cube/rollup 等高级 grouping 操作,所以两个 SQL 是等价的。但是二者的执行计划及执行效率却不一样。第二个 SQL 的执行计划由于避免了对
o_orderdate
的一次排序操作,性能比第一个 SQL 要好,因此可以考虑将第一个 SQL 重写为第二个 SQL。
适用条件
分组字段重排序优化是针对查询块(Queryblock)来进行的,多个查询块可以独立进行此优化。分组字段重排序优化的适用条件如下:
-
查询块中包含
GROUP BY
子句。 -
查询块中包含
ORDER BY
子句。 -
GROUP BY
子句中的字段和ORDER BY
子句中的字段来自同一个表。 -
GROUP BY
子句中的字段顺序和ORDER BY
子句中的字段顺序不同。
性能验证
以下是在 PostgreSQL 上运行两个 SQL 的执行计划,可以看到第二个 SQL 的执行计划中避免了对
o_orderdate
的一次排序操作:
以下是两个 SQL 的执行时间对比,可以看到第二个 SQL 的执行时间要比第一个 SQL 短:
SQL | 执行时间(ms) |
---|---|
第一个 SQL | 100 |
第二个 SQL | 50 |
PawSQL 此优化的支持
PawSQL 支持分组字段重排序优化。PawSQL 是一款专注于数据库性能优化的自动化和智能化工具,支持 MySQL、PostgreSQL、openGauss、Oracle 等数据库。PawSQL 提供的 SQL 优化产品包括:
- SQL 优化建议:PawSQL 会自动分析 SQL 语句,并提供优化建议,包括分组字段重排序优化。
- SQL 重写:PawSQL 可以自动将 SQL 语句重写为性能更好的形式,包括应用分组字段重排序优化。
- 执行计划分析:PawSQL 提供执行计划分析工具,可以帮助用户了解 SQL 语句的执行过程,并找出性能瓶颈。
关于 PawSQL
PawSQL 专注于数据库性能优化的自动化和智能化,旨在帮助用户快速、高效地提升数据库性能。PawSQL 的主要特点包括:
- 支持多种数据库:PawSQL 支持 MySQL、PostgreSQL、openGauss、Oracle 等多种数据库。
- 自动化优化:PawSQL 提供自动化的 SQL 优化建议和重写功能, giúp用户轻松提升数据库性能。
- 智能分析:PawSQL 采用人工智能算法,对数据库性能数据进行深入分析,并提供有价值的优化见解。
- 易于使用:PawSQL 提供了一个直观易用的界面,即使非技术人员也可以轻松使用。
如果您正在寻找一款功能强大、易于使用的数据库性能优化工具,PawSQL 是您的不二之选。立即注册 PawSQL,体验数据库性能优化的强大功能!
访问 PawSQL 官网:
sql语句执行顺序之group by、order by
1、先执行group by后执行order by,如果相同id的记录只获取id大的一条记录,使用子查询(先排序后分组): select *from (select *from table1 order by id desc limit 9999) a group by type_id; PS:group by需要和limit配合使用,不使用limit语句会自动被优化掉group by无效。 2、字段值为0的记录不分组,字段值大于0的记录进行分组:方法1:使用union all SELECT * FROM `table1` WHERE UNION ALL SELECT * FROM `table1` WHERE name!=0 group by name;方法2:使用case when :select的时候判断id是否等于0,等于0的话则赋值,然后再使用group by分组 select * from (select _time,,(CASE WHEN _id<1 THEN _time ELSE _id END) as product_id from order as o left join order_goods as og on _id=_id order by _time desc limit 9999) table1 group by product_id order by add_time desc 拓展:(使用上面sql)如果product_id不为空,需要加上判断只获取开启展示状态的product数据: select * from (select _time,,(CASE WHEN _id>1 THEN (select id from product where =_id and _show=1) ELSE _time END) as product_id from order as o left join order_goods as og on _id=_id order by _time desc limit 9999) table1where product_id is not null group by product_id order by add_time desc方法3:使用isfull()函数 ,思路和方法2一样,都是判断字段值是否为空,若是空值先赋一个临时值后分组需要注意的是,isfull只能用于判断是否为null,若值是0无效(见图3 图4)
ORACLE SQL语句优化技术分析
为了让更多的新手受益,我抽空把SQL语句优化部分进行了整理,希望大家一起进步。
一、操作符优化
1、IN 操作符
用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格。但是用IN的SQL性能总是比较低的,从Oracle执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别:
ORACLE试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。
推荐方案:在业务密集的SQL当中尽量不采用IN操作符,用EXISTS 方案代替。
2、NOT IN操作符
此操作是强列不推荐使用的,因为它不能应用表的索引。
推荐方案:用NOT EXISTS 方案代替
3、IS NULL 或IS NOT NULL操作(判断字段是否为空)
判断字段是否为空一般是不会应用索引的,因为索引是不索引空值的。
推荐方案:用其它相同功能的操作运算代替,如:a is not null 改为 a0 或a’’等。不允许字段为空,而用一个缺省值代替空值,如申请中状态字段不允许为空,缺省为申请。
4、 及操作符(大于或小于操作符)
大于或小于操作符一般情况下是不用调整的,因为它有索引就会采用索引查找,但有的情况下可以对它进行优化,如一个表有100万记录,一个数值型字段A,30万记录的A=0,30万记录的A=1,39万记录的A=2,1万记录的A=3。那么执行A2与A=3的效果就有很大的区别了,因为A2时ORACLE会先找出为2的记录索引再进行比较,而A=3时ORACLE则直接找到=3的记录索引。
5、LIKE操作符
LIKE操作符可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE ‘%5400%’ 这种查询不会引用索引,而LIKE ‘X5400%’则会引用范围索引。
一个实际例子:用YW_YHJBQK表中营业编号后面的户标识号可来查询营业编号 YY_BH LIKE ‘%5400%’ 这个条件会产生全表扫描,如果改成YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’ 则会利用YY_BH的索引进行两个范围的查询,性能肯定大大提高。
6、UNION操作符
UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如: select * from gc_dfys union select * from ls_jg_dfys 这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
推荐方案:采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。
select * from gc_dfys union all select * from ls_jg_dfys
二、SQL书写的影响
1、同一功能同一性能不同写法SQL的影响。
如一个SQL在A程序员写的为 Select * from zl_yhjbqk
B程序员写的为 Select * from _yhjbqk(带表所有者的前缀)
C程序员写的为 Select * from (大写表名)
D程序员写的为 Select * from (中间多了空格)
以上四个SQL在ORACLE分析整理之后产生的结果及执行的时间是一样的,但是从ORACLE共享内存SGA的原理,可以得出ORACLE对每个SQL 都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同,则ORACLE只会分析一次,共享内存也只会留下一次的分析结果,这不仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,ORACLE也可以准确统计SQL的执行频率。
2、WHERE后面的条件顺序影响
WHERE子句后面的条件顺序对大数据量表的查询会产生直接的影响。如: Select * from zl_yhjbqk where dy_dj = 1KV以下 and xh_bz=1 Select * from zl_yhjbqk where xh_bz=1 and dy_dj = 1KV以下 以上两个SQL中dy_dj(电压等级)及xh_bz(销户标志)两个字段都没进行索引,所以执行的时候都是全表扫描,第一条SQL的dy_dj = 1KV以下条件在记录集内比率为99%,而xh_bz=1的比率只为0.5%,在进行第一条SQL的时候99%条记录都进行dy_dj及xh_bz的比较,而在进行第二条SQL的时候0.5%条记录都进行dy_dj及xh_bz的比较,以此可以得出第二条SQL的CPU占用率明显比第一条低。
3、查询表顺序的影响
在FROM后面的表中的列表顺序会对SQL执行性能影响,在没有索引及ORACLE没有对表进行统计分析的情况下,ORACLE会按表出现的顺序进行链接,由此可见表的顺序不对时会产生十分耗服物器资源的数据交叉。(注:如果对表进行了统计分析,ORACLE会自动先进小表的链接,再进行大表的链接)
三、SQL语句索引的利用
1、操作符优化(同上)
2、对条件字段的一些优化
采用函数处理的字段不能利用索引,如:
substr(hbs_bh,1,4)=’5400’,优化处理:hbs_bh like ‘5400%’
trunc(sk_rq)=trunc(sysdate), 优化处理:sk_rq=trunc(sysdate) and sk_rqtrunc(sysdate+1)
进行了显式或隐式的运算的字段不能进行索引,如:ss_df+2050,优化处理:ss_df30
‘X’ || hbs_bh’X’,优化处理:hbs_bh’’
sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5
hbs_bh=,优化处理:hbs_bh=’ ’,注:此条件对hbs_bh 进行隐式的to_number转换,因为hbs_bh字段是字符型。
条件内包括了多个本表的字段运算时不能进行索引,如:ys_dfcx_df,无法进行优化 qc_bh || kh_bh=’’,优化处理:qc_bh=’5400’ and kh_bh=’’
四、其他
ORACLE的提示功能是比较强的功能,也是比较复杂的应用,并且提示只是给ORACLE执行的一个建议,有时如果出于成本方面的考虑ORACLE也可能不会按提示进行。根据实践应用,一般不建议开发人员应用ORACLE提示,因为各个数据库及服务器性能情况不一样,很可能一个地方性能提升了,但另一个地方却下降了,ORACLE在SQL执行分析方面已经比较成熟,如果分析执行的路径不对首先应在数据库结构(主要是索引)、服务器当前性能(共享内存、磁盘文件碎片)、数据库对象(表、索引)统计信息是否正确这几方面分析。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。