Oracle条件连接

时间:2012-08-21 11:29:47

标签: oracle select join plsql

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中执行此操作,但它会引发相同的可维护性问题。

1 个答案:

答案 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...]