暂无图片
SQL 求助,如下sql怎么改写可以快起来,Oracle 12
我来答
分享
Mr.Cui
2023-03-22
SQL 求助,如下sql怎么改写可以快起来,Oracle 12
SQL 求助,如下sql怎么改写可以快起来,Oracle 12C
/*tabs表数据量5000w,会持续增长; time列有索引; end_time 非空时两行数有联系,end_time d列数值比 start_time 多 1; where 条件只能基于end_time判断*/ SELECT * FROM (SELECT a, b, c, t.time start_time, LEAD (t.time, 1, SYSDATE) OVER (PARTITION BY a, b ORDER BY time) end_time FROM tabs) WHERE end_time > = SYSDATE - 8 / 24 ORDER BY 1, 2;
复制
我来答
添加附件
收藏
分享
问题补充
7条回答
默认
最新
赵勇

1、从SQL逻辑上看,全表描是必须的。从避免全表扫的角度看,可以将SQL中出现的列建一个组合索上,变全表扫为对索引的快速全扫。

2、如果从业务上,我们可以确定,满足条件的记录的TIME列上的值,一定是某个时间之后的,且施加这个条件后,满足条件的记录不多,走TIME列上的索引会比较高效的话,那么可以在内层查询中,加入对TIME列的过滤条件。即这个条件的加入,不会导致结果不同。

暂无图片 评论
暂无图片 有用 0
打赏 0
Thomas

create table tab (a varchar2(10),b varchar2(10),c varchar2(10),time date);

insert into tab values ('南京','玄武区','ZHANG',sysdate -2/3);
insert into tab values ('南京','玄武区','LI',sysdate -0.5);
insert into tab values ('南京','玄武区','ZHAO',sysdate -0.25);
insert into tab values ('南京','玄武区','SHEN',sysdate -0.21);
insert into tab values ('南京','玄武区','GU',sysdate -0.2);
insert into tab values ('武汉','江汉区','WANG',sysdate -0.48);
insert into tab values ('武汉','江汉区','GUAN',sysdate -0.24);

commit;

业务逻辑到底是什么?能否详述?
如果只存在一条a='xx',b='yy'的记录,且这条记录的time是5年前比如2018-03-01 00:00:00,那么这条SQL会查出这条记录,其start_time为
2018-03-01 00:00:00,end_time为sysdate即SQL发出的时间;过了8小时,又发出同样SQL,这条记录仍会被查出,只不过end_time变成了8小时后
那个sysdate, 那么反复列出这条记录,意义何在?
我们先按原SQL查一下:
SQL>
SELECT *
FROM (SELECT a,
b,
c,
t.time start_time,
LEAD (t.time, 1, sysdate) OVER (PARTITION BY a, b ORDER BY time) end_time
FROM tab t)
WHERE end_time > = SYSDATE - 1/3 ORDER BY 1, 2;

A B C START_TIME END_TIME
---------- ---------- ---------- ------------------- -------------------
南京 玄武区 LI 2023-03-22 08:15:48 2023-03-22 14:15:48
南京 玄武区 ZHAO 2023-03-22 14:15:48 2023-03-22 15:13:24
南京 玄武区 SHEN 2023-03-22 15:13:24 2023-03-22 15:27:48
南京 玄武区 GU 2023-03-22 15:27:48 2023-03-22 20:15:58
武汉 江汉区 WANG 2023-03-22 08:44:36 2023-03-22 14:30:12
武汉 江汉区 GUAN 2023-03-22 14:30:12 2023-03-22 20:15:58



看到C='GU'和C='GUAN'的记录,就属于这种情况。本身它们孤独一枝,但因为所谓的LEAD (t.time, 1, sysdate)让它们出现在结果集里。
如果我们改写下,屏蔽掉这些记录,是否在业务上可行?改写后如下:
col a for a10;
col b for a10;
col c for a10;
SQL>
SELECT *
FROM (SELECT a,
b,
c,
t.time start_time,
LEAD (t.time, 1, null) OVER (PARTITION BY a, b ORDER BY time) end_time
FROM tab t)
WHERE end_time > = SYSDATE - 1/3 and end_time is not null ORDER BY 1, 2;

A B C START_TIME END_TIME
---------- ---------- ---------- ------------------- -------------------
南京 玄武区 LI 2023-03-22 08:15:48 2023-03-22 14:15:48
南京 玄武区 ZHAO 2023-03-22 14:15:48 2023-03-22 15:13:24
南京 玄武区 SHEN 2023-03-22 15:13:24 2023-03-22 15:27:48
武汉 江汉区 WANG 2023-03-22 08:44:36 2023-03-22 14:30:12


如果确实可以这样改写,那么也可以改写成如下:

