您将返回一个带有任何具有空类的记录的叉积。这对你的结果好吗?
我在 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 行。如果类为空,则将表 A 中的这一行包含在结果集中。
- 虽然仍在表 A 的第 1 行,但在表 B 中搜索类为空的所有行。
- 对表 A 第 1 行和表 B 中找到的所有行执行叉积
- 在结果集中包含这些行
- 获取表 A,第 2 行。如果类为空,则将表 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 类值转换为“N/A”
- 散列表 A 的每一行。
- 哈希表 A 现在位于临时空间或内存中。
- 扫描表 B,将 NULL 类值转换为“N/A”,然后计算值的哈希值。哈希表中的查找哈希,如果存在,则将表 A 和 B 中的连接行包含在结果集中。
- 继续扫描 B.
如果您对查询运行 EXPLAIN PLAN,您可能会发现类似的结果。
即使最终结果是相同的,由于您没有在第一个查询中使用“OR”连接表,因此优化器无法使用更好的连接方法。如果驱动表很大,或者如果您强制对大型辅助表进行全表扫描,则嵌套循环可能会非常慢。
您可以使用 ANSICOALESCE
函数来模拟其他数据库系统中的 NVL oracle 函数。这里真正的问题是你试图加入一个 NULL 值,在那里你真的应该有一个“NO CLASS”或其他一些识别“null”类的方法,在 null = nothing 而不是 null = unknown 的意义上.
在评论中回答您的问题的附录:
对于空查询,SQL 引擎将执行以下操作:
- 从表 A 中读取第 1 行,类为空,转换为“N/A”。
- 表 B 有 3 行,其类为空,将每个空转换为“N/A”。
- 由于第一行与所有 3 行匹配,因此将 3 行添加到我们的结果集中,一个用于 A1B1、A1B2、A1B3。
- 从表 A 中读取第 2 行,类为空,转换为 'N/A'/
- 表 B 有 3 行,其类为空,将每个空转换为“N/A”。
- 由于第二行匹配所有 3 行,因此将 3 行添加到我们的结果集中,A2B1、A2B2、A2B3 各行。
- 从表 A 中读取第 3 行,类为空,转换为“N/A”/
- 表 B 有 3 行,其类为空,将每个空转换为“N/A”。
- 由于第三行与所有 3 行匹配,因此将 3 行添加到我们的结果集中,A3B1、A3B2、A3B3 各行。10.. 第 4 行和第 5 行不为空,因此不会在连接的这一部分中处理它们。
对于“N/A”查询,SQL 引擎将执行以下操作:
- 从表 A 中读取第 1 行,类为空,转换为“N/A”,散列该值。
- 从表 A 中读取第 2 行,类为空,转换为“N/A”,散列该值。
- 从表 A 中读取第 3 行,类为空,转换为“N/A”,散列该值。
- 从表 A 中读取第 4 行,类不为空,散列此值。
- 从表 A 中读取第 5 行,类不为空,散列此值。
- 哈希表 C 现在在内存中。
- 从表 B 中读取第 1 行,类为空,转换为“N/A”,对值进行哈希处理。
- 将哈希值与内存中的哈希表进行比较,为每个匹配添加一行到结果集中。找到 3 行,A1、A2 和 A3。结果添加了 A1B1、A2B1、A3B1。
- 从表 B 中读取第 2 行,类为空,转换为“N/A”,散列值。
- 将哈希值与内存中的哈希表进行比较,为每个匹配添加一行到结果集中。找到 3 行,A1、A2 和 A3。结果添加了 A1B2、A2B2、A3B2。
- 从表 B 中读取第 3 行,类为空,转换为“N/A”,对值进行哈希处理。
- 将哈希值与内存中的哈希表进行比较,为每个匹配添加一行到结果集中。找到 3 行,A1、A2 和 A3。结果添加了 A1B3、A2B3、A3B3。