`
dacoolbaby
  • 浏览: 1267306 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Oracle版 WITH语句的使用

阅读更多
  本文是看了DB2 SQL精粹有感写的Oracle版本,毕竟两个大公司的产品有不同嘛。
  说起WITH 语句,除了那些第一次听说WITH 语句的人,大部分人都觉得它是用来做
递归查询的。其实那只是它的一个用途而已,它的本名正如我们标题写的那样,叫做:公共
表表达式(Common Table Expression),从字面理解,大家觉得它是用来干嘛的呢?
其实,它是用来定义临时集合的。
WITH TEMP(ID,USER) AS(
SELECT ‘01’,’SAM’ FROM DUAL
UNION ALL
SELECT ‘02’,’MIKE’ FROM DUAL
UNION ALL
SELECT ‘03’,’TOM’ FROM DUAL
UNION ALL
SELECT ‘04’,’JANE’ FROM DUAL
)
SELECT * FROM TEMP;

WITH可以定义多个结果集,中间用逗号分隔。
(这种写法更加符合普通思维的逻辑,写出来的代码更加容易理解)
WITH 语句是为复杂的查询为设计的,的确是这样的,下面我们举个复杂的例子,想提高技术的朋友可千万不能错过。考虑下面的情况:
CREATE TABLE USER
(
 NAME VARCHAR(20) NOT NULL,--姓名
 DEGREE INTEGER NOT NULL,--学历(1、专科2、本科3、硕士4、博士)
 STARTWORKDATE date NOT NULL,--入职时间
 SALARY1 FLOAT NOT NULL,--基本工资
 SALARY2 FLOAT NOT NULL--奖金
);

假设现在让你查询一下那些1、学历是硕士或博士2、学历相同,入职年份也相同,但是工资(基本工资+奖金)却比相同条件员工的平均工资低的员工。(哈哈,可能是要涨工资),不知道你听明白问题没有?该怎么查询呢?我们是这样想的:
1、查询学历是硕士或博士的那些员工得到结果集1,如下:
 SELECT NAME,DEGREE,YEAR(STARTWORKDATE) AS WORDDATE, SALARY1+SALARY2 AS SALARY FROM USER WHERE DEGREE IN (3,4);

2、根据学历和入职年份分组,求平均工资得到结果集2,如下:
 SELECT DEGREE,YEAR(STARTWORKDATE) AS WORDDATE, AVG(SALARY1+SALARY2) AS AVG_SALARY
 FROM USER WHERE DEGREE IN (3,4)
 GROUP BY DEGREE,YEAR(STARTWORKDATE)

3、以学历和入职年份为条件联合两个结果集,查找工资<平均工资的员工,以下是完整的SQL:
 WITH TEMP1(NAME,DEGREE,WORDDATE,SALARY) AS
 (
 SELECT NAME,DEGREE,YEAR(STARTWORKDATE) AS WORDDATE, SALARY1+SALARY2 AS SALARY FROM USER WHERE DEGREE IN (3,4)
 ),
 TEMP2 (DEGREE,WORDDATE,AVG_SALARY) AS
 (
 SELECT DEGREE,YEAR(STARTWORKDATE) AS WORDDATE, AVG(SALARY1+SALARY2) AS AVG_SALARY
 FROM USER WHERE DEGREE IN (3,4)
 GROUP BY DEGREE,YEAR(STARTWORKDATE)
 )
 SELECT NAME FROM TEMP1, TEMP2 WHERE
 TEMP1.DEGREE=TEMP2.DEGREE
 AND TEMP1.WORDDATE=TEMP2.WORDDATE
 AND SALARY<AVG_SALARY;

查询结果完全正确,但我们还有改善的空间,在查询结果集2 的时候,我们是从user 表中取得数据的。其实此时结果集1 已经查询出来了,我们完全可以从结果集1 中通过分组得到结果集2,而不用从uer 表中得到结果集2,比较上面和下面的语句你就可以知道我说的是什么意思了!
WITH TEMP1(NAME,DEGREE,WORDDATE,SALARY) AS
(
SELECT NAME,DEGREE,YEAR(STARTWORKDATE) AS WORDDATE, SALARY1+SALARY2 AS SALARY FROM USER WHERE DEGREE IN (3,4)
 ),
 TEMP2 (DEGREE,WORDDATE,AVG_SALARY) AS
 (
 SELECT DEGREE,WORDDATE, AVG(SALARY) AS AVG_SALARY
 FROM TEMP1
 GROUP BY DEGREE,WORDDATE
 )
 SELECT NAME FROM TEMP1, TEMP2 WHERE
 TEMP1.DEGREE=TEMP2.DEGREE
 AND TEMP1.WORDDATE=TEMP2.WORDDATE
 AND SALARY<AVG_SALARY;

当你看到这时,如果很好的理解了上面的内容,我相信你会对WITH 语句有了一定的体会。然而WITH 语句能做的还不止这些,下面给大家介绍一下,如何用WITH 语句做递归查询。
 CREATE TABLE BBS
 (
 PARENTID INTEGER NOT NULL,
 ID INTEGER NOT NULL,
 NAME VARCHAR(200) NOT NULL
 );
insert into bbs (PARENTID,ID,NAME) values (0,1,'数据库开发');
insert into bbs (PARENTID,ID,NAME) values (1,11,'DB2');
insert into bbs (PARENTID,ID,NAME) values (11,111,'DB2 文章1');
insert into bbs (PARENTID,ID,NAME) values (111,1111,'DB2 文章1 的评论1');
insert into bbs (PARENTID,ID,NAME) values (111,1112,'DB2 文章1 的评论2');
insert into bbs (PARENTID,ID,NAME) values (11,112,'DB2 文章2');
insert into bbs (PARENTID,ID,NAME) values (1,12,'Oracle');
insert into bbs (PARENTID,ID,NAME) values (0,2,'Java 技术');
COMMIT;

那么,现在让你查询一下DB2 的所有文章及评论,怎么办?传统的方法就
很难查询了,这时候递归查询就派上用场了,如下:
WITH TEMP(PARENTID,ID,NAME) AS
 (
 SELECT PARENTID,ID,NAME FROM BBS WHERE NAME='DB2'---语句1
 UNION ALL---语句2
 SELECT B.PARENTID,B.ID,B.NAME FROM BBS AS B, TEMP AS T WHERE B.PARENTI
D=T.ID---语句3
 )
 SELECT NAME FROM TEMP;---语句4

运行后,我们发现,结果完全正确,那它到底是怎么运行的呢?下面我们详细讲解一下。
1、首先,语句1 将会执行,它只执行一次,作为循环的起点。得到结果集:DB2
2、接着,将循环执行语句3,这里我们有必要详细介绍一下。
首先语句3 的意图是什么呢?说白了,它就是查找语句1产生结果集(DB2)的下一级,那么在目录树中DB2 的下一级是什么呢?是‘DB2 文章1’和‘DB2 文章2’,并且把查询到的结果集作为下一次循环的起点,然后查询它们的下一级,直到没有下一级为止。
怎么样?还没明白?哈哈,不要紧,我们一步一步来:
首先,语句1 产生结果集:DB2,作为循环的起点,把它和BBS 表关联来查找它的下一级,查询后的结果为:‘DB2 文章1’和‘DB2 文章2’接着,把上次的查询结果(也就是‘DB2 文章1’和‘DB2 文章2’)和BBS 表关联来查找它
们的下一级,查询后的结果为:‘DB2 文章1 的评论1’ 和‘DB2 文章1 的评论2’。然后,在把上次的查询结果(也就是‘DB2 文章1 的评论1’ 和‘DB2 文章1 的评论2’)和BBS 表关联来查找它们的下一级,此时,没有结果返回,循环结束。
3、第三,将执行语句2,将所有的结果集放在一起,最终得到temp 结果集。
4、最后,我们通过语句4 从temp 临时集合中得到我们期望的查询结果。怎么样,这回理解了吧,如果还没有理解,那么我也无能为力了。
需要特别提醒的是
1、一定要注意语句3 的关联条件,否则很容易就写成死循环了。
2、语句2必须是UNION ALL

接下来,如果做如此更改后,再次执行WITH 递归语句
UPDATE BBS SET ID = 1 WHERE PARENTID = 1;

再次执行则会出现以下错误:
ERROR:ORA-32044:执行递归WITH查询时检测到循环

这时候,可以使用CYCLE语句忽略循环导致的错误,如下:
WITH TEMP(PARENTID,ID,NAME) AS
 (
 SELECT PARENTID,ID,NAME FROM BBS WHERE NAME='DB2'---语句1
 UNION ALL---语句2
 SELECT B.PARENTID,B.ID,B.NAME FROM BBS AS B, TEMP AS T WHERE B.PARENTI
D=T.ID---语句3
 )
 CYCLE ID SET DUP_IND TO 'Y' DEFAULT 'N'
 SELECT NAME FROM TEMP;---语句4

此时我们得到多了一列出来,DUP_IND
(PS.需要注意的是,这个结果和使用CONNECT BY NOCYCLE得到的结果并不一致,ID为1的记录会出现两次。后面会降到CONNECT BY语句的使用)

DUP_IND为’Y’的时候,表示出现了死循环。

同时CYCLE可以判断在在同一叶子上是否出现重复的值。
如果执行:
insert into bbs (PARENTID,ID,NAME) values (11,112,'DB2 文章1');

再次执行带CYCLE的递归查询语句。
也会发现有一个’Y’在上面,因为后面的“DB2 文章1”内容重复了。

但是,如果执行的插入数据语句是:
insert into bbs (PARENTID,ID,NAME) values (111,1113,'DB2 文章1');

则不会出现Y。
1
0
分享到:
评论

相关推荐

    oracle常用SQL语句(汇总版).docx

    Oracle 常用 SQL 语句汇总 Oracle 是一个功能强大且复杂的关系数据库管理系统,它提供了多种 SQL 语句来管理和操作数据库。在本文中,我们将详细介绍 Oracle 中常用的 SQL 语句,包括数据控制语句(DML)、数据定义...

    Oracle基本建表语句

    ### Oracle基本建表语句知识点总结 #### 一、创建用户 在Oracle数据库中,创建用户是基础操作之一。这通常用于控制不同开发者或应用程序之间的访问权限。 **语法:** ```sql CREATE USER &lt;username&gt; IDENTIFIED BY...

    oracle数据库startwith用法

    通过本文,我们详细介绍了Oracle数据库中 `START WITH` 和 `CONNECT BY` 的使用方法以及应用场景。这两个关键字对于处理具有层级结构的数据非常有用。此外,我们还讨论了如何利用 `LEVEL` 关键字来显示节点所在的...

    Oracle万能查询语句

    ### Oracle万能查询语句详解 #### 一、概述 在Oracle数据库中,查询语句是数据检索的核心工具。本文将详细介绍一个复杂的Oracle查询语句,该语句涉及多个表的连接、序列、存储过程以及多表查询等相关知识点。通过...

    Oracle_基本建表语句

    在Oracle中,创建自增序列可以使用`CREATE SEQUENCE`语句,如创建一个名为`class_seq`的序列: ```sql CREATE SEQUENCE class_seq INCREMENT BY 1 START WITH 1 MAXVALUE 999999 NOCYCLE NOCACHE; ``` 然后,在插入...

    oracle关于日期的查询语句

    本篇将深入探讨Oracle关于日期的查询语句及其相关知识点。 1. 数据类型: Oracle提供了多种日期和时间数据类型,包括DATE、TIMESTAMP、TIMESTAMP WITH TIME ZONE以及TIMESTAMP WITH LOCAL TIME ZONE。其中,DATE...

    Oracle的sql语句练习题及参考答案

    9. **分页**:`LIMIT`和`OFFSET`在某些数据库系统中用于实现数据分页,但Oracle使用`ROWNUM`或`FETCH NEXT`结合`WITH TIES`来实现类似功能。 10. **动态SQL**:允许在运行时构建和执行SQL语句,增强了SQL的灵活性,...

    oracle中sql语句用法

    根据提供的文件信息,我们可以深入探讨Oracle数据库中的...通过以上的详细介绍,我们已经了解了在Oracle数据库中如何创建序列、使用各种约束以及如何解锁用户的基本方法。这些技能对于维护和管理Oracle数据库至关重要。

    巧用with as 对sql语句多次利用

    对于需重复查询的sql语句,可巧用with as 对sql语句进行精简,提高查询效率

    oracle 复杂查询语句的使用

    在提供的示例中,使用了`JOIN...USING`语法的变体,即`JOIN...WITH (+)`(在Oracle中,这是一种过时的表示法,现代语法更倾向于使用`LEFT JOIN`): ```sql SELECT a.*, b.skill FROM students a, student_skill b...

    Oracle中轻松取得建表和索引的DDL语句

    我们都知道在9i之前,要想获得建表和索引的语句是一件很麻烦的事。我们可以通过 export with rows=no来得到,但它的输出因为格式的问题并不能...使用这个功能强大的工具,我们可以获得单个对象或整个SCHEMA 的DDL语句。

    数据库 创建索引 sql oracle

    * 用 Transact-SQL 语句创建索引:使用 CREATE INDEX 语句创建索引,例如 CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_name ON { table | view } ( column [ ASC | DESC ] [ ...n ] ) [ WITH [PAD_...

    Oracle 基本建表语句集

    ### Oracle基本建表语句集知识点详解 #### 创建用户 - **语法**: ```sql CREATE USER &lt;用户名&gt; IDENTIFIED BY &lt;密码&gt; DEFAULT TABLESPACE &lt;表空间名&gt; TEMPORARY TABLESPACE &lt;临时表空间名&gt;; GRANT &lt;角色&gt;,&lt;角色...

    Oracle常用sql语句

    ### Oracle常用SQL语句详解 #### 数据控制语句(DML) **1. INSERT语句** 插入数据到表中的操作通过`INSERT`语句实现。基本语法如下: ``` INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...)...

    Oracle_基本建表语句.doc

    本文将详细解析Oracle的基本建表语句以及相关的数据库操作。 首先,创建用户是数据库管理的第一步。以下是一个创建用户`han`的示例: ```sql CREATE USER han IDENTIFIED BY han DEFAULT TABLESPACE users ...

    DB2与ORACLE常用语句对照

    - `SELECT`语句基本兼容,但Oracle的`WITH`子句在DB2中可能需要额外的语法调整。 - Oracle的`ROWNUM`用于行号,DB2则使用`ROW_NUMBER()`函数。 - 分区查询,Oracle的`PARTITION BY`在DB2中对应`WINDOW`函数。 4....

    使用简单的SQL语句实现的Oracle数据库分页技术

    ### 使用简单的SQL语句实现的Oracle数据库分页技术 #### 环境:Oracle 9i 在Oracle数据库中实现分页查询是一项常见的需求,尤其是在处理大量数据时。本文将介绍一种利用简单SQL语句来实现Oracle数据库分页的方法,...

    ORACLE学习之常用数据库系统表语句

    接下来将详细介绍一些常用的Oracle系统表语句及其应用场景。 首先,对于统计分析,`ANALYZE TABLE`语句用于收集表或索引的统计信息,这有助于优化器更好地制定执行计划,从而提升查询效率。例如: ```sql ANALYZE ...

Global site tag (gtag.js) - Google Analytics