with s as
(SELECT a,b,
lag(t.c,1,null) OVER (PARTITION BY a, b ORDER BY time) as c,
lag(t.time,1,null) OVER (PARTITION BY a, b ORDER BY time) as start_time,
t.time as end_time from tab t WHERE t.time > = SYSDATE - 1/3
ORDER BY 1, 2),
u as (SELECT a,
b,
c,
tab.time start_time,
LEAD (tab.time, 1, null) OVER (PARTITION BY a, b ORDER BY time) end_time
FROM tab where (a,b) in (select a,b from s where c is null))
select * from u where end_time>=sysdate -1/3
union
select * from s where c is not null order by 1,2;
A B C START_TIME END_TIME
---------- ---------- ---------- ------------------- -------------------
南京 玄武区 LI 2023-03-22 08:15:48 2023-03-22 14:15:48
南京 玄武区 SHEN 2023-03-22 15:13:24 2023-03-22 15:27:48
南京 玄武区 ZHAO 2023-03-22 14:15:48 2023-03-22 15:13:24
武汉 江汉区 WANG 2023-03-22 08:44:36 2023-03-22 14:30:12

这样写与原SQL比,避免了查询全表,应该效率还行。









如果

暂无图片 评论
暂无图片 有用 0
打赏 0
Mr.Cui
题主
2023-03-24
如我补充的业务场景描述,业务是需要lead 取值sysdate数据的,这表示这个产品正在这个阶段生产
Thomas

感觉本身表设计会不会有问题。同一个a,b的记录,应该至多在表里存在两条,一条表示开始时间,一条表示结束时间。既然如此,为何不涉及成一条记录有start_time和end_time两个字段?如果因为设计不合理而只能在SQL上下功夫,也很累。

暂无图片 评论
暂无图片 有用 1
打赏 0
刘晓华
2023-03-23
言之有理
Mr.Cui
题主
2023-03-24
历史原因,没有追究。这库设计的time\date类型都是varchar2,这都能搞出来。
chengang

条件可以带入内表的。特点是 分区的最后一行总是符合条件,最后一行特殊处理即可,其它行其实就是要满足外查询条件。

暂无图片 评论
暂无图片 有用 0
打赏 0
chengang

借助你的问题。我写了一个优化案例。性能有9倍提升。

https://www.modb.pro/db/620081

暂无图片 评论
暂无图片 有用 0
打赏 0
Mr.Cui
题主
2023-03-24
您这个思路挺好的,我其实搞了表自连接取出来已经这个阶段完成的数据,就是没有考虑好怎么高效的取值正在生产的,也就是lead 取值 sysdate的部分,或许应该从业务场景考虑是否有其他合适的条件筛选
Mr.Cui

实际场景如同各位老师猜测,表设计有问题。
具体场景是一个产品生产每个阶段只记录了开始时间,没有记录结束时间,因此将指定条件操作的下一次开始时间作为结束时间来记录,如果没有下一次开始时间取sysdate。
业务是统计8h之内结束的相关数据,因此需要取值没有下一次记录的信息

暂无图片 评论
暂无图片 有用 0
打赏 0
Thomas

是统计8h之内结束的相关数据,如果没有下一次开始时间取sysdate。既然是统计8h内结束的操作,那么没有下一次的取sysdate,不合适啊。没有下一次的就是没结束。为何也要输出?

暂无图片 评论
暂无图片 有用 0
打赏 0
回答交流
Markdown


请输入正文
提交
相关推荐
一般来讲,Oracle数据库表的行数上限是多少?
回答 1
已采纳
Oracle一个表的行数理论上是无限的,不过因为存在着另外某些限制,使得它实际上无法达到“无限”。一般来讲,一个表空间最多有1022个数据文件(OracleDatabase10g及以上版本有一种新的B
Oracle adg主从切换的话,temp表空间的数据文件数不一致,有没有影响?
回答 3
临时表空间,是不影响的吧
19c的数据库,用 toad for12去连接的,这个错跟这个有关系吗
回答 1
根据server跟client兼容矩阵,如果支持12,那应该19c也可以兼容,如下图:你这个报错跟执行的操作跟语句有关系吧?可以看看detail。
openGauss和Oracle在应用场景上有什么区别吗?
回答 1
技术上Oracle场景更加多一些。如果不是对每个场景都要深度的话,比如时序,图等等。那么Oracle都支持了。opengauss算国产的。有国产要求的考虑一下。
oracle global-names的值能随便改吗,现在是true,要改成false?
回答 1
globalnames参数设置为TRUE,影响的是DBLINK。当GLOBALNAMES参数设置为TRUE时,使用DATABASELINK时,DATABASELINK的名称必须与被连接的数据库的GLO
Oracle清空数据文件,包含临时段
回答 2
已采纳
临时段的清理方法1、如果条件允许的话可以通过重启实例来释放2、不能重启实例的情况新建临时表空间;指定default临时表空间删除原来的临时表空间
Oracle的recover database using backup controlfile until cancel;恢复不了?
回答 1
已采纳
需要最新的redo文件。可以手动指定。如果redo已经丢失,则可能会丢失部分数据。如果允许丢失少量数据,则在rman中使用recoverdatabaseuntilsequence12918;&nbsp
scn最大2的63次方。请问2的63次方换成16进制是多少?谢谢!
回答 1
已采纳
0x8000000000000000
Oracle 12c 隐藏参数_defer_sga_enabled 配置问题
回答 1
在Oracle12c中,defersgaenabled是一个隐含参数,用于控制SGA(SystemGlobalArea)的延迟分配。该参数的默认值在大多数情况下是关闭(disabled)的。当defe
Windows环境RMAN备份脚本报错问题
回答 10
已采纳
修改日期格式之后一点问题没有啊,你命令行里面的百分号是不是中文百分号,手打英文%上去试试