Oracle中的单行子查询 - 什么是连接计划?

时间:2008-10-07 21:30:07

标签: sql database oracle

我刚刚发现Oracle允许您执行以下操作:

SELECT foo.a, (SELECT c 
               FROM bar 
               WHERE foo.a = bar.a) 
from foo

只要bar中只有一行与foo中的任何一行匹配。

我从PL / SQL开发人员那里得到的解释计划是:

SELECT STATEMENT, GOAL = ALL_ROWS               
 TABLE ACCESS FULL  BAR 
 TABLE ACCESS FULL  FOO 

这实际上并没有指定表的连接方式。一位同事断言,这比定期加入更有效率。真的吗?这样一个select语句的连接策略是什么,为什么它不会出现在解释计划中?

感谢。

2 个答案:

答案 0 :(得分:4)

您在那里的计划根本没有提供太多信息。

使用SQL * Plus并使用dbms_xplan获取更详细的计划。查找名为utlxpls.sql的脚本。

这提供了更多信息: -

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1837 | 23881 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| BAR  |    18 |   468 |     2   (0)| 00:00:01 |
|   2 |  TABLE ACCESS FULL| FOO  |  1837 | 23881 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("BAR"."A"=:B1)

Note
-----
   - dynamic sampling used for this statement

18 rows selected.

我没有创建任何索引或外键或收集表的统计信息,这将改变计划和连接机制的选择。 Oracle实际上是在这里进行NESTED LOOPS类型的连接。步骤1,对FOO返回的每一行执行内联子选择。

这种执行SELECT的方式并不快。它可能相同或更慢。通常,尝试并加入主WHERE子句中的所有内容,除非它变得非常难以理解。

答案 1 :(得分:2)

如果在bar(a)上创建一个普通索引,CBO应该可以使用,但我很确定它不能进行散列连接。只有在使用聚合函数并且在顶部SELECT中有多个单行子查询时,这些查询才有意义。即便如此,您始终可以将查询重写为:

SELECT foo.a, bar1.c, pub1.d
FROM foo
JOIN (SELECT a, MIN(c) as c
      FROM bar
      GROUP BY a) bar1
  ON foo.a = bar1.a
JOIN (SELECT a, MAX(d) as d
      FROM pub
      GROUP BY a) pub1
  ON foo.a = pub1.a

这将使CBO能够使用更多选项,同时它可以让您轻松地从子表中检索多个列,而无需多次扫描相同的表。