除了第一次听说WITH语句的人,大部分人都觉得它是用来做递归查询的。其实那只是它的一个用途而已,
它的本名叫做:公共表表达式(Common Table Expression)它是用来定义临时集合的。VALUES语句也是用来定义临时集合的。那么WITH语句和value有什么区别呢?

VALUES语句是用明确的值来定义临时集合的,如下:

values (1,2), (1,3),(2,1)

WITH语句是用查询(也就是select语句)来定义临时集合的,从这个角度讲,有点像视图,不过不是视图。

--建表
DROP TABLE USER;CREATE TABLE USER (NAME VARCHAR(20) NOT NULL,---姓名  SEX INTEGER,---性别(1、男   2、女)  BIRTHDAY DATE---生日
); --插数据
insert into user (name,sex,birthday) values ('zhangshan','1','1990-1-1');
insert into user (name,sex,birthday) values ('lisi','2','1991-1-1');
insert into user (name,sex,birthday) values ('wangwu','1','1992-1-1');
insert into user (name,sex,birthday) values ('sunliu','2','1949-10-1');
insert into user (name,sex,birthday) values ('tianqi','1','1994-1-1');
insert into user (name,sex,birthday) values ('zhaoba','2','1995-1-1');
WITH TEST(NAME_TEST, BDAY_TEST) AS  --test是括号中查询出来的结果集命名,后接重命名列
(  SELECT NAME,BIRTHDAY FROM USER--语句1
)
SELECT NAME_TEST FROM TEST WHERE BDAY_TEST='1949-10-1';--语句2

下面我们来解释一下,首先语句1执行,它会产生一个有两列(NAME,BIRTHDAY)的结果集;接着,我们将这个结果集命名为test,并且将列名重命名为NAME_TEST, BDAY_TEST;
最后我们执行语句2,从这个临时集合中找到生日是1949-10-1,也就是共和国的同龄人。

下面我们举个VALUES语句和WITH语句结合使用的例子,如下:

WITH TEST(NAME_TEST, BDAY_TEST) AS
(  VALUES ('张三','1997-7-1'),('李四','1949-10-1')
)
SELECT NAME_TEST FROM TEST WHERE BDAY_TEST='1949-10-1'

从上面的介绍和WITH语句不为大多数人所熟悉可以猜测,WITH语句是为复杂的查询为设计的,的确是这样的,
下面我们举个复杂的例子,想提高技术的朋友可千万不能错过。考虑下面的情况:

--建表
DROP TABLE USER2;
CREATE TABLE USER2
(  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--奖金
);--插数据
insert into user2 (name,degree,startworkdate,salary1,salary2) values ('zhangsan',1,'1995-1-1',10000.00,1600.00);
insert into user2 (name,degree,startworkdate,salary1,salary2) values ('lisi',2,'1996-1-1',5000.00,1500.00);
insert into user2 (name,degree,startworkdate,salary1,salary2) values ('wangwu',3,'1997-1-1',6000.00,1400.00);
insert into user2 (name,degree,startworkdate,salary1,salary2) values ('sunliu',4,'1998-1-1',7000.00,1300.00);
insert into user2 (name,degree,startworkdate,salary1,salary2) values ('tianqi',2,'1999-1-1','7000','1300');
insert into user2 (name,degree,startworkdate,salary1,salary2) values ('zhaoba',1,'2000-1-1',9000,1400);
insert into user2 (name,degree,startworkdate,salary1,salary2) values ('qianjiu',3,'1997-1-1',2000,1000);
insert into user2 (name,degree,startworkdate,salary1,salary2) values ('dushe',4,'1992-1-1',3000,1000);
select * from user2;

假设现在让你查询一下那些 1、学历是硕士或博士 2、学历相同,入职年份也相同,但是工资(基本工资+奖金)却比相同条件员工的平均工资低的员工。
哈哈,可能是要涨工资),不知道你听明白问题没有?该怎么查询呢?我们是这样想的:

1、查询学历是硕士或博士的那些员工得到结果集1,如下:

SELECT NAME,DEGREE,YEAR(STARTWORKDATE) AS WORDDATE,SALARY1+SALARY2 AS SALARY FROM USER2 WHERE DEGREE IN (3,4);

2、根据学历和入职年份分组,求平均工资 得到结果集2,如下:

