Oracle - 使相关查询更高效

问题描述 投票:0回答:2

Oracle RDBMS:18C

这个主题是我上一个主题的后续主题:
Oracle - 具有空值条件的Where子句没有给出相应的结果

因为最后一题解决了(为了得到正确的结果),所以我关闭了。
这是另一个关于性能的问题,所以我提出这个新问题。
为了让我的问题更清楚,请先阅读上面的链接。

The Impaler使用decode对上一篇文章的回答正是我所需要的。
但后来我发现查询效率不是很高,因为我的外表比内表大得多,这可能是导致查询速度变慢的原因(与in运算符相比,请参阅下面的速度测试表)

由于专有问题,我无法上传我的实际工作表。
话虽这么说,让我用下面的小样本表来解释我的实际工作表:
db<>小提琴

With Desired_Column_Combination as (
  Select 'A1' ColA,'B2' ColB,'C2' ColC from dual union all 
  Select 'A1' ColA,'B1' ColB,'' ColC from dual union all --Should've 3 records
  Select 'A2' ColA,'' ColB,'C3' ColC from dual union all --Should've 2 records
  Select 'A3' ColA,'B1' ColB,'C1' ColC from dual
)
select * 
from SampleTable S
where exists (
  select 1 from Desired_Column_Combination d
  where decode(s.ColA, d.ColA, 1, 0) = 1
    and decode(s.ColB, d.ColB, 1, 0) = 1
    and decode(s.ColC, d.ColC, 1, 0) = 1
);

在我的实际工作台中:
<1>。 Desired_Column_Combination 由用户选择,条件数量最多可达 70 select ... from...。我使用 VBA 根据用户的选择制作动态 Desired_Column_Combination 表。
<2>。 Desired_Column_Combination 中的所有条件都是 100% 来自 SampleTable,因此 SampleTable 中至少有 1 条记录与 Desired_Column_Combination 表中的它相对应。换句话说,Desired_Column_Combination 表是 SampleTable 的子集。
<3>。 SampleTable 在 ColA、B 和 C 上有索引。
<4>。我只有查询表权限,无法删除/更新/更改表。

然后我尝试理解exists运算符,希望自己解决它。
经过几个小时的研究后,这是一个简短的结论:
一般来说,如果外表比内表大得多,那么使用 in 运算符会更好。另一方面,如果内表包含的行数多于外表,并且内表有索引,则使用 existsin 更快。

然后我在工作台上进行一些速度测试:

看来测试结果和我google的结论是一样的,因为我的外表SampleTable比内表Desired_Column_Combination大得多,所以exists运算符的性能比in运算符差。

我的问题是:
<1>。有没有办法使用 in 运算符,同时又不过度扩展 where 子句? (据我所知,为了在空值查询下使用in,当Desired_Column_Combination有空值条件时,我们需要在where子句中将每一列指定为空)
<2>。如果 exists 是唯一的答案,我们是否可以重写查询,使外表成为内表,反之亦然,以使 exists 运行得更快?

oracle exists correlated-subquery
2个回答
0
投票

您可以使用

AND
OR
代替
DECODE
:

With Desired_Column_Combination (colA, ColB, colC) as (
  Select 'A1', 'B2', 'C2' from dual union all 
  Select 'A1', 'B1', NULL from dual union all
  Select 'A2', NULL, 'C3' from dual union all
  Select 'A3', 'B1', 'C1' from dual
)
SELECT * 
FROM   SampleTable S
WHERE  EXISTS (
  SELECT 1
  FROM   Desired_Column_Combination d
  WHERE  (s.ColA = d.ColA OR (s.colA IS NULL AND d.colA IS NULL))
  AND    (s.ColB = d.ColB OR (s.colB IS NULL AND d.colB IS NULL))
  AND    (s.ColC = d.ColC OR (s.colC IS NULL AND d.colC IS NULL))
);

或者,由于您有静态选项列表,因此不需要使用

IN
EXISTS
或联接,只需在主查询上使用
WHERE
过滤器即可:

SELECT * 
FROM   SampleTable S
WHERE  (colA, colB, colC) IN (('A1', 'B2', 'C2'), ('A3', 'B1', 'C1'))
OR     (colB IS NULL AND (colA, colC) IN (('A2', 'C3')))
OR     (colC IS NULL AND (colA, colB) IN (('A1', 'B1')));

0
投票

DECODE
解决方案的问题在于它无法建立索引,因为该函数具有来自两个不同表的输入。如果只是简单的
NULL
处理并且您想要索引的好处,我建议使用简单的
NVL
函数和匹配的索引。

select * 
from SampleTable S
where exists (
  select 1 from Desired_Column_Combination d
  where NVL(s.ColA,' ') = NVL(d.ColA,' ')
    and NVL(s.ColB,' ') = NVL(d.ColB,' ')
    and NVL(s.ColC,' ') = NVL(d.ColC,' ')
);

现在是一个匹配索引,这样做时最好包含所有三列:

CREATE OR REPLACE INDEX index1 ON Desired_Column_Combination 
(NVL(colA,' '),NVL(colB,' '),NVL(colC,' '))
仅供参考,Oracle 默认

NULL

 逻辑的原因是 
NULL
 并不是一个有助于行标识的有意义的值。它的目的是“未知”或没有任何意义 - 它表示“缺乏”任何有意义的东西,因此如果连接键列中有一个“值”,通常您不想找到子行。当然也有例外,因此 Oracle 允许这样做,但您必须做一些额外的工作才能将它们视为作为行标识一部分的有意义的键值。 
NULL 是最简单的方法,但是将函数应用于列会导致索引使用失败,除非您创建相应的基于函数的索引来匹配。但通常当您发现自己处于这种情况时,您的数据模型就会出现问题,其中 NVL
 被用在了不该用于的事情上。
© www.soinside.com 2019 - 2024. All rights reserved.