如何解决如何调整Oracle SQL查询以使其运行更快?
我在class_period
表中有10,000多个记录。当我运行如下所示的查询时,要花费太多时间来获取数据。
能否请您帮我-如何加快查询速度?
WITH DATA AS
( SELECT distinct class_time,class_id
from class_period
)
SELECT distinct class_id,trim(regexp_substr(class_time,'[^:]+',1,LEVEL)) class_time
FROM DATA
CONNECT BY regexp_substr(class_time,LEVEL) IS NOT NULL
样本数据作为图像附加 enter image description here
所需数据作为图像附加 enter image description here
我正在使用oracle 11g。
解决方法
- 修正您的查询,因此您无需使用
DISTINCT
。您的方法的问题在于,您正在使用具有多行输入的层次结构查询,并且无法将层次结构的每个级别与上一个级别相关联,因此查询会将其与 ALL 相关联的项目层次结构的上一个级别,您将获得成倍增加的深度重复生成的行。这效率极低。 - 从使用正则表达式更改为简单的字符串函数。
您可以使用:
WITH bounds ( class_id,class_time,start_pos,end_pos ) AS (
SELECT class_id,1,INSTR( class_time,':',1 )
FROM data
UNION ALL
SELECT class_id,end_pos + 1,end_pos + 1 )
FROM bounds
WHERE end_pos > 0
)
SELECT class_id,CASE end_pos
WHEN 0
THEN SUBSTR( class_time,start_pos )
ELSE SUBSTR( class_time,end_pos - start_pos )
END AS class_time
FROM bounds;
其中的示例数据:
CREATE TABLE data ( class_id,class_time ) AS
SELECT 1,'0800AM:0830AM' FROM DUAL UNION ALL
SELECT 1,'0900AM' FROM DUAL UNION ALL
SELECT 2,'0830AM:0900AM:0930AM' FROM DUAL UNION ALL
SELECT 2,'1000AM' FROM DUAL;
输出:
CLASS_ID | CLASS_TIME -------: | :--------- 1 | 0800AM 1 | 0900AM 2 | 0830AM 2 | 1000AM 1 | 0830AM 2 | 0900AM 2 | 0930AM
db 提琴here
但是,更好的方法是更改用于存储数据的模型,并停止将其存储为带分隔符的字符串,而是将其存储在单独的表中,或者存储为嵌套表中的集合。
使用第二个表的示例是:
CREATE TABLE data (
class_id NUMBER PRIMARY KEY
);
CREATE TABLE class_times (
class_id NUMBER REFERENCES data ( class_id ),class_time VARCHAR2(6)
);
INSERT ALL
INTO data ( class_id ) VALUES ( 1 )
INTO data ( class_id ) VALUES ( 2 )
INTO class_times ( class_id,class_time ) VALUES ( 1,'0800AM' )
INTO class_times ( class_id,'0830AM' )
INTO class_times ( class_id,'0900AM' )
INTO class_times ( class_id,class_time ) VALUES ( 2,'0930AM' )
INTO class_times ( class_id,'1000AM' )
SELECT * FROM DUAL;
然后您的查询将是(假设您需要data
和class_id
旁边的其他列):
SELECT d.class_id,c.class_time
FROM data d
INNER JOIN class_times c
ON ( d.class_id = c.class_id );
哪个输出:
CLASS_ID | CLASS_TIME -------: | :--------- 1 | 0800AM 1 | 0830AM 1 | 0900AM 2 | 0830AM 2 | 0900AM 2 | 0930AM 2 | 1000AM
使用嵌套表的示例是:
CREATE TYPE stringlist IS TABLE OF VARCHAR2(6);
CREATE TABLE data (
class_id NUMBER,class_time stringlist
) NESTED TABLE class_time STORE AS data__class_time;
INSERT INTO data ( class_id,class_time )
SELECT 1,stringlist( '0800AM','0830AM' ) FROM DUAL UNION ALL
SELECT 1,stringlist( '0900AM' ) FROM DUAL UNION ALL
SELECT 2,stringlist( '0830AM','0900AM','0930AM' ) FROM DUAL UNION ALL
SELECT 2,stringlist( '1000AM' ) FROM DUAL;
然后您的查询将变为:
SELECT d.class_id,ct.COLUMN_VALUE AS class_time
FROM data d
CROSS APPLY TABLE ( d.class_time ) ct
哪个输出:
CLASS_ID | CLASS_TIME -------: | :--------- 1 | 0800AM 1 | 0830AM 1 | 0900AM 2 | 0830AM 2 | 0900AM 2 | 0930AM 2 | 1000AM
db 提琴here
, MT0发现了connect by
过滤器的大问题,该过滤器允许读取所有行。您无需将其转换为递归CTE,因为您已经区分了要投影的所有列,因此可以将其视为主键(假设它不可为空,或者您不希望使用null值)
您还需要一个特殊的过滤器,以免混淆您以为您陷入了无限循环。
WITH DATA AS
( SELECT distinct class_time,class_id
from class_period
)
SELECT distinct class_id,trim(regexp_substr(class_time,'[^:]+',LEVEL)) class_time
FROM DATA
CONNECT BY regexp_substr(class_time,LEVEL) IS NOT NULL
and prior class_time = class_time
and prior class_id = class_id
and prior sys_guid() is not null
prior sys_guid() is not null
是一种特殊的过滤器,可防止它因ORA-01436: CONNECT BY loop in user data
而出错。
这应该与递归CTE相似。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。