oracle 9i r2中引入了SQL-99中的with子句,它是一个实现子查询的工具,
我们可以用它来临时存储oracle经过计算得到的结果集。
with子句的作用有点类似global temporary tables,设计with子句目的,
是为提高复杂子查询的速度。下面是with子句的一些要点:
1.with子句应用于oracle 9i以及更高版本
2.正式的,with子句称作分解子查询
3.with子句应用于当一个查询被多次执行的情况
4.with子句对于递归查询也很有用(SQL-99,oracle不支持)
为了简单起见,下面的例子仅仅引用集合一次;
而with子句通常被用在一个查询中多次引用一个集合情况下。
--
with子句能够简化复杂的sql查询
在SQL-99中,我们可以用with子句来代替临时表。
oracle中的with子句一次性获得一个集合,并为其取一个名,
以便于我们在后面的查询中引用到它。
--
首先,SQL-99中的with子句让人很困惑,因为这个sql语句不是以select开始的;
然而,我们的查询语句可以以with子句开始,定义一个集合,在主查询中引用到集合,
它就好像一个"真正的表":
with subquery_name
as (
the aggregation SQL statement)
select (
query naming subquery_name);
--
回到我们过于简单化的例子,我们用with子句代替临时表(注意:通过使用global temporary table,
你会发现你的执行计划更优了,不过这取决于你的oracle版本):
WITH sum_sales AS(
select /*+ materialize */
sum(quantity) all_sales
from stores ),
number_stores AS(
select /*+ materialize */
count(*) nbr_stores
from stores ),
sales_by_store AS(
select /*+ materialize */
store_name,
sum(quantity) store_sales
from
store natural join sales )
SELECT store_name
FROM
store,
sum_sales,
number_stores,
sales_by_store
where
store_sales > (all_sales / nbr_stores);
--
注释掉了oracle中没有公开的实现提示;oracle中的实现提示是用来确定
在with子句内部创建的临时表是基于开销优化的。对于oracle 10g版本来说,
这是不必要的,但是它确保了这个表只被创建一次。
应该指出的是,with子句在oracle中并不是完全的起作用,
oracle的sql中就不支持用with来替代connect by的递归查询。
--
Here is an actual performance comparison of equivalent queries:
SQL> --*********************************************
SQL> -- Using subqueries
SQL> --*********************************************
SQL>
SQL> select
2 store_name,
3 sum(quantity) store_sales,
4 (select sum(quantity) from sales)/(select count(*) from store) avg_sales
5 from
6 store s,
7 sales sl
8 where
9 s.store_key = sl.store_key
10 having
11 sum(quantity) > (select sum(quantity) from sales)/(select count(*) from store)
12 group by
13 store_name
14 ;
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 31 | 4 (25)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 4 | | |
| 2 | TABLE ACCESS FULL | SALES | 100 | 400 | 2 (0)| 00:00:01 |
| 3 | SORT AGGREGATE | | 1 | | | |
| 4 | INDEX FULL SCAN | SYS_C003999 | 10 | | 1 (0)| 00:00:01 |
|* 5 | FILTER | | | | | |
| 6 | HASH GROUP BY | | 1 | 31 | 4 (25)| 00:00:01 |
| 7 | NESTED LOOPS | | 100 | 3100 | 3 (0)| 00:00:01 |
| 8 | TABLE ACCESS FULL | SALES | 100 | 900 | 2 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY INDEX ROWID| STORE | 1 | 22 | 1 (0)| 00:00:01 |
|* 10 | INDEX UNIQUE SCAN | SYS_C003999 | 1 | | 0 (0)| 00:00:01 |
| 11 | SORT AGGREGATE | | 1 | 4 | | |
| 12 | TABLE ACCESS FULL | SALES | 100 | 400 | 2 (0)| 00:00:01 |
| 13 | SORT AGGREGATE | | 1 | | | |
| 14 | INDEX FULL SCAN | SYS_C003999 | 10 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
113 consistent gets //IO数量
--
SQL> --*********************************************
SQL> -- Using CTAS(create table as select)
SQL> --*********************************************
SQL> create table t1 as select sum(quantity) all_sales from sales;
Table created.
SQL> create table t2 as select count(*) nbr_stores from store;
Table created.
SQL> create table t3 as select store_name, sum(quantity) store_sales from store natural join sales group by store_name;
Table created.
SQL>
SQL> select
2 store_name
3 from
4 t1,
5 t2,
6 t3
7 where
8 store_sales > (all_sales / nbr_stores);
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 61 | 6 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 61 | 6 (0)| 00:00:01 |
| 2 | MERGE JOIN CARTESIAN| | 1 | 26 | 4 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | T1 | 1 | 13 | 2 (0)| 00:00:01 |
| 4 | BUFFER SORT | | 1 | 13 | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | T2 | 1 | 13 | 2 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL | T3 | 1 | 35 | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------
30 consistent gets
--
SQL> --*********************************************
SQL> -- Using the WITH clause
SQL> --*********************************************
SQL>
SQL> with
2 number_stores as
3 (select count(*) nbr_stores from store),
4 total_sales as
5 (select sum(quantity) all_sales from sales),
6 store_sales as
7 (select store_name, sum(quantity) sales from store natural join sales group by store_name)
8 select
9 store_name
10 from
11 number_stores,
12 total_sales,
13 store_sales
14 where
15 sales > (all_sales / nbr_stores);
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 61 | 7 (15)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 61 | 7 (15)| 00:00:01 |
| 2 | NESTED LOOPS | | 1 | 26 | 3 (0)| 00:00:01 |
| 3 | VIEW | | 1 | 13 | 1 (0)| 00:00:01 |
| 4 | SORT AGGREGATE | | 1 | | | |
| 5 | INDEX FULL SCAN | SYS_C003999 | 10 | | 1 (0)| 00:00:01 |
| 6 | VIEW | | 1 | 13 | 2 (0)| 00:00:01 |
| 7 | SORT AGGREGATE | | 1 | 4 | | |
| 8 | TABLE ACCESS FULL | SALES | 100 | 400 | 2 (0)| 00:00:01 |
|* 9 | VIEW | | 1 | 35 | 4 (25)| 00:00:01 |
| 10 | SORT GROUP BY | | 10 | 310 | 4 (25)| 00:00:01 |
| 11 | NESTED LOOPS | | 100 | 3100 | 3 (0)| 00:00:01 |
| 12 | TABLE ACCESS FULL | SALES | 100 | 900 | 2 (0)| 00:00:01 |
| 13 | TABLE ACCESS BY INDEX ROWID| STORE | 1 | 22 | 1 (0)| 00:00:01 |
|* 14 | INDEX UNIQUE SCAN | SYS_C003999 | 1 | | 0 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
109 consistent gets