我刚刚发现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语句的连接策略是什么,为什么它不会出现在解释计划中?
感谢。
答案 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能够使用更多选项,同时它可以让您轻松地从子表中检索多个列,而无需多次扫描相同的表。