文章出处:http://blog.163.com/[email protected]/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子句有可能会改变执行计划。