TL;DR:如果参数这么说,我想消除整个连接+子选择。
假设我在这样的存储过程中有一个查询:
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 中做到这一点,但它提出了同样的可维护性问题。