SQLServer学习笔记系列6
一.写在前面的话
时间是我们每个人都特别熟悉的,但是到底它是什么,用什么来衡量,可能很多人会愣在那里。时间可以见证一切,也可以消磨一切,那些过往的点点滴滴可思可忆。回想往年清明节过后,在家乡的晚上总能听见阵阵的青蛙叫声,那是清脆的叫声,那是家乡的味道。时间一转眼,貌似那些日子已离我远去好久,在城市的喧嚣浮华中,找寻不到那种内心的宁静。感叹时间流逝的同时,怀念过去的点点滴滴。我想在繁华的都市中寻找一种安定的心情来学习,或许是一种不错的方式。学习才会让我们认清自己,找回自我,做内心的强者,不骄不躁,积极进取,阳光自信。努力的人最幸运!Just do it!

从今以后,别再过你应该过的人生,去过你想过的人生吧!——梭罗
二.派生表
派生表也就是特殊的子查询,用在from之后的子查询,同时这里注意一点:派生表必须起别名。
例如:加入我们要查询,顾客信息,以及每个国家所拥有的顾客数量。那么我们可以这样写:
SELECT * FROM
(
SELECT custid,COUNT(*) OVER(PARTITION BY country) AS N'顾客数量'
FROM Sales.Customers
) t
括号里面的子句就相当于一个派生表,假如我们在这里不给 COUNT(*) OVER(PARTITION BY country) 起别名,那么select * 就不知道取出哪一列,所以会报错。

同时有另一种起别名的方法,也就是在外面其别名,同样可以达到效果,写法如下:
SELECT * FROM
(
SELECT custid,COUNT(*) OVER(PARTITION BY country)
FROM Sales.Customers
) t(custid,顾客数量)
三.CTE(公用表表达式)
(1)常见CTE用法
如何使用CTE,在这里必须先定义,并且命名。定义形式如下:
WITH USE_Customers
AS
(
SELECT companyname,country
FROM Sales.Customers
WHERE country='USA'
)
那么我们要使用CTE,只要查询对应的CTE名称即可。
WITH USE_Customers
AS
(
SELECT companyname,country
FROM Sales.Customers
WHERE country='USA'
) SELECT * FROM USE_Customers
结果如图所示:

但是要注意的一点是:使用CTE时,外部查询一旦完成,那么CTE的生命期就结束了,如果需要在查询这个CTE,此时失效。

同时也可以给CTE每列其别名,其中也存在在表达式内部和外部起别名,看个人喜欢哪种方式。
WITH USE_Customers
AS
(
SELECT companyname AS N'公司名',country AS N'国家名'
FROM Sales.Customers
WHERE country='USA'
) SELECT * FROM USE_Customers
WITH USE_Customers(公司名,国家名)
AS
(
SELECT companyname ,country
FROM Sales.Customers
WHERE country='USA'
) SELECT * FROM USE_Customers
其查询结果都为:

(2)有参数的CTE
使用有参数的CTE可以多次使用,只需要改变参数的传递值即可。
例如:查询每个国家的顾客信息,国家存在USA,UK........,多个国家,此时我们将国家作为一个变量,类似于C#中的参数。声明CTE如下:
DECLARE @country NVARCHAR();
SET @country='USA';
WITH USE_Customers(公司名,国家名)
AS
(
SELECT companyname ,country
FROM Sales.Customers
WHERE country=@country
) SELECT * FROM USE_Customers

假如要查询UK国家的一些顾客信息,那么只需要改变set @country=‘UK’就可以查询了,不需要改变CTE的结构。
DECLARE @country NVARCHAR();
SET @country='UK';
WITH USE_Customers(公司名,国家名)
AS
(
SELECT companyname ,country
FROM Sales.Customers
WHERE country=@country
) SELECT * FROM USE_Customers

(3)复杂的CTE使用
【1】在这里,假如我们要查询年度订单数量在10以上的顾客信息。按照一般的思维,我们可以讲订单按照年度分组,然后用聚合函数count统计每个顾客下的订单数量,接着找出大于订单数量大于10的顾客信息。所以我们可以下sql如下:

查询结果如图所示:

【2】那么同时可以使用子查询,即派生表依次来出来问题。
第一步,先查询订单的年份,顾客的信息
SELECT YEAR(orderdate) AS orderyear,custid
FROM Sales.Orders

