ORACLE with语句

本文通过复杂的SQL查询解决了一个关于三人年龄的谜题。利用WITH语句创建临时表,逐步筛选符合条件的年龄组合,最终确定三人的具体年龄。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用一个sql语句完成一下问题:
A,B,C三人的年龄相乘为36;相加等于所有在场的人的人数;年龄大的人的宠物仓鼠的有个腿是木头的,试着给出这三人的年龄。
解决方案:
需要知道用于查询分解的基本格式,考虑类似下面的表达式
    with
alias1 as (subQuery1)
alias2 as(subQuery2)
……
select
……

首先,注意带三人年龄的乘积是36,可设年龄都是整数,因此就必须创建一个1~36的范围内,——就需要一个36行的表,每一个表对应一个可能的年龄:
    with age_list as
{
select rowNum as age from dual where rowNum<=36;
},

是三个人,需要创建三个副本,同时年龄乘积是36——之后还要把这3个年龄相加,下面同时进行。注意前面的SQL结尾使用是一个逗号,下面的部分就简单地以一个新的别名开始(不再重复with):
     product_check as
{
select
age1.age as youngest,
age2.age as middle,
age3.age as oldest
age1.age+age2.age+age3.age as sumed
from age_list age1,age_list2 age2,age_list age3
where age1.age<=age2.age and age2.age<=age3.age //保证每种组合值出现一次
and age1.age*age2.age*age3.age=36
}

得出的结果是
[list]
[*] Y M O S
[*]——————————————————————————
[*] 1 1 36 38
[*] 1 2 18 21
[*] 1 3 12 16
[*] 1 4 9 14
[*] 1 6 6 13
[*] 2 2 9 13
[*] 2 3 6 11
[*] 3 3 4 10
[/list]
现在 年龄之和等于一个数,知道了现场人数仍不能确定,——说明查询集合的和至少有两行的值是一样的。
缩小输出范围
    summed_checked as
{
select youngest, middle, oldest ,sumed
from
{ select youngest,middle,oldest,sumed,count (*) over (partition by summed) cnt
from product_chect
}
where cnt>=2
}

输出结果:
[list]
[*] Y M O S
[*]————————————————————————————
[*] 1 6 6 13
[*] 2 2 9 13
[/list]
接着“年龄大的人的宠物仓鼠有个腿是木质的”,表明年龄大的人的年龄比中间那个要大。
所以
    select yongest,middle,oldest from sumed_checked
where oldest>middle

得出结果:
[list]
[*] Y M O S
[*]————————————————————————————
[*] 2 2 9 13
[*]
[/list]

完整的查询如下(在oracle9.2.0.1中测试通过):
with
age_list as
(select rowNum age from all_all_tables where rownum<=36),
product_check as
(
select
age1.age youngest,
age2.age middle,
age3.age oldest,
age1.age+age2.age+age3.age as sumed
from age_list age1,age_list age2,age_list age3
where age1.age<=age2.age and age2.age<=age3.age
and age1.age*age2.age*age3.age=36
),
sumed_check as
(
select youngest,middle,oldest,sumed
from
(
select youngest,
middle,
oldest,
sumed,
count(*) over(partition by sumed) cnt
from product_check
)
where cnt>=2
)
select youngest,middle,oldest
from sumed_check
where middle<oldest


----------------------------------------------------------------
oracle 中with的用法
当查询中多次用到某一部分时,可以用with语句创建一个公共临时表。因为子查询在内存临时表中,避免了重复解析,所以执行效率会提高不少。临时表在一次查询结束自动清除。
一般语法格式:
       with
alias_name1 as (subquery1),
alias_name2 as (subQuery2),
……
alias_nameN as (subQueryN)
select col1,col2…… col3
from alias_name1,alias_name2……,alias_nameN


例子:
      SQL> WITH
Q1 AS (SELECT 3 + 5 S FROM DUAL),
Q2 AS (SELECT 3 * 5 M FROM DUAL),
Q3 AS (SELECT S, M, S + M, S * M FROM Q1, Q2)
SELECT * FROM Q3;

[list]
[*]输出结果:
[*]S M S+M S*M
[*]---------- ---------- ---------- ----------
[*]8 15 23 120
[/list]