SELECT DEGREE,YEAR(STARTWORKDATE) AS WORDDATE, AVG(SALARY1+SALARY2) AS AVG_SALARY
FROM USER2 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 USER2 WHERE DEGREE IN (3,4)
),
TEMP2 (DEGREE,WORDDATE,AVG_SALARY) AS
(  SELECT DEGREE,YEAR(STARTWORKDATE) AS WORDDATE, AVG(SALARY1+SALARY2) AS AVG_SALARY
FROM USER2 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 USER2 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语句也可以查出来,的确是这样,如下:

SELECT U.NAME FROM USER2 AS U,
(
SELECT DEGREE,YEAR(STARTWORKDATE) AS WORDDATE, AVG(SALARY1+SALARY2) AS AVG_SALARY
FROM USER2 WHERE DEGREE IN (3,4)
GROUP BY DEGREE,YEAR(STARTWORKDATE)
) AS G
WHERE U.DEGREE=G.DEGREE
AND YEAR(U.STARTWORKDATE)=G.WORDDATE
AND (SALARY1+SALARY2)<G.AVG_SALARY;

那使用WITH 和不使用 WITH,这两种写法有什么区别呢?一般情况下这两种写法在性能上不会有太大差异,但是,
1、当USER表的记录很多
2、硕士或博士(DEGREE IN (3,4))在USER表中的比例很少

当满足以上条件时,这两种写法在性能的差异将会显现出来,为什么呢?因为不使用WITH写法的语句访问了2次USER表,
如果DEGREE 字段又没有索引,性能差异将会非常明显。

with的递归应用一

当你看到这时,如果很好的理解了上面的内容,我相信你会对WITH语句有了一定的体会。然而WITH语句能做的还不止这些,
下面给大家介绍一下,如何用WITH语句做递归查询。递归查询的一个典型的例子是对树状结构的表进行查询,考虑如下的情况:
01.论坛首页
02.–数据库开发
03.----DB2
04.------DB2 文章1
05.--------DB2 文章1 的评论1
06.--------DB2 文章1 的评论2
07.------DB2 文章2
08.----Oracle
09.–Java技术

以上是一个论坛的典型例子,下面我们新建一个表来存储以上信息。

drop table BBS;
CREATE TABLE BBS
(  PARENTID INTEGER NOT NULL,  ID INTEGER NOT NULL,  NAME VARCHAR(200) NOT NULL---板块、文章、评论等。
);
insert into bbs (PARENTID,ID,NAME) values
(0,0,'论坛首页'),
(0,1,'数据库开发'),
(1,11,'DB2'),
(11,111,'DB2 文章1'),
(111,1111,'DB2 文章1 的评论1'),
(111,1112,'DB2 文章1 的评论2'),
(11,112,'DB2 文章2'),
(1,12,'Oracle'),
(0,2,'Java技术');

现在万事兼备了,我们开始查询吧。假设现在让你查询一下‘DB2 文章1’的所有评论,有人说,这还不简单,如下这样就可以了。
SELECT * FROM BBS WHERE PARENTID=(SELECT ID FROM BBS WHERE NAME=‘DB2 文章1’);

答案完全正确。那么,现在让你查询一下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.PARENTID=T.ID ---语句3
)
SELECT NAME FROM TEMP; ---语句4

WITH 子句内的第一个 SELECT 语句是初始化表。它只执行一次。它的结果形成虚拟表的初始内容以作为递归的种子。在上面的示例中,种子是 ‘NAME’ 为 DB2的一行或多行。

第二个 SELECT 语句执行多次。将种子作为输入传递给第二个 SELECT 语句以产生下一个行集合。将结果添加(UNION ALL)到虚拟表的当前内容中,并放回到其中以形成用于下一次传递的输入。只要有行产生,这个过程就会继续。

运行后,我们发现,结果完全正确,那它到底是怎么运行的呢?下面我们详细讲解一下。
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
最后请大家猜想一下,把语句1的where子句去掉,将会产生什么样的结果呢?去掉where后将全是死循环,因为每次查出的结果集都是全查的记录,而且永远都是。

with递归应用二,行转列

