3

所以我知道某些事情在 SQL 中是如何工作的,但我不知道为什么,而且我无法在网上找到一个很好的外行描述。作为参考,我使用的是 Oracle 11g 和 TOAD。

问题 1 - 符合条件的外部联接

我知道,如果您在外部联接表上放置条件,则无论您的语法如何,您都会将查询转换为内部联接。所以这个查询作为一个内部连接:

SELECT a.field1, b.field1 
FROM tableA a 
LEFT JOIN tableB b on a.key = b.key 
WHERE b.field2 = 'someCriteria'

解决此问题的方法是在第二个表的条件中包含“OR IS NULL”。我知道这是真的,但我一直无法理解为什么会这样。有人可以解释为什么外部表上的标准将外部联接变成内部联接吗?

问题 2 - 将标准添加到不同的子句更改结果

所以以上是真的,我一直在努力解决我的标准顺序如何改变以下两个查询的结果。我有两个表 - tableA 和 tableB - 并且需要对 tableA 的子集与 tableB 的子集进行左连接比较。

SQL1

SELECT DISTINCT a.field1, b.field2
FROM tableA a 
LEFT JOIN tableB b 
on a.key = b.key
AND (b.field2 = 'somecriteria' or b.field2 IS NULL)
WHERE a.field1 = 'othercriteria' 

结果:SQL1 给了我正确的左连接结果。

SQL2

SELECT DISTINCT a.field1, b.field2
FROM tableA a 
LEFT JOIN tableB b 
on a.key = b.key 
WHERE a.field1 = 'othercriteria' 
AND (b.field2 = 'somecriteria' or b.field2 IS NULL)

结果:SQL2 仅拉回两个子集的内部连接结果(不包括 tableA 中 tableB 不匹配的那些行)。

了解结果

其原因与 join 和 where 运行的顺序有关。我可以理解这种不断变化的性能,但我不理解为什么它会改变结果,因为语法几乎相同。为了解决这个问题,我运行了两个查询的执行计划并得到了以下结果(来自 TOAD):

执行计划:

SQL 1:

  • 5) Select 语句(行由 Select 语句返回)
  • 4) 唯一排序(对第 3 步中的行进行排序以消除重复行)
    • 3) Hash Join Right Outer (第 1、2 步的结果集被加入 (hash)
      • 1)表访问Full TABLE tableB(表tableB中的每一行都被读取)
      • 2)Table Access Full TABLE tableA(读取tableA中的每一行)

SQL 2:

  • 11) Select 语句(行由 Select 语句返回)
  • 10) 唯一排序(对第 9 步中的行进行排序以消除重复行)
  • 9) 返回步骤 4、8 中的所有不同行
    • 4)过滤(对于第3步返回的行,根据过滤条件过滤掉行)
      • 3) Hash Join Right Outer (第 1、2 步的结果集被加入 (hash)
      • 1)表访问Full TABLE tableB(表tableB中的每一行都被读取
      • 2)Table Access Full TABLE tableA(读取tableA中的每一行)
    • 8)过滤(对于第7步返回的行,根据过滤条件过滤掉行)
      • 7) Hash Join Right Outer (第 5、6 步的结果集被加入 (hash)
      • 5)Table Access Full TABLE tableB(读取tableB中的每一行)
      • 6)Table Access Full TABLE tableA(读取tableA中的每一行)

所以我毫不怀疑上述执行计划完美地解释了为什么 SQL 2 给我的结果与 SQL 1 不同,但我很难阅读这些计划。有人可以帮我翻译这些执行计划并解释为什么 SQL2 被视为 Inner Join,因为 tableA 标准列在 WHERE 子句而不是 JOIN 中?

提前致谢!!

4

2 回答 2

4

对于问题 1,考虑包含两行的表 a:

key       field1
1         a
2         b

表 b 包含 3 行:

key       field1       field2
1         c            someCriteria
1         d            notSomeCriteria
1         e            NULL

您的FROM子句(及其JOINs)有效地生成如下所示的结果集:

(a)key     (a)field1     (b)key      (b)field1     (b)field2
1          a             1           c             someCriteria
1          a             1           d             notSomeCriteria
1          a             1           e             NULL
2          b             NULL        NULL          NULL

在考虑该WHERE子句时,它不再真正“知道”某个特定JOIN是否成功 - 它不会根据连接是否成功选择性地应用标准。因此,如果您指定了b.field2should equal someCriteria,则表示它应该只返回第一行 ( 1,a,1,c,someCriteria)。

如果您想对可用列做出特定断言NULL,您确实希望该WHERE子句以这种方式运行并强制您明确考虑NULLs (无论是从NULL列生成还是通过JOIN失败生成)

我通常采用的解决方法是HLGEM 的答案中显示的方法,而不是添加,OR b.field2 IS NULL因为您通常希望排除 ( 1,a,1,e,NULL) 行。

于 2013-10-29T15:12:41.583 回答
2

我会把第一个写成:

SELECT a.field1, b.field1 
FROM tableA a 
LEFT JOIN tableB b on a.key = b.key 
AND b.field2 = 'someCriteria'

这将重新返回表 a 中的所有记录,并且 b.field1 只有在 b.field2 = 'somecriteria' 时才会有数据

于 2013-10-29T14:49:04.650 回答