Oracle SQL - 比较空值时的JOIN性能

时间:2012-05-04 14:37:06

标签: sql performance oracle join

早上好,

在一个查询中我昨天写了两个体面大小的结果集(每个结果大约50k),我的JOIN的一部分是检查数据是否匹配或是否为空的子句(下面的简化版本):

SELECT a JOIN b ON a.class = b.class OR (a.class is null AND b.class is null)

但是,我注意到一个严重的性能问题集中在使用OR语句。我使用以下方法解决了这个问题:

SELECT a JOIN b ON NVL(a.class, 'N/A') = NVL(b.class, 'N/A')

第一个查询具有不可接受的长运行时间,而第二个查询的速度快几个数量级(> 45分钟与<1)。由于更多的比较,我希望OR运行得更慢,但是在这个特定数据集中a.class = b.class = null的情况相对较少。

什么会导致表现时间大幅增加? Oracle SQL是否不像许多其他语言那样短路布尔比较?有没有办法在第二个查询中抢救第一个查询(用于一般SQL而不仅仅是Oracle)?

5 个答案:

答案 0 :(得分:4)

您正在使用任何具有空类的记录返回交叉产品。你的结果可以吗?

我在11gR2中创建了两个示例查询:

WITH a as 
(select NULL as class, 5 as columna from dual
 UNION
 select NULL as class, 7 as columna from dual
 UNION
 select NULL as class, 9 as columna from dual
 UNION
 select 'X' as class, 3 as columna from dual
 UNION
 select 'Y' as class, 2 as columna from dual),
 b as 
 (select NULL as class, 2 as columnb from dual
 UNION
 select NULL as class, 15 as columnb from dual
 UNION
 select NULL as class, 5 as columnb from dual
 UNION
 select 'X' as class, 7 as columnb from dual
 UNION
 select 'Y' as class, 9 as columnb from dual)
    SELECT * from a JOIN b ON (a.class = b.class 
                              OR (a.class is null AND b.class is null))

当我在此查询上运行EXPLAIN PLAN时,它表示表格(在我的情况下为内联视图)通过NESTED LOOPS加入。 NESTED LOOPS连接通过扫描一个表的第一行,然后扫描另一个表的每一行进行匹配,然后扫描第一个表的第二行,查找第二个表上的匹配等等来进行操作。因为您不是直接比较无论是JOIN的OR部分中的表,优化器都必须使用NESTED LOOPS。

在幕后它可能看起来像:

  • 获取表A,第1行。如果class为null,则在结果集中包含表A中的此行。
  • 仍然在表A第1行上,搜索表B,其中所有行都为class。
  • 在表A第1行和表B中的所有行
  • 上执行交叉产品
  • 在结果集中包含这些行
  • 获取表A,第2行。如果class为null,则在结果集中包含表A中的此行。
  • .... etc

当我将SELECT语句更改为SELECT * FROM a JOIN b ON NVL(a.class, 'N/A') = NVL(b.class, 'N/A')时,EXPLAIN指示使用了HASH JOIN。散列连接实质上生成较小表的每个连接键的散列,然后扫描大表,在匹配的每一行的较小表中查找散列。在这种情况下,由于它是一个简单的Equijoin,优化器可以毫无问题地对驱动表的每一行进行散列。

在幕后它可能看起来像:

  • 通过表A,将NULL类值转换为&#39; N / A&#39;
  • 随时随地散列表格A的每一行。
  • 哈希表A现在位于临时空间或内存中。
  • 扫描表B,将NULL类值转换为&#39; N / A&#39;,然后计算值的哈希值。散列表中的查找散列(如果存在)包括结果集中表A和B的连接行。
  • 继续扫描B.

如果您对查询运行EXPLAIN PLAN,您可能会发现类似的结果。

即使最终结果是相同的,因为您没有使用&#34; OR&#34;加入第一个查询中的表,优化程序无法使用使用更好的连接方法。如果驱动表很大,或者如果要对大型辅助表强制进行全表扫描,则嵌套的LOOPS可能会非常慢。