--1.建表
drop table zxt_test
create table zxt_test( id varchar(10),ivalue varchar(20),iname varchar(20));
commit;
select * from zxt_test;--------------2.插入测试语句insert into zxt_test values('1','aa','x'),('2','bb','x'),('3','bb','x'),('1','bb','y'),('2','bb','y'),('3','bb','y');commit;with
s as ( --这里是用iname来分区,id来排序。如果表没有这样序号分明的id字段,可以用rowNum()生成序号select row_number()over(partition by iname order by id) id1, row_number()over(partition by iname order by id) id2,ivalue,iname from zxt_test),t(iname,id1,id2,ivalue) as(select iname,id1,id2,cast(ivalue as varchar(100)) from  s where id1 =1 and id2=1 --语句1union all select t.iname,t.id1+1,t.id2,cast(s.ivalue||','||t.ivalue as varchar(100))    --语句2from  s, t where s.id2=t.id1+1 and t.iname = s.iname
)   --where s.iname = t.iname可以去掉,不影响select iname,ivalue from t where t.id1= (select max(id1) from s where s.iname = t.iname); --语句3

结果集s

临时表t里面,首先执行语句1.取得根结果集,这是循环的基础。注意:语句1只执行一次。

第一次循环传入t到语句2获取到的结果集是: 这时联合的结果集临时表t是:(V为加豆号后的值)

INAME    t.id1+1     t.id2          V          INAME  t.id1   t.id2  VX         2               1           bb,aa           X            1  1       aaY         2               1           bb,bb           Y            1      1       bbX         2               1       bb,aaY          2               1       bb,bb

第二次循环传入t到语句2获取到的结果集是: 这时联合的最终结果集临时表t是:

INAME        t.id1+1    t.id2       V          INAME  t.id1     t.id2   VX                  3               1    bb,bb,aa        X          1           1       aaY                 3               1       bb,bb,bb         Y          1           1       bbX                 2               1   bb,aaY                  2               1   bb,bbX                  3               1   bb,bb,aaY                   3               1   bb,bb,bb

在语句3加条件可取最终结果集临时表t中id1值最大的记录,
得到最终所期望的行转列结果集:


DB2行转列:(不确定有多少行的情况)
实现思路,先递归,然后排序,取第一行。

with rs as (select bbd043,row_number() over() RN from bb72 where bae007='10001' ),
RPL(RN,bbd043) as(select ROOT.RN,CAST(ROOT.bbd043 as varchar(2000)) from rs ROOTUNION ALLSELECT CHILD.RN,CHILD.bbd043||','||PARENT.bbd043 FROM RPL PARENT,rs CHILD WHEREPARENT.RN+1=CHILD.RN)SELECT MAX(bbd043) bbd043 FROM RPLGROUP BY RN ORDER BY RN DESC FETCH FIRST 1 ROWS ONLY;

DB2中行转列效率比较
效率高,可应付大数据量

with s as ( select row_number()over() id1, row_number()over() id2,AAE004 from BB20 where AAE004 <> ''       ------sql01),t(id1,id2,AAE004) as(select id1,id2,AAE004 from  s where id1 =1 and id2=1 union all select t.id1+1,t.id2,cast(s.AAE004||','||t.AAE004 as varchar(20000))   from  s, t where s.id2=t.id1+1
)  select AAE004 from t where t.id1= (select max(id1) from s );

效率差,数据量一大,就屌丝了

with rs as (select AAE004,row_number() over() RN from BB20 where AAE004 <> '' ------sql02),
RPL(RN,AAE004) as(select ROOT.RN,CAST(ROOT.AAE004 as varchar(20000)) from rs ROOTUNION ALLSELECT CHILD.RN,CHILD.AAE004||','||PARENT.AAE004 FROM RPL PARENT,rs CHILD WHEREPARENT.RN+1=CHILD.RN)SELECT RPL.RN,MAX(AAE004) AAE004 FROM RPL GROUP BY RN ORDER BY RN DESC FETCH FIRST 1 ROWS ONLY;