所有的子查询都可以用到,下面是转帖网上的一个UPDATE语句(url:http://www.oracle.com.cn/viewthread.php?tid=83530):(

在讨论一个有关表的UPDATE时, 写了如下的SQL:<原始需求,请参考:http://www.oracle.com.cn/viewthr ... ghlight=&page=2>

SQL> update test2 set spc = (
2 select substr(max(sys_connect_by_path(b.name, '-')),2) name
3 from (select rn, skycode id,
4 decode(rn, 1, 0, instr(skycode, '-', 1, rn-1)) + 1 sp,
5 decode(instr(skycode, '-', 1, rn), 0, length(skycode) + 1, instr(skycode, '-', 1, rn)) ep
6 from (select rownum rn from dual connect by rownum<=20) a, test2 b
7 where decode(rn, 1, 0, instr(skycode, '-', 1, rn-1)) + instr(skycode, '-', 1, rn) > 0
8 ) a, test b
9 where b.citycode = substr(a.id, sp, ep-sp) and a.id=test2.skycode
10 start with rn=1 connect by rn-1=prior rn and a.id = prior a.id );


当时是在9.2.0.7下做的,没有问题,可以有朋友在9.2.0.1下,就会出现:

ORA-03113:通信通道文件结束
ORA-03114:未连接ORACLE

的错误提示, 这是9.2.0.1的一个BUG, 在多次的子查询时出现, 我试过,在9.2.0.5已经没有了,但不知道从那个版本ORACLE做了更正.

前段时间在写类似的多子查询的SELECT语句时, ORACLE9I提供的一个新子句: WITH在某种程度上解决了部分这类错误的出现. 经测试,原来同样的写法,也可以用于UPDATE中, 如上面的语句,可以用WITH改写为:

SQL> update test2 set spc = (
2 with myque as (select rn, skycode id,
3 decode(rn, 1, 0, instr(skycode, '-', 1, rn-1)) + 1 sp,
4 decode(instr(skycode, '-', 1, rn), 0, length(skycode) + 1,
5 instr(skycode, '-', 1, rn)) ep
6 from (select rownum rn from dual connect by rownum<=20) a, test2 b
7 where decode(rn, 1, 0, instr(skycode, '-', 1, rn-1)) + instr(skycode, '-', 1, rn) > 0 )
8 select substr(max(sys_connect_by_path(b.name, '-')),2) name
9 from myque a, test b
10 where b.citycode = substr(a.id, sp, ep-sp) and a.id=test2.skycode
11 start with rn=1 connect by rn-1=prior rn and a.id = prior a.id );


已更新4行。
<think>嗯,用户之前问过关于Oracle中多对多递归查询的问题,现在又问了“with语句使用oracle”。我需要先回想一下之前的回答内容,确保这次回答的一致性和连贯性。之前详细解释了递归WITH子句在多对多关系中的应用,包括语法、示例和注意事项。这次用户可能想更全面地了解WITH语句Oracle中的各种用途,而不仅仅是递归查询。 首先,我需要确认用户的具体需求。他们可能对WITH语句的基础用法、应用场景或高级功能感兴趣。可能用户在使用Oracle时遇到了需要优化查询、重用子查询或者处理复杂逻辑的情况,因此想深入了解WITH语句的使用方法。 接下来,我需要整理WITH语句的不同用途。除了递归查询,OracleWITH子句(即公共表表达式,CTE)主要用于简化复杂查询,提高可读性,避免重复子查询。可能还需要提到它的临时视图特性,以及如何在同一个查询中多次引用同一个CTE。 然后,要考虑用户可能的疑问点。比如,WITH语句和临时表的区别,性能优化方面的考虑,或者如何避免常见的错误,比如循环引用或无限递归。特别是用户之前接触过递归查询,可能需要强调递归CTE和非递归CTE的不同之处,以及如何正确使用它们。 另外,用户可能希望看到具体的示例,特别是实际应用中的案例,比如分步统计、数据去重、层次查询等。这些例子能帮助他们更好地理解如何在实际项目中应用WITH语句。同时,需要确保示例的SQL语法正确,符合Oracle的标准,特别是版本兼容性,比如CYCLE子句在11g及以上版本才支持。 还要注意用户可能存在的误区。例如,WITH子句虽然可以提升可读性,但过度使用或不当使用可能导致性能问题。需要提醒用户合理使用,并结合索引、执行计划等优化手段。 最后,总结部分要简明扼要,强调WITH语句的优势和适用场景,帮助用户快速抓住重点。同时,可以推荐一些进一步学习的资源或最佳实践,方便用户深入探索。</think>以下是对Oracle中`WITH`语句(公共表表达式,CTE)的详细介绍,分步骤说明其使用方法和场景: --- ### 一、`WITH`语句基础 #### 1. **定义** - `WITH`语句(Common Table Expression,CTE)用于定义**临时命名查询结果集**,可在同一查询中多次引用。 - 类似“临时视图”,但仅存在于当前查询的生命周期内。 #### 2. **核心优势** - **简化复杂查询**:将多层嵌套的子查询拆分为逻辑块。 - **代码重用**:避免重复编写相同子查询。 - **递归支持**:处理树形/图结构数据(需配合`UNION ALL`)。 --- ### 二、基本语法 ```sql WITH cte_name1 (列1, 列2, ...) AS ( SELECT ... FROM ... -- 子查询1 ), cte_name2 AS ( SELECT ... FROM cte_name1 -- 子查询2(可引用其他CTE) ) SELECT * FROM cte_name2; -- 最终查询 ``` --- ### 三、典型应用场景 #### 1. **简化复杂查询** **示例**:统计部门平均工资及高于平均工资的员工 ```sql WITH dept_avg AS ( SELECT department_id, AVG(salary) avg_sal FROM employees GROUP BY department_id ) SELECT e.employee_id, e.salary, d.avg_sal FROM employees e JOIN dept_avg d ON e.department_id = d.department_id WHERE e.salary > d.avg_sal; ``` #### 2. **分步计算** **示例**:分阶段统计销售额 ```sql WITH region_sales AS ( SELECT region, SUM(amount) total FROM orders GROUP BY region ), top_regions AS ( SELECT region FROM region_sales WHERE total > 1000000 ) SELECT * FROM top_regions; ``` #### 3. **递归查询** **示例**:查询员工层级关系(树形结构) ```sql WITH employee_tree (employee_id, name, manager_id, level) AS ( -- 锚点:顶级管理者(无上级) SELECT employee_id, name, manager_id, 1 FROM employees WHERE manager_id IS NULL UNION ALL -- 递归:逐层向下查找下属 SELECT e.employee_id, e.name, e.manager_id, et.level + 1 FROM employees e JOIN employee_tree et ON e.manager_id = et.employee_id ) SELECT * FROM employee_tree; ``` --- ### 四、高级用法 #### 1. **多CTE串联** ```sql WITH temp1 AS (SELECT ...), temp2 AS (SELECT ... FROM temp1), temp3 AS (SELECT ... FROM temp2) SELECT * FROM temp3; ``` #### 2. **递归中的循环检测**(Oracle 11g+) ```sql WITH employee_tree (...) AS (...) CYCLE employee_id SET is_cycle TO 'Y' DEFAULT 'N' SELECT * FROM employee_tree WHERE is_cycle = 'N'; ``` #### 3. **MATERIALIZE提示强制物化**(性能优化) ```sql WITH /*+ MATERIALIZE */ temp AS (SELECT ...) SELECT * FROM temp; ``` --- ### 五、性能注意事项 1. **避免过度嵌套**:超过3层的CTE可能影响可读性。 2. **索引优化**:确保CTE中的JOIN字段已建索引。 3. **递归深度控制**:使用`WHERE level < N`限制层级。 4. **替代方案**:复杂场景可考虑临时表或内联视图。 --- ### 六、与子查询的对比 | 特性 | `WITH`语句 | 子查询 | |---------------------|---------------------|-------------------| | 可读性 | 高(分块命名) | 低(嵌套复杂) | | 重用性 | 可多次引用 | 不可重用 | | 递归支持 | 支持 | 不支持 | | 执行计划优化 | 可能被优化器物化 | 通常内联执行 | --- ### 七、实际案例 #### 场景:统计部门及子部门的总人数 ```sql WITH dept_hierarchy (department_id, parent_id, level) AS ( SELECT department_id, parent_id, 1 FROM departments WHERE parent_id IS NULL -- 根部门 UNION ALL SELECT d.department_id, d.parent_id, dh.level + 1 FROM departments d JOIN dept_hierarchy dh ON d.parent_id = dh.department_id ) SELECT dh.department_id, COUNT(e.employee_id) AS total_employees FROM dept_hierarchy dh LEFT JOIN employees e ON dh.department_id = e.department_id GROUP BY dh.department_id; ``` --- ### 总结 Oracle的`WITH`语句通过模块化查询逻辑显著提升代码可维护性,尤其适用于: 1. 递归数据处理(如组织结构、BOM表) 2. 多步骤复杂计算 3. 需要重复引用的子查询场景 建议在涉及多层嵌套或递归时优先使用`WITH`语句,并结合执行计划分析优化性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值