您可以使用ANSI COALESCE函数模拟其他数据库系统中的NVL oracle函数。这里真正的问题是你试图加入一个NULL值,你真的应该有一个&#34; NO CLASS&#34;或其他一些识别&#34; null&#34;在null的意义上的类=没有,而不是null = unknown。

在评论中回答您问题的附录:

对于空查询,SQL引擎将执行以下操作:

  1. 从表A中读取第1行,类为空,转换为&#39; N / A&#39;。
  2. 表B有3行,其中class为null,将每个null转换为N / A&#39;。
  3. 由于第一行与所有3行匹配,因此我们的结果集中添加了3行,一行用于A1B1,A1B2,A1B3。
  4. 从表A读取第2行,类为空,转换为&#39; N / A&#39; /
  5. 表B有3行,其中class为null,将每个null转换为N / A&#39;。
  6. 由于第二行与所有3行匹配,因此在我们的结果集中添加了3行,一行用于A2B1,A2B2,A2B3。
  7. 从表A读取第3行,类为空,转换为&#39; N / A&#39; /
  8. 表B有3行,其中class为null,将每个null转换为N / A&#39;。
  9. 由于第三行与所有3行匹配,因此我们的结果集中添加了3行,一行用于A3B1,A3B2,A3B3。 10 ..第4行和第5行不为空,因此不会在此部分连接中处理它们。
  10. 对于&#39; N / A&#39;查询,SQL引擎将执行以下操作:

    1. 从表A中读取第1行,类为空,转换为&#39; N / A&#39;,哈希此值。
    2. 从表A中读取第2行,类为空,转换为&#39; N / A&#39;,哈希此值。
    3. 从表A中读取第3行,类为空,转换为&#39; N / A&#39;,哈希此值。
    4. 从表A中读取第4行,类not null,哈希此值。
    5. 从表A中读取第5行,类not null,哈希此值。
    6. 哈希表C现在在内存中。
    7. 从表B中读取第1行,类为空,转换为&#39; N / A&#39;,哈希值。
    8. 将散列值与内存中的散列表进行比较,为每个匹配在结果集中添加一行。找到3行,A1,A2和A3。结果添加A1B1,A2B1,A3B1。
    9. 从表B中读取第2行,类为空,转换为&#39; N / A&#39;,哈希值。
    10. 将散列值与内存中的散列表进行比较,为每个匹配在结果集中添加一行。找到3行,A1,A2和A3。结果添加A1B2,A2B2,A3B2。
    11. 从表B中读取第3行,类为空,转换为&#39; N / A&#39;,哈希值。
    12. 将散列值与内存中的散列表进行比较,为每个匹配在结果集中添加一行。找到3行,A1,A2和A3。结果添加A1B3,A2B3,A3B3。

答案 1 :(得分:1)

在第一种情况下,因为每个null都不同,所以数据库不使用优化(对于来自a的每一行,检查表b中的每一行)。

在第二种情况下,数据库首先将所有空值更改为“N / A”,然后仅使用优化来比较a.classb.class

比较Oracle中的空值非常耗时。 Null是未定义的值 - 一个null与其他null不同。 比较两个几乎相同的查询的结果:

select 1 from dual where null is null

select 1 from dual where null = null

只有带有特殊is null子句的第一个查询才会返回正确答案。因此,无法索引空值。

答案 2 :(得分:0)

试试这个:

SELECT a from Table1 a JOIN JTable1 b ON a.class = b.class
where a.class is null
union all
SELECT a from Table1 a JOIN JTable1 b ON a.class = b.class
where b.class is null

应该加快速度

答案 3 :(得分:-1)

解释很简单: 首先必须在连接操作中使用嵌套循环,当您使用OR操作时,始终会发生。 第二个必须使用散列连接操作,这比前一个快。

答案 4 :(得分:-2)

为什么不让它变得容易一点。 像

SELECT * 从a,b 哪里 的A.class(+)= b.class(+)

我认为它更具可读性。