DB2with的定义与用法相关推荐

  1. php面向对象程序设计,PHP面向对象程序设计类的定义与用法简单示例

    本文实例讲述了PHP面向对象程序设计类的定义与用法.分享给大家供大家参考,具体如下: class Person { private $name; private $sex; private $age; ...

  2. js indexof用法indexOf()定义和用法

    indexOf()定义和用法 indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置. 语法 stringObject.indexOf(searchvalue,fromindex) ...

  3. python定义链表节点_Python数据结构与算法之链表定义与用法实例详解【单链表、循环链表】...

    本文实例讲述了Python数据结构与算法之链表定义与用法.分享给大家供大家参考,具体如下: 本文将为大家讲解: (1)从链表节点的定义开始,以类的方式,面向对象的思想进行链表的设计 (2)链表类插入和 ...

  4. html里面onclick属性是什么,html中onclick事件属性定义与用法

    在前端网页设计时,离不开一些动态事件的交互,比如提交数据,登录,注册等等.这事件一般都需要通过HTML onclick事件的调用,这篇文章重点介绍一下 onclick事件的定义与用法 onclick事 ...

  5. php mysql_query的的用法_PHPmysqli_query()函数定义与用法

    定义和用法 mysqli_query() 函数执行某个针对数据库的查询. 语法mysqli_query(connection,query,resultmode); 参数描述: connection 必 ...

  6. java循环的概念_Java数据结构之循环队列简单定义与用法示例

    本文实例讲述了Java数据结构之循环队列简单定义与用法.分享给大家供大家参考,具体如下: 一.概述: 1.原理: 与普通队列的区别在于循环队列添加数据时,如果其有效数据end == maxSize - ...

  7. android object数组赋值_Java对象数组定义与用法详解

    本文实例讲述了Java对象数组定义与用法.分享给大家供大家参考,具体如下: 所谓的对象数组,就是指包含了一组相关的对象,但是在对象数组的使用中一定要清楚一点:数组一定要先开辟空间,但是因为其是引用数据 ...

  8. phppage类封装分页功能_PHP封装的page分页类定义与用法完整示例

    本文实例讲述了PHP封装的page分页类定义与用法.分享给大家供大家参考,具体如下: 亲测有效,见下图=========> 1. 测试实例test.php header("Conten ...

  9. php双向链表+性能,PHP双向链表定义与用法示例

    本文实例讲述了PHP双向链表定义与用法.分享给大家供大家参考,具体如下: 由于需要对一组数据多次进行移动操作,所以写个双向链表.但对php实在不熟悉,虽然测试各个方法没啥问题,就是不知道php语言深层 ...

最新文章

  1. 当adobe flash player不能安装时
  2. adb 命令小集(转)
  3. 如何正确使用Python临时文件
  4. 【深度学习】网络架构设计:CNN based和Transformer based
  5. html相对定位向上偏移,使用CSS的相对定位和偏移量
  6. [剑指offer]面试题第[38]题[JAVA][字符串的排列][回溯法]
  7. 【报告分享】罗兰贝格2019年关于人工智能的十个议题.pdf(附下载链接)
  8. 终于将win7的basic主题改成黑色了!
  9. 一天搞懂深度学习—学习笔记3(RNN)
  10. 高等数学上册下册笔记课后答案 同济大学第七版
  11. Windows系统关闭端口教程
  12. 国外常用的论文检索网站有哪些?
  13. 4键电子手表说明书_4键sport电子表使用说明书,按START键可循环选择12/24小时显示格式...
  14. 哈勃望远镜研究员测试区块链的空间数据处理
  15. python爬虫获取服务器信息,通过python自动化获取服务器信息,并写入到excel(示例代码)...
  16. 教你如何轻松解密Md5密码
  17. 具有 Unity Terrain 功能的简单环境设计
  18. 2022AcWing寒假算法每日一题之2058. 笨拙的手指
  19. Rao-Cramer下界
  20. 【Python机器学习及实践】进阶篇:模型实用技巧(特征提升)

热门文章

  1. B00006 函数itoa()
  2. MySQL 基本信息的查询(初始化配置信息 my.ini)
  3. Linux 下的任务管理 —— ps、top
  4. 多元高斯分布及多元条件高斯分布
  5. 算法求解中的变量、数组与数据结构(STL 中的容器)
  6. sklearn 中的 Pipeline 机制
  7. theano —— shared, function(outputs, updates, givens)
  8. C++基础——模板的0初始化
  9. 怎么用python做我的世界皮肤_Python爬取mc皮肤【爬虫项目】
  10. mysql导出数据到文件_MySQL导出数据到文件中