文章出處:http://blog.163.com/weidaolan666@126/blog/static/49479943201171710305298/ 感謝作者的分享
3.1 with基礎
使用WITH AS 語句可以爲一個子查詢語句塊定義一個名稱,使用這個子查詢名稱可以在查詢語句的很多地方引用這個子查詢。Oracle 數據庫像對待內聯視圖或臨時表一樣對待被引用的子查詢名稱,從而起到一定的優化作用。with子句是9i新增語法。
你可以在任何一個頂層的SELECT 語句以及幾乎所有類型的子查詢語句前,使用子查詢定義子句。被定義的子查詢名稱可以在主查詢語句以及所有的子查詢語句中引用,但未定義前不能引用。
with子句中不能嵌套定義<也就是with子句中不能有with子句>,但子查詢中出現的子查詢定義語句可以引用已定義的子查詢名稱。<可以引用前面已經定義的with子句>
with子句相關總結:
1.使用with子句可以讓子查詢重用相同的with查詢塊,通過select調用(with子句只能被select查詢塊引用),一般在with查詢用到多次情況下。在引用的select語句之前定義,同級只能定義with關鍵字只能使用一次,多個用逗號分割。
2.with子句的返回結果存到用戶的臨時表空間中,只做一次查詢,反覆使用,提高效率。
3.在同級select前有多個查詢定義的時候,第1個用with,後面的不用with,並且用逗號隔開。
5.最後一個with 子句與下面的查詢之間不能有逗號,只通過右括號分割,with 子句的查詢必須用括號括起來
6.如果定義了with子句,而在查詢中不使用,那麼會報ora-32035 錯誤:未引用在with子句中定義的查詢名。(至少一個with查詢的name未被引用,解決方法是移除未被引用的with查詢),注意:只要後面有引用 的就可以,不一定非要在主查詢中引用,比如後面的with查詢也引用了,也是可以的。
7.前面的with子句定義的查詢在後面的with子句中可以使用。但是一個with子句內部不能嵌套with子句。
8.當一個查詢塊名字和一個表名或其他的對象相同時,解析器從內向外搜索,優先使用子查詢塊名字。
9.with查詢的結果列有別名,引用的時候必須使用別名或*。
with子句優點:
1. SQL可讀性增強。比如對於特定with子查詢取個有意義的名字等。
2. with子查詢只執行一次,將結果存儲在用戶臨時表空間中,可以引用多次,增強性能。
with子句語法:
With alias_name as (select1), --as和select中的括號都不能省略
alias_name2 as (select2),--後面的沒有with,逗號分割,同一個主查詢同級別地方,with子查詢只能定義一次
…
alias_namen as (select n) –與下面的實際查詢之間沒有逗號
Select ….
with使用例子:
1.一般使用方式
如查詢銷售部門員工的姓名:
--with clause
with a as
(select id from s_dept where name=Sales order by id)
select last_name,title
from s_emp where dept_id in (select * from a);--使用select查詢別名
使用with 子句,可以在複雜的查詢中預先定義好一個結果集,然後在查詢中反覆使用,不使用會報錯。而且with 子句獲得的是一個臨時表,如果在查詢中使用,必須採用select from with 查詢名,比如
With cnt as(select count(*) from table)
Select cnt+1 from dual;
是錯誤的。必須是
With cnt as(select count(*) shumu from user_tables)
Select shumu+1 from cnt;
2.在大多數子查詢中引用,同級可見
再如下面兩個語句含義等價:
with a as (select trade_id,name from product) --使用之前定義
select name from a where a.trade_id in (
with b as (select id from trademark where id=1) --使用之前定義
select id from b
);
select name from product where trade_id in (select id from trademark where id=1);
3. with子查詢不可嵌套定義,但是後面的with定義可以引用前面的結果集。
with select_trade as (select trade_id from product where id=1),
--後面的with子查詢可以引用前面的結果
select_trademark as (select name from trademark where id=(select trade_id from select_trade))
select * from select_trademark;
--這條語句錯誤
with select_trade as
--with中有嵌套with,不允許
( with temp as ( select trade_id from product where id=1)
select trade_id from temp
),
select_trademark as (select name from trademark where id=(select trade_id from select_trade))
select * from select_trademark;
4. 同級定義,只能有一個with
with a as (select trade_id from product),
b as (select id from trademark where rownum<3) --第2個定義不用with
select trade_id from a
minus
select id from b;
5. 在大多數子查詢中定義和使用,一個複雜的例子
SELECT a ,b
FROM (
--第1個定義t_with
WITH
t_with AS (SELECT 1 a FROM DUAL)
--子查詢使用t_with
SELECT x.a ,(
--內部定義了個t_with_z,並且使用t_with
WITH t_with_z as (SELECT 1 a FROM t_with )
SELECT s_1.a FROM t_with_z s_1 ,t_with s_2
6. 集合中使用with子查詢
--這個是錯誤的,同級只能有一個with定義,當然兩個可以寫在一起。
SELECT *
FROM (
WITH
t_with_1 AS (SELECT 1 a ,1 b FROM DUAL)
--此後的整個語句被認爲是一個主查詢語句
SELECT x.a , x.b FROM t_with_1 x
union all
WITH -- 此處再次出現定義
t_with_2 AS (SELECT 1 a ,2 b FROM DUAL)
SELECT y.a ,y.b FROM t_with_2 y
--正確
SELECT *
FROM (
--第1個with
WITH
t_with_1 AS (SELECT 1 a ,1 b FROM DUAL)
SELECT x.a , x.b ,x c FROM t_with_1 x
union all
--這裏不能用with再次定義,同級只能有1個with,可以寫在最前面
--內部with
SELECT y.a ,y.b , (WITH
t_with_x AS (SELECT * FROM t_with_1)
SELECT a FROM t_with_x )
FROM ( WITH
t_with_2 AS (SELECT 1 a ,2 b FROM DUAL)
SELECT a ,b FROM t_with_2
) y
WHERE y.a = (
with t_with_3 AS (SELECT 1 a ,2 b FROM DUAL)
select a from t_with_3
)
)
7.一個with查詢的實例:
查詢出部門的總薪水大於所有部門平均總薪水的部門。部門表s_dept,員工表s_emp。分析:做這個查詢,首先必須計算出所有部門的總薪 水,然後計算出總薪水的平均薪水,再篩選出部門的總薪水大於所有部門總薪水平均薪水的部門。那麼第1 步with 查詢查出所有部門的總薪水,第2 步用with 從第1 步獲得的結果表中查詢出平均薪水,最後利用這兩次的with 查詢比較總薪水大於平均薪水的結果,如下:
with
--step1:查詢出部門名和部門的總薪水
dept_costs as(
select a.name,sum(b.salary) dept_total
from
s_dept a,s_emp b
where a.id=b.dept_id
group by a.name
),
--step2:利用上一個with查詢的結果,計算部門的平均總薪水
avg_costs as(
select sum(dept_total)/count(*) dept_avg
from dept_costs
)
--step3:從兩個with查詢中比較並且輸出查詢結果
select name,dept_total
from dept_costs
where
dept_total>
(
select dept_avg
from
avg_costs
)
order by name;
從上面的查詢可以看出,前面的with 查詢的結果可以被後面的with查詢重用,並且對with 查詢的結果列支持別名的使用,在最終查詢中必須要引用所有with 查詢,否則會報錯ora-32035 錯誤。
再如有這樣一個需求:一個查詢,如果查詢的結果行不滿足是10 的倍數,則補空行,直到是查詢出的行數是10 的倍數。例如:select * from trademark這個查詢。
3.2 11G R2with新特性
新版本的WITH 允許遞歸使用,在功能上比原來有了質的飛躍,可以實現許多原來CONNECT BY做不到的事。
3.3 with對執行計劃的影響
with子句有可能會改變執行計劃。