第二步,根据年份进行分组,统计每位顾客的订单数量。第一步查询出来的结果作为子查询,即派生表。
SELECT orderyear,custid,COUNT(*) AS N'订单数量'
FROM
(
SELECT YEAR(orderdate) AS orderyear,custid
FROM Sales.Orders
) AS t1
GROUP BY orderyear,custid

第三步,找出订单数量大于10的顾客信息。
SELECT orderyear,custid,ordercount
FROM
(
SELECT orderyear,custid,COUNT(*) AS ordercount
FROM
(
SELECT YEAR(orderdate) AS orderyear,custid
FROM Sales.Orders
) AS t1
GROUP BY orderyear,custid
) AS t2
WHERE ordercount >

是不是感觉使用子查询,一层套一层,不过分析以后,符合我们一般解决问题的思路先做哪一步,再做哪一步。在这里我们可以使用新学的CTE来解决问题。
【3】使用CTE来解决年度订单数量在10以上的顾客信息。
好处就是定义好的CTE,可以直接拿来作为一个派生表使用,那么我们可以这样定义CTE,按照上述的分析,分别定义三个CTE,最后查询出结果:
需要注意的一点事:定义多个CTE之间用逗号(,)隔开即可,不需要使用with重新定。
WITH yearorder01
AS
(
SELECT YEAR(orderdate) AS orderyear,custid
FROM Sales.Orders
),
yearorder02
AS
(
SELECT orderyear,custid,COUNT(*) AS ordercount
FROM yearorder01
GROUP BY orderyear,custid
),
yearorder03
AS
(
SELECT orderyear,custid,ordercount
FROM yearorder02
WHERE ordercount>
) SELECT * FROM yearorder03
查询结果如图所示,跟子查询结果相同:

四.多CTE
多个CTE的使用会让我们更加方便的进行相关子查询。例如:假如我们要查询年度有多少顾客,测试该怎么处理了?我们会想到,还是根据年度分组,然后聚合count(custid)求出每年度有多少顾客,其中要注意去重,即相同的custid属于同一个顾客,所以要写成count(distinct custid)。sql如下:
SELECT YEAR(orderdate) AS orderyear,COUNT(DISTINCT custid) AS custcount
FROM Sales.Orders
GROUP BY YEAR(orderdate)

现在加入有一个需求,就是要做一个查询,求出当年的顾客数量以及前一年的顾客数量,同时算出两者之间顾客数量隔了多少,我们又该怎么处理了?要求前一年和当前年的差,那么就可以想到,是否可以把当前查出来的结果集做一次连接,不就可以得到前一年和当前年的嘛,所以sql如下:
SELECT pre_orderyear,now_orderyear,pre_custcount,now_custcount,
(now_custcount-pre_custcount) AS N'顾客数量差'
FROM
(
SELECT YEAR(orderdate) AS now_orderyear,COUNT(DISTINCT custid) AS now_custcount
FROM Sales.Orders
GROUP BY YEAR(orderdate)
) AS t1
LEFT JOIN
(
SELECT YEAR(orderdate) AS pre_orderyear,COUNT(DISTINCT custid) AS pre_custcount
FROM Sales.Orders
GROUP BY YEAR(orderdate)
) AS t2
ON t1.now_orderyear=t2.pre_orderyear+;
其中就是将结果集做一个自身的连接,但是我们是不是发现这样写显得很冗余了,作为程序猿的我们就是要写出尽量简洁明了的code,Don‘t repeat yourself !在这里我们的CTE就发挥出他的作用,定义的CTE可以在下一步中使用,作为查询条件。那么我们看看用CTE如何实现?同理,我们使用CTE之前就必须先定义CTE,这里我们定义一个CTE,查询出每年的顾客数量,其实跟上面的sql一样,只不过定义成CTE的形式。
WITH custcount
AS
(
SELECT YEAR(orderdate) AS orderyear,COUNT(DISTINCT custid) AS custcount
FROM Sales.Orders
GROUP BY YEAR(orderdate)
) SELECT t1.orderyear AS nowYear,t2.orderyear AS preYear,t1.custcount AS nowcount,t2.custcount AS precount,
(t1.custcount-t2.custcount) AS N'顾客数量差'
FROM custcount t1
LEFT JOIN custcount t2
ON t1.orderyear=t2.orderyear+;
其结果如图所示:
其实我们从sql语句可以看出,CTE的简洁之处,就在于,可以将已定义的CTE重复使用进行连接,相比于子查询自身的连接,更加简洁,容易操作,加入需要的计算,我们只需要改变CTE的定义即可。
五.递归CTE
递归的CTE在递归查找中应用的非常多,其实可以看做一种层次的查找关系,比如公司一般情况下是分一级部门,二级部门,三级部门.......那么一级部门下包含多个二级部门,二级部门又包含多个三级部门等等.....,举例说明:公司内部有上下级的关系,假如我们要查找某个员工的上级是谁,一般情况下我们可以做一次自身的连接,找出上级是谁。
例如:查找所有顾客的上级信息。也就是在HR.employees里面的mgrid等于员工的empid,那么找到的那个员工几位mgrid的上司。sql如下:
SELECT t1.empid,t1.mgrid,t1.lastname,t2.empid,t2.lastname
FROM HR.Employees t1 LEFT JOIN hr.Employees t2
ON t1.mgrid =t2.empid
因为为了确保所有的员工信息都显示,所以在这里用到饿左连接,保证左边表里面的员工都存在。其结果如图:

这里再进一步讨论,加入我们要查询某个员工下面所有的下属信息,怎么办了?比如要查询2号员工的下属信息,我们这可以这样写sql:
SELECT * FROM
hr.Employees WHERE mgrid=

我们了可以看到2号员工下属有3号和5号,那么5号员工可能也有下属,接着查询:
SELECT * FROM HR.Employees
WHERE mgrid in
(SELECT empid FROM
hr.Employees WHERE mgrid=
)

我们可以看到,查出的结果中有4,6,7,8,9,那么4,6,7,8,9下面是否还有下属了,是不是还需要接着查询了,这样查下去就是无止尽的,因为我们不知道有多少层,所以递归CTE就是解决这样的问题的,递归CTE可以查询出所以层级的情况,直至结束。首先递归CTE需要定义一个起点,即查询起点。然后根据定义的CTE自身做递归查询,直至找出所有的节点信息。如下:
DECLARE @mgrid INT;
SET @mgrid=;
WITH Emplist
AS
(
--此处为起点,执行一次
SELECT empid,lastname,mgrid
FROM HR.Employees
WHERE mgrid=@mgrid
UNION ALL --递归开始 SELECT e.empid,e.lastname,e.mgrid
FROM HR.Employees e INNER JOIN Emplist m
ON e.mgrid=m.empid ) SELECT * FROM Emplist

定义一个变量,接受要查询的员工id,设为2,即查询2号员工下面所有的下属信息。
关于CTE的运用还有很多,这里只列出一些基本的用法,如有想法,可以提出,希望一起学习!
sqlserver学习笔记1:http://www.cnblogs.com/liupeng61624/p/4354983.html
sqlserver学习笔记2:http://www.cnblogs.com/liupeng61624/p/4367580.html
sqlserver学习笔记3:http://www.cnblogs.com/liupeng61624/p/4375135.html
sqlserver学习笔记4:http://www.cnblogs.com/liupeng61624/p/4388959.html
sqlserver学习笔记5:http://www.cnblogs.com/liupeng61624/p/4392746.html
希望各位大牛给出指导,不当之处虚心接受学习!谢谢!
SQLServer学习笔记系列6的更多相关文章
- SQLServer学习笔记系列3
一.写在前面的话 今天又是双休啦!生活依然再继续,当你停下来的时候,或许会突然显得不自在.有时候,看到一种东西,你会发现原来在这个社会上,优秀的人很多,默默 吃苦努力奋斗的人也多!星期五早上按时上班, ...
- SQLServer学习笔记系列2
一.写在前面的话 继上一次SQLServer学习笔记系列1http://www.cnblogs.com/liupeng61624/p/4354983.html以后,继续学习Sqlserver,一步一步 ...
- SQLServer学习笔记系列5
一.写在前面的话 转眼又是一年清明节,话说“清明时节雨纷纷”,武汉的天气伴随着这个清明节下了一场暴雨,整个城市如海一样,朋友圈渗透着清明节武汉看海的节奏.今年又没有回老家祭祖,但是心里依然是怀念着那些 ...
- SQLServer学习笔记系列1
一.前言 一直自己没有学习做笔记的习惯,所以为了加强自己对知识的深入理解,决定将学习笔记写下来,希望向各位大牛们学习交流! 不当之处请斧正!在此感谢!这边就先从学习Sqlserver写起,自己本身对数 ...
- SQLServer学习笔记系列4
一.写在前面的话 好多天没有记录sql学习笔记了,要坚持下去,坚信每一点的进步都是为在积蓄力量.今天看到一幅图,特此分享出来. 通过这幅图,我看到的是每人站在自己的角度看问题,感受是不一样的,就如同学 ...
- SQLServer学习笔记系列12
一.写在前面的话 这个sql学习系列,今天准备告一段落,虽然短短的十几篇文章,深刻感受到将学习的东西记录下来,是需要一种坚持! 这些东西只有反复的学习吸收,最终沉淀下来的才是属于自己的知识.也是提醒自 ...
- SQLServer学习笔记系列11
一.写在前面的话 身体是革命的本钱,这句放在嘴边常说的话,还是拿出来一起共勉,提醒一起奋斗的同僚们,保证睡眠,注意身体!偶尔加个班,也许不曾感觉到身体发出的讯号,长期晚睡真心扛不住!自己也制定计划,敦 ...
- SQLServer学习笔记系列10
一.写在前面的话 生活的路很长,还是要坚持走下去,自己选择的生活,就该让这样的生活放射精彩!我不奢求现在的积累,在将来能够收获多少,至少在以后的日子里回忆起来,我不曾放弃过,我坚持过,我不后悔!最近跟 ...
- SQLServer学习笔记系列8
一.写在前面的话 最近一直在思考一个问题,什么才能让我们不显得浮躁,真正的静下心来,用心去感受,用心去回答每个人的问题,用心去帮助别人.现实的生活,往往让我们显得精疲力尽,然后我们仔细想过没用,其实支 ...
随机推荐
- GCC 源码编译 mpc mprf gmp 不用make(否则会有lib/libgmp.so: could not read symbols: File in wrong format等错误)
错误信息: lib/libgmp.so: could not read symbols: File in wrong formatcollect2: error: ld returned 1 exit ...
- Linux学习进阶路线图
摘自:http://blog.csdn.net/zdwzzu2006/article/details/4334791 Linux 基础 Linux 基础 Linux安装专题教程 Linux中文环境 L ...
- MySql与Oracle的区别总结
在平时工作中使用这两个数据库的时候要多一些,这两数据库的使用方面存在的一些各自不同的地方,许多面试官也会问这两个的区别.所以,凭着自己的一些经验个感触,来说说这二者的区别. 使用的群众:MySql中小 ...
- LoadRunner参数化取值及连接数据库操作步骤
很多情况下,参数添加的数据不是十条二十条,也不是一百两百,对于这种大数量的数据我们可以通过数据库将数据导入: 选中要参数化的内容如下图一所示: 方法一,右键---[Replace with a new ...
- idlcpp 功能改进
最近没有续写 idlcpp 的教程, 因为忙着对它进行大幅度的修改. 一开始本是计划用idlcpp对付新写的代码,让这些新写的代码能够很容易提供给脚本使用.后来又希望能将大量现存的代码移植过来,毕竟有 ...
- SQL SERVER 内存学习系列(一)
最近帮客户解决发布订阅的问题时,突然遇到这样一个问题发布订阅中报下面的错误,另外执行alter table 操作时也会报错 : 问题很奇怪发布订阅和CLR有什么关系?memtoleave内存是个啥?回 ...
- Linux 网络编程(IO模型)
针对linux 操作系统的5类IO模型,阻塞式.非阻塞式.多路复用.信号驱动和异步IO进行整理,参考<linux网络编程>及相关网络资料. 阻塞模式 在socket编程(如下图)中调用如下 ...
- Windows Phone 8.1上的开发人员请看
1)SDK选择:如果你是在Windows Phone 8.1上做一个新App, 或者想把7.x/8.0的App移植到8.1上,请使用WinRT SDK,而不是Silverlight.当然Silverl ...
- Java虚拟机6:内存溢出和内存泄露、并行和并发、Minor GC和Full GC、Client模式和Server模式的区别
前言 之前的文章尤其是讲解GC的时候提到了很多的概念,比如内存溢出和内存泄露.并行与并发.Client模式和Server模式.Minor GC和Full GC,本文详细讲解下这些概念的区别. 内存溢出 ...
- 【Java】ThreadLocal细节分析
ThreadLocal通过中文解释就是线程本地变量,是线程的一个局部变量.根据哲学家黑格尔“的存在即合理”的说法,ThreadLocal的出现肯定是有它的意义,它的出现也是因为多线程的一个产物.Thr ...