2

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 中做到这一点,但它提出了同样的可维护性问题。

4

1 回答 1

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...]
于 2012-08-21T11:47:31.647 回答