Oracle SQL强制引擎选择子查询作为内存中记录的子集[重复]

xqnpmsa8  于 2023-04-05  发布在  Oracle
关注(0)|答案(2)|浏览(144)

此问题在此处已有答案

Subquery function invoked twice if alias is used in main SQL(2个答案)
How to prevent Oracle from calling a function for every attribute of the function result(1个答案)
昨天关门了。
我有一个这样的Oracle SQL查询,它需要44秒运行。

select shipment_id, Shipment_Flow_API.Get_Next_Step(SHIPMENT_ID) Shipment_flow
from shipment_line_ovw
where
SHIPMENT_STATE = 'Preliminary' 
and Shipment_Flow_API.Get_Next_Step(SHIPMENT_ID) = 'Report picking, Print pick list'

如果我把最后一行取出来,像这样做,只需要0.43秒,就可以返回大约100条记录。

select shipment_id, Shipment_Flow_API.Get_Next_Step(SHIPMENT_ID) Shipment_flow
from shipment_line_ovw
where
SHIPMENT_STATE = 'Preliminary'

我在想,如果我试着把上面的一个cte或一个100条记录的子查询,然后在之后的shipping flow列上进行过滤,它应该运行得很快,就像下面一样。但是oracle引擎仍然试图从原始表中重新选择并按所有记录进行过滤,所以它仍然需要40多秒

with cte as (
select 
shipment_id, Shipment_Flow_API.Get_Next_Step(SHIPMENT_ID) Shipment_flow
from shipment_line_ovw
where
SHIPMENT_STATE = 'Preliminary' 
)

select * from cte
where Shipment_flow = 'Report picking, Print pick list';

如何在过滤之前将其作为内存中的一组记录,以便运行得更快?

lyfkaqu1

lyfkaqu11#

我明白了。可以把记忆中的记录变成这样。

with cte as (
select /*+ MATERIALIZE*/
shipment_id, Shipment_Flow_API.Get_Next_Step(SHIPMENT_ID) Shipment_flow
from shipment_line_ovw
where
SHIPMENT_STATE = 'Preliminary' 
)

select * from cte
where Shipment_flow = 'Report picking, Print pick list';

这样跑得快多了,0.73秒。
在这个问题的答案的帮助下弄明白了,尽管原来的问题和我的略有不同。
How do you create a temporary table in an Oracle database?

gywdnpxw

gywdnpxw2#

Oracle很可能在计算SHIPMENT_STATE predicate 之前执行了该函数,而您的函数相当慢。通过具体化CTE,您首先强制计算SHIPMENT_STATE predicate ,这肯定大大减少了函数计算的候选行。您可以使用带有NO_MERGE提示的常规内联查询块执行相同的操作。或者,如果这是一个视图,在一个块中添加或减少一个 predicate 可能会显著地改变视图的执行计划,即使是以您意想不到的方式。
然而,最快的方法不是这两个选项。相反,使用函数创建虚拟列,然后索引它。如果shipment_line_ovw是一个表:

ALTER TABLE shipment_line_ovw ADD 
(next_step varchar2(50) AS (Shipment_Flow_API.Get_Next_Step(SHIPMENT_ID)) VIRTUAL)
/
CREATE INDEX shipment_line_ovw_idx1 on shipment_line_ovw(shipment_state,next_step)
/

然后简单地说:

select shipment_id, Shipment_Flow_API.Get_Next_Step(SHIPMENT_ID) Shipment_flow
from shipment_line_ovw
where
SHIPMENT_STATE = 'Preliminary' 
and next_step = 'Report picking, Print pick list'

但是,如果shipment_line_ovw实际上是一个视图,那么您可能希望将虚拟列添加到底层基表并在视图中暴露next_step。如果您有任何排序,则可能需要进行一些返工以使 predicate 在视图中下推。但这是可行的。因此,最好直接查询表而不是视图。

相关问题