TL; DR:如果参数如此,我想删除整个join + subselect。
假设我在这样的存储过程中有一个查询:
open cur_result for
select t1.* from table1 t1
join (select key, value, row_number() over (partition by key order by whatever) rn from table2) t2
on t1.key = t2.key and t2.rn = 1
where [...lots of things...]
and t2.value = 'something'
你看,我不只是将table1连接到table2,我只需要根据某些条件连接table2的第一条记录,因此子查询中的row_number计算和rn = 1附加连接条件。无论如何,关键是这是一个昂贵的子查询,我想根据这个子查询进行过滤。
我的目标是这个子查询应该是有条件的,基于一个额外的参数。如果我想重复所有内容,请看它的外观:
if should_filter_table2 = 1 then
[the above query is copied here completely]
else
open cur_result for
select t1.* from table1 t1
-- no table2 join
where [...lots of things...]
-- no table2 condition
end if;
问题是可能有许多这样的参数和几乎相同的SQL的许多分支看起来很难看并且难以维护。我也可以这样做:
open cur_result for
select t1.* from table1 t1
join (select key, value, row_number() over (partition by key order by whatever) rn from table2) t2
on t1.key = t2.key and t2.rn = 1
where [...lots of things...]
and (should_filter_table2 = 0 or t2.value = 'something')
这很容易维护,但如果参数说子查询无关紧要,它仍然无条件执行。根据我的经验,Oracle无法对此进行优化,这是一个巨大的性能损失。
所以问题是:你能在1个查询中做到这一点并且性能良好吗?像这样:
open cur_result for
select t1.* from table1 t1
join {case when should_filter_table2 = 1 then (select key, value, row_number() over (partition by key order by whatever) rn from table2) else [empty table] end} t2
on t1.key = t2.key and t2.rn = 1
where [...lots of things...]
and (should_filter_table2 = 0 or t2.value = 'something')
因此,如果should_filter_table2为0,则不应计算子查询,也不应该应用过滤器。
应避免使用动态SQL。我怀疑如何在动态SQL中执行此操作,但它会引发相同的可维护性问题。
答案 0 :(得分:1)
我不是100%确定优化器是否按照我的想法行事,但我可能会从以下内容开始。不幸的是,我手头没有测试数据来模拟长时间运行的查询。
select t1.* from table1 t1
where
(should_filter_table2 = 0 or (
(t1.key, 'something', 1) in (
select key, value, row_number() over
(partition by key order by whatever) rn
from table2)
)
)
and [...lots of things...]