如何使用内联函数优化查询?
How to optimize query with Inline Function in it?
我有一个 table TAB_1 具有以下架构
CAR_NO(Varchar) START_DATE(Date) ACTUAL_ARRIVAL_TIME(Number) SOURCE_POINT(Varchar)
END_POINT(Varchar)
Table TAB_2 具有以下架构
CAR_NO(Varchar) ACTL_TIME_OF_ARRVL(Date) EVENT_CODE(Varchar)
我的查询是:
SELECT DISTINCT CAR_NO,START_DATE FROM TAB_1 WHERE
(TRUNC(START_DATE +CASE WHEN ACTUAL_ARRIVAL_TIME=0 THEN NULL ELSE ACTUAL_ARRIVAL_TIME END/1440)='10-Feb-2020' )
AND SOURCE_POINT=END_POINT
UNION
SELECT DISTINCT CAR_NO,START_DATE FROM TAB_2 WHERE EVENT_CODE='TD'
AND TRUNC( ACTL_TIME_OF_ARRVL)='10-Feb-2020'
列ACTUAL_ARRIVAL_TIME存储以分钟为单位的时间值,列ACTL_TIME_OF_ARRVL存储日期作为时间戳值,我试图找到所有在 2020 年 2 月 10 日终止的汽车。table 中的任何一个都可能缺少数据,所以我在这里使用了 UNION 操作,在此查询之上我必须应用更多操作,因此总体而言大约需要 35 秒的时间。请指导优化此查询。
在列值上使用 trunc()
将阻止使用该列上的索引(除非它是基于函数的索引)。最好使用日期范围,涵盖您感兴趣的一整天。将时间偏移添加到 start_date
也会影响索引; '10-Feb-2020'
是一个字符串而不是日期,所以你强制隐式转换 - 从来都不是一个好主意。您也不需要 distinct
和 union
作为(与 union all
不同)无论如何都可以消除重复项。
我建议这样作为起点:
select car_no, start_date
from tab_1
where source_point = end_point
and start_date >= date '2020-02-10' - actual_arrival_time * interval '1' minute
and start_date < date '2020-02-11' - actual_arrival_time * interval '1' minute
union
select car_no, start_date
from tab_2
where event_code='TD'
and actl_time_of_arrvl >= date '2020-02-10'
and actl_time_of_arrvl < date '2020-02-11'
actual_arrival_time * interval '1' minute
与 ACTUAL_ARRIVAL_TIME END/1440
的效果相同;第一个是间隔数据类型,第二个是一天的小数部分,但两者都将分钟数表示为可以添加到日期值的值。
我使用的是日期文字,它仍然是硬编码的。如果您真的在使用参数,则可以将 interval '1' day
添加到您想要的日期,而不是在一天后进行硬编码。无论哪种方式,都会在第一个日期的午夜或之后查找值,并在第二个日期的 before 午夜查找值 - 涵盖当天所有可能的时间。
第一个分支仍然不能真正正确使用索引,因为在范围计算中引用另一个列值的可变性,但是如果您知道例如 actual_arrival_time
在 24 小时内,您可以帮助它解决更严格的限制:
select car_no,start_date
from tab_1
where source_point = end_point
and start_date >= date '2020-02-10'
and start_date < date '2020-02-10' + interval '2' day -- depending on allowed ranges
and start_date + actual_arrival_time * interval '1' minute >= date '2020-02-10'
and start_date + actual_arrival_time * interval '1' minute < date '2020-02-10' + interval '1' day
union
select car_no, start_date
from tab_2
where event_code='TD'
and actl_time_of_arrvl >= date '2020-02-10'
and actl_time_of_arrvl < date '2020-02-10' + interval '1' day
这里start_date >= date '2020-02-10'
给索引一个下界去搜索(假设actual_arrival_time
不能为负数,这似乎是合理的); start_date < date '2020-02-10' + interval '2' day
给出上限。上限是多少取决于允许的值,特别是 actual_arrival_time
.
在您的问题中不清楚 tab1.start_date
是否总是午夜,因此您可以将检查的第一部分简化为精确的日期匹配而不是范围。但是然后查看 actual_arrival_time
可能没有必要......如果 start_date
实际上总是午夜并且 actual_arrival_time
被限制在 0 到 1440 之间那么它可以像这样简单:
select car_no,start_date
from tab_1
where source_point = end_point
and start_date >= date '2020-02-10'
and start_date < date '2020-02-10' + interval '1' day
union
...
就像第二个分支一样。但是您尝试处理它的方式表明情况可能并非如此,您只需要尽可能缩小初始索引搜索范围,然后再根据确切时间进行过滤。我怀疑 actual_arrival_time
可能代表几天,甚至几周或几个月;所以回到以前的版本,
and start_date < date '2020-02-10' + interval '2' day -- depending on allowed ranges
会扩展到您期望看到的最大值;或者可能完全省略。
您需要查看执行计划以了解它实际上在做什么,可能是针对整个查询和联合的每个分支。
如果您要经常这样做,可能值得将计算值 start_date + actual_arrival_time * interval '1' minute
添加到 tab1
作为虚拟列,并为其编制索引。
我猜是处理日期和时间的奇怪方式。
首先检查你的执行计划。
我想在数据很少的情况下效果很好,但在数据很多的情况下,性能就没了。
从 UNION 开始,它本身就是一个性能问题。
您在 TAB_1 中所做的内联操作取消了索引的使用。我希望你在 ACTUAL_ARRIVAL_TIME 和 ACTUAL_ARRIVAL_TIME 字段中有索引,但在你的情况下它们没有被使用。
我想这个查询是在一个进程的中间,所以你可以在执行它之前完成一些解决方法
1.Know 您的月经开始时间:例如参数 'startMinute'.
2.Know 您经期的结束分钟:例如参数 'endMinute'.
3.Rewite查询
SELECT DISTINCT CAR_NO,START_DATE
FROM TAB_1
WHERE START_DATE >= startMinute
AND START_DATE < endMinute
AND SOURCE_POINT = END_POINT
UNION
.
.
.
我也希望你在TAB_2.ACTL_TIME_OF_ARRVL有一个索引,而且用的是trunc,不使用索引。
也许您需要创建一个基于 函数的索引 TRUNC(ACTL_TIME_OF_ARRVL) 索引。
我相信您会发现以下 link 非常有趣:
https://blog.dbi-services.com/index-on-truncdate-do-you-still-need-old-index/
我建议首先将查询重写为:
SELECT CAR_NO, START_DATE
FROM TAB_1
WHERE SOURCE_POINT = END_POINT AND
TRUNC(START_DATE + ACTUAL_ARRIVAL_TIME * INTERVAL '1' MINUTE) = DATE '2020-02-10'
UNION -- ON PURPOSE TO REMOVE DUPLICATES
SELECT CAR_NO, START_DATE
FROM TAB_2
WHERE EVENT_CODE = 'TD' AND
TRUNC(ACTL_TIME_OF_ARRVL) = DATE '2020-02-10' ;
那么对于这个查询,你可以定义以下基于函数的索引:
create index idx_tab_1_f1 on tab_1 (TRUNC(START_DATE + ACTUAL_ARRIVAL_TIME * INTERVAL '1' MINUTE), SOURCE_POINT, END_POINT);
create index idx_tab_2_f2 on tab_2 (EVENT_CODE, TRUNC(ACTL_TIME_OF_ARRVL));
如果要在索引中包含日期的时间部分,也可以改写查询。目前还不清楚这对其他查询是否有用。
我有一个 table TAB_1 具有以下架构
CAR_NO(Varchar) START_DATE(Date) ACTUAL_ARRIVAL_TIME(Number) SOURCE_POINT(Varchar)
END_POINT(Varchar)
Table TAB_2 具有以下架构
CAR_NO(Varchar) ACTL_TIME_OF_ARRVL(Date) EVENT_CODE(Varchar)
我的查询是:
SELECT DISTINCT CAR_NO,START_DATE FROM TAB_1 WHERE
(TRUNC(START_DATE +CASE WHEN ACTUAL_ARRIVAL_TIME=0 THEN NULL ELSE ACTUAL_ARRIVAL_TIME END/1440)='10-Feb-2020' )
AND SOURCE_POINT=END_POINT
UNION
SELECT DISTINCT CAR_NO,START_DATE FROM TAB_2 WHERE EVENT_CODE='TD'
AND TRUNC( ACTL_TIME_OF_ARRVL)='10-Feb-2020'
列ACTUAL_ARRIVAL_TIME存储以分钟为单位的时间值,列ACTL_TIME_OF_ARRVL存储日期作为时间戳值,我试图找到所有在 2020 年 2 月 10 日终止的汽车。table 中的任何一个都可能缺少数据,所以我在这里使用了 UNION 操作,在此查询之上我必须应用更多操作,因此总体而言大约需要 35 秒的时间。请指导优化此查询。
在列值上使用 trunc()
将阻止使用该列上的索引(除非它是基于函数的索引)。最好使用日期范围,涵盖您感兴趣的一整天。将时间偏移添加到 start_date
也会影响索引; '10-Feb-2020'
是一个字符串而不是日期,所以你强制隐式转换 - 从来都不是一个好主意。您也不需要 distinct
和 union
作为(与 union all
不同)无论如何都可以消除重复项。
我建议这样作为起点:
select car_no, start_date
from tab_1
where source_point = end_point
and start_date >= date '2020-02-10' - actual_arrival_time * interval '1' minute
and start_date < date '2020-02-11' - actual_arrival_time * interval '1' minute
union
select car_no, start_date
from tab_2
where event_code='TD'
and actl_time_of_arrvl >= date '2020-02-10'
and actl_time_of_arrvl < date '2020-02-11'
actual_arrival_time * interval '1' minute
与 ACTUAL_ARRIVAL_TIME END/1440
的效果相同;第一个是间隔数据类型,第二个是一天的小数部分,但两者都将分钟数表示为可以添加到日期值的值。
我使用的是日期文字,它仍然是硬编码的。如果您真的在使用参数,则可以将 interval '1' day
添加到您想要的日期,而不是在一天后进行硬编码。无论哪种方式,都会在第一个日期的午夜或之后查找值,并在第二个日期的 before 午夜查找值 - 涵盖当天所有可能的时间。
第一个分支仍然不能真正正确使用索引,因为在范围计算中引用另一个列值的可变性,但是如果您知道例如 actual_arrival_time
在 24 小时内,您可以帮助它解决更严格的限制:
select car_no,start_date
from tab_1
where source_point = end_point
and start_date >= date '2020-02-10'
and start_date < date '2020-02-10' + interval '2' day -- depending on allowed ranges
and start_date + actual_arrival_time * interval '1' minute >= date '2020-02-10'
and start_date + actual_arrival_time * interval '1' minute < date '2020-02-10' + interval '1' day
union
select car_no, start_date
from tab_2
where event_code='TD'
and actl_time_of_arrvl >= date '2020-02-10'
and actl_time_of_arrvl < date '2020-02-10' + interval '1' day
这里start_date >= date '2020-02-10'
给索引一个下界去搜索(假设actual_arrival_time
不能为负数,这似乎是合理的); start_date < date '2020-02-10' + interval '2' day
给出上限。上限是多少取决于允许的值,特别是 actual_arrival_time
.
在您的问题中不清楚 tab1.start_date
是否总是午夜,因此您可以将检查的第一部分简化为精确的日期匹配而不是范围。但是然后查看 actual_arrival_time
可能没有必要......如果 start_date
实际上总是午夜并且 actual_arrival_time
被限制在 0 到 1440 之间那么它可以像这样简单:
select car_no,start_date
from tab_1
where source_point = end_point
and start_date >= date '2020-02-10'
and start_date < date '2020-02-10' + interval '1' day
union
...
就像第二个分支一样。但是您尝试处理它的方式表明情况可能并非如此,您只需要尽可能缩小初始索引搜索范围,然后再根据确切时间进行过滤。我怀疑 actual_arrival_time
可能代表几天,甚至几周或几个月;所以回到以前的版本,
and start_date < date '2020-02-10' + interval '2' day -- depending on allowed ranges
会扩展到您期望看到的最大值;或者可能完全省略。
您需要查看执行计划以了解它实际上在做什么,可能是针对整个查询和联合的每个分支。
如果您要经常这样做,可能值得将计算值 start_date + actual_arrival_time * interval '1' minute
添加到 tab1
作为虚拟列,并为其编制索引。
我猜是处理日期和时间的奇怪方式。 首先检查你的执行计划。
我想在数据很少的情况下效果很好,但在数据很多的情况下,性能就没了。
从 UNION 开始,它本身就是一个性能问题。
您在 TAB_1 中所做的内联操作取消了索引的使用。我希望你在 ACTUAL_ARRIVAL_TIME 和 ACTUAL_ARRIVAL_TIME 字段中有索引,但在你的情况下它们没有被使用。 我想这个查询是在一个进程的中间,所以你可以在执行它之前完成一些解决方法
1.Know 您的月经开始时间:例如参数 'startMinute'.
2.Know 您经期的结束分钟:例如参数 'endMinute'.
3.Rewite查询
SELECT DISTINCT CAR_NO,START_DATE
FROM TAB_1
WHERE START_DATE >= startMinute
AND START_DATE < endMinute
AND SOURCE_POINT = END_POINT
UNION
.
.
.
我也希望你在TAB_2.ACTL_TIME_OF_ARRVL有一个索引,而且用的是trunc,不使用索引。 也许您需要创建一个基于 函数的索引 TRUNC(ACTL_TIME_OF_ARRVL) 索引。 我相信您会发现以下 link 非常有趣:
https://blog.dbi-services.com/index-on-truncdate-do-you-still-need-old-index/
我建议首先将查询重写为:
SELECT CAR_NO, START_DATE
FROM TAB_1
WHERE SOURCE_POINT = END_POINT AND
TRUNC(START_DATE + ACTUAL_ARRIVAL_TIME * INTERVAL '1' MINUTE) = DATE '2020-02-10'
UNION -- ON PURPOSE TO REMOVE DUPLICATES
SELECT CAR_NO, START_DATE
FROM TAB_2
WHERE EVENT_CODE = 'TD' AND
TRUNC(ACTL_TIME_OF_ARRVL) = DATE '2020-02-10' ;
那么对于这个查询,你可以定义以下基于函数的索引:
create index idx_tab_1_f1 on tab_1 (TRUNC(START_DATE + ACTUAL_ARRIVAL_TIME * INTERVAL '1' MINUTE), SOURCE_POINT, END_POINT);
create index idx_tab_2_f2 on tab_2 (EVENT_CODE, TRUNC(ACTL_TIME_OF_ARRVL));
如果要在索引中包含日期的时间部分,也可以改写查询。目前还不清楚这对其他查询是否有用。