1. SQL(高级查询)

1.1. 子查询

1.1.1. 子查询在WHERE子句中

在SELECT查询中,在WHERE查询条件中的限制条件不是一个确定的值,而是来自于另外一个查询的结果。为了给查询提供数据而首先执行的查询语句叫做子查询。

子查询:嵌入在其它SQL语句中的SELECT语句,大部分时候出现在WHERE子句中。子查询嵌入的语句称作主查询或父查询。主查询可以是SELECT语句,也可以是其它类型的语句比如DML或DDL语句。

根据返回结果的不同,子查询可分为单行子查询、多行子查询及多列子查询。

图-1子查询

例如查找和SCOTT同职位的员工:

  1. SELECT e.ename, e.job
  2. FROM emp e
  3. WHERE e.job =
  4. (SELECT job FROM emp WHERE ename = 'SCOTT');

查找薪水比整个机构平均薪水高的员工:

  1. SELECT deptno, ename, sal
  2. FROM emp e
  3. WHERE sal> (SELECT AVG(sal) FROM emp);

如果子查询返回多行,主查询中要使用多行比较操作符,包括IN、ALL、ANY。其中ALL和ANY不能单独使用,需要配合单行比较操作符>、>=、<、<=一起使用。例如查询出部门中有SALESMAN但职位不是SALESMAN的员工的信息:

  1. SELECT empno, ename, job, sal, deptno
  2. FROM emp
  3. WHERE deptno IN
  4. (SELECT deptno FROM emp WHERE job = 'SALESMAN')
  5. AND job <> 'SALESMAN';

在子查询中需要引用到主查询的字段数据,使用EXISTS关键字。EXISTS后边的子查询至少返回一行数据,则整个条件返回TRUE。如果子查询没有结果,则返回FALSE。例如列出来那些有员工的部门信息:

  1. SELECT deptno, dname FROM dept d
  2. WHERE EXISTS
  3. (SELECT * FROM emp e
  4. WHERE d.deptno = e.deptno);

1.1.2. 子查询在HAVING部分

子查询不仅可以出现在WHERE子句中,还可以出现在HAVING部分。例如查询列出最低薪水高于部门30的最低薪水的部门信息:

  1. SELECT deptno, MIN(sal) min_sal
  2. FROM emp
  3. GROUP BY deptno
  4. HAVING MIN(sal) >
  5. (SELECT MIN(sal) FROM emp WHERE deptno = 30);

1.1.3. 子查询在FROM部分

在查询语句中,FROM子句用来指定要查询的表。如果要在一个子查询的结果中继续查询,则子查询出现在FROM 子句中,这个子查询也称作行内视图或者匿名视图。这时,把子查询当作视图对待,但视图没有名字,只能在当前的SQL语句中有效。

查询出薪水比本部门平均薪水高的员工信息:

  1. SELECT e.deptno, e.ename, e.sal
  2. FROM emp e,
  3. (SELECT deptno, AVG(sal) avg_sal FROM emp GROUP BY deptno) x
  4. WHERE e.deptno = x.deptno
  5. ande.sal>x.avg_sal
  6. ORDER BY e.deptno;

1.1.4. 子查询在SELECT部分

把子查询放在SELECT子句部分,可以认为是外连接的另一种表现形式,使用更灵活:

  1. SELECT e.ename, e.sal, e.deptno,
  2. (SELECT d.deptno FROM dept d
  3. WHERE d.deptno = e.deptno) deptno
  4. FROM emp e;

1.2. 分页查询

1.2.1. ROWNUM

ROWNUM被称作伪列,用于返回标识行数据顺序的数字。例如:

  1. SELECT ROWNUM, empno, ename, sal
  2. FROM emp;

ROWNUM只能从1计数,不能从结果集中直接截取。下面的查询语句将没有结果:

  1. SELECT ROWNUM, empno, ename, sal
  2. FROM emp
  3. WHERE rownum> 3;

如果利用ROWNUM截取结果集中的部分数据,需要用到行内视图:

  1. SELECT * FROM
  2. (SELECT ROWNUMrn , e.* FROM emp e )
  3. WHERE rn BETWEEN 8 AND 10;

也就是将ROWNUM先作为行内视图的一个列,在主查询中就可以使用这个列值作为条件。

1.2.2. 使用子查询进行分页

分页策略是指每次只取一页的数据。当每次换页,取下一页的数据。在ORACLE中利用ROWNUM的功能来进行分页。

假设结果集共105条,每20条分为一页,则共6页:

Page1: 1 至 20

Page2: 21 至40

PageN: (n - 1) * pageSize + 1 至 n * pageSize

1.2.3. 分页与ORDER BY

按薪水倒序排列,取出结果集中第8到第10条的记录:

  1. SELECT * FROM
  2. (SELECT ROWNUMrn , t.* FROM
  3. (SELECT empno,ename,sal FROM emp
  4. ORDER BY sal DESC) t
  5. )
  6. WHERE rn BETWEEN 8 AND 10;

根据要查看的页数,计算起点值((n - 1) * pageSize + 1)和终点值(n * pageSize),替换掉BETWEEN和AND的参数,即得到当前页的记录。

1.3. DECODE函数

1.3.1. DECODE函数基本语法

DECODE函数的语法如下:

DECODE (expr, search1, result1[, search2, result2…][, default])

它用于比较参数expr的值,如果匹配到哪一个search条件,就返回对应的result结果,可以有多组search和result的对应关系,如果任何一个search条件都没有匹配到,则返回最后default的值。default参数是可选的,如果没有提供default参数值,当没有匹配到时,将返回NULL。

查询职员表,根据职员的职位计算奖励金额,当职位分别是’MANAGER’、’ANALYST’、’SALESMAN’时,奖励金额分别是薪水的1.2倍、1.1倍、1.05倍,如果不是这三个职位,则奖励金额取薪水值:

  1. SELECT ename, job, sal,
  2. DECODE(job, 'MANAGER', sal * 1.2,
  3. 'ANALYST', sal * 1.1,
  4. 'SALESMAN', sal * 1.05,
  5. sal
  6. ) bonus
  7. FROM emp;

和DECODE函数功能相似的有CASE语句,实现类似于if-else的操作。

  1. SELECT ename, job, sal,
  2. CASE job WHEN 'MANAGER' THEN sal * 1.2
  3. WHEN 'ANALYST' THEN sal * 1.1
  4. WHEN 'SALESMAN' THEN sal * 1.05
  5. ELSE sal END
  6. bonus
  7. FROM emp;

1.3.2. DECODE函数在分组查询中的应用

DECODE函数可以按字段内容分组,例如:计算职位的人数,analyst/manager职位属于vip,其余是普通员工operation,这种功能无法用GROUP BY简单实现。用decode的实现方式:

  1. SELECT DECODE(job, 'ANALYST', 'VIP',
  2. 'MANAGER', 'VIP',
  3. 'OPERATION') job,
  4. COUNT(1) job_cnt
  5. FROM emp
  6. GROUP BY DECODE(job, 'ANALYST', 'VIP', 'MANAGER', 'VIP', 'OPERATION');

图-2DECODE函数的运行结果

DECODE函数也可以按字段内容排序,例如:Dept表中按”研发部”、“市场部”、“销售部”排序,用普通的select语句,无法按照字面数据排序:

  1. SELECT deptno, dname, loc
  2. FROM dept
  3. ORDER BY
  4. DECODE(dname, '研发部',1,'市场部',2,'销售部',3), loc;

1.4. 排序函数

1.4.1. ROW_NUMBER

ROW_NUMBER语法如下:

  1. ROW_NUMBER() OVER(
  2. PARTITION BY col1 ORDER BY col2)

表示根据col1分组,在分组内部根据col2排序。此函数计算的值就表示每组内部排序后的顺序编号,组内连续且唯一。

ROWNUM是伪列, ROW_NUMBER功能更强,可以直接从结果集中取出子集。

场景:按照部门编码分组显示,每组内按职员编码排序,并赋予组内编码

  1. SELECT deptno, ename, empno,
  2. ROW_NUMBER()
  3. OVER (PARTITION BY deptno ORDER BY empno) AS emp_id
  4. FROM emp;

1.4.2. RANK

RANK函数的语法如下:

  1. RANK() OVER(
  2. PARTITION BY col1 ORDER BY col2)

表示根据col1分组,在分组内部根据col2给予等级标识,即排名,相同的数据返回相同排名。特点是跳跃排序,如果有相同数据,则排名相同,比如并列第二,则两行数据都标记为2,但下一位将是第四名。

和ROW_NUMBER的区别是有结果有重复值,而ROW_NUMBER没有。

场景:按照部门编码分组,同组内按薪水倒序排序,相同薪水则按奖金数正序排序,并给予组内等级,用Rank_ID表示

  1. SELECT deptno, ename, sal, comm,
  2. RANK() OVER (PARTITION BY deptno
  3. ORDER BY sal DESC, comm) "Rank_ID"
  4. FROM emp;

1.4.3. DENSE_RANK

DENSE_RANK函数的语法如下:

  1. DENSE_RANK() OVER(
  2. PARTITION BY col1 ORDER BY col2)

表示根据col1分组,在分组内部根据col2给予等级标识,即排名,相同的数据返回相同排名。特点是连续排序,如果有并列第二,下一个排序将是三,这一点是和RANK的不同,RANK是跳跃排序。

场景:关联emp和dept表,按照部门编码分组,每组内按照员工薪水排序,列出员工的部门名字、姓名和薪水:

  1. SELECT d.dname, e.ename, e.sal,
  2. DENSE_RANK()
  3. OVER (PARTITION BY e.deptno ORDER BY e.sal)
  4. AS drank
  5. FROM emp e join dept d
  6. one.deptno = d.deptno;

1.5. 高级分组函数

1.5.1. ROLLUP

ROLLUP、CUBE 和 GROUPING SETS 运算符是 GROUP BY 子句的扩展,可以生成与使用 UNION ALL 来组合单个分组查询时相同的结果集,用来简化和高效的实现统计查询。语法形式如下:

  • GROUP BY ROLLUP(a, b, c)
  • GROUP BY CUBE(a, b, c)
  • GROUP BY GROUPING SETS ( (a), (b))

假设有表test,有a、b、c、d四个列。

  1. SELECT a,b,c,SUM(d) FROM test GROUP BY ROLLUP(a,b,c);

等价于:

  1. SELECT a,b,c,SUM(d) FROM test GROUP BY a,b,c
  2. UNION ALL
  3. SELECT a,b,null,SUM(d) FROM test GROUP BY a,b
  4. UNION ALL
  5. SELECT a,null,null,SUM(d) FROM test GROUP BY a
  6. UNION ALL
  7. SELECT null,null,null,sum(d) FROM test;

对ROLLUP的列从右到左以一次少一列的方式进行分组直到所有列都去掉后的分组(也就是全表分组)。对于n个参数的ROLLUP,有n+1次分组。

表-1 数据样例表

准备数据:

  1. SQL>DROP TABLE sales_tab;
  2. SQL>CREATE TABLE sales_tab (
  3. year_id NUMBER NOT NULL,
  4. month_id NUMBER NOT NULL,
  5. day_id NUMBER NOT NULL,
  6. sales_value NUMBER(10,2) NOT NULL
  7. );
  8. SQL>INSERT INTO sales_tab
  9. SELECT TRUNC(DBMS_RANDOM.value(low => 2010, high => 2012)) AS year_id,
  10. TRUNC(DBMS_RANDOM.value(low => 1, high => 13)) AS month_id,
  11. TRUNC(DBMS_RANDOM.value(low => 1, high => 32)) AS day_id,
  12. ROUND(DBMS_RANDOM.value(low => 1, high => 100), 2) AS sales_value
  13. FROM dual
  14. CONNECT BY level <= 1000;
  15. SQL>COMMIT;

复习组函数的用法:

  1. SQL>SELECT SUM(sales_value) AS sales_value FROM sales_tab;
  2. SQL>SELECT year_id, COUNT(*) AS num_rows,
  3. SUM(sales_value) AS sales_value
  4. FROM sales_tab
  5. GROUP BY year_id
  6. ORDER BY year_id;
  7. SQL>SELECT year_id, month_id,
  8. COUNT(*) AS num_rows,
  9. SUM(sales_value) AS sales_value
  10. FROM sales_tab
  11. GROUP BY year_id, month_id
  12. ORDER BY year_id, month_id;

图-3 在测试表中使用组函数的运行结果

图-4 在测试表中使用组函数的运行结果

ROLLUP函数的用法:

  1. SELECT year_id, month_id,
  2. SUM(sales_value) AS sales_value
  3. FROM sales_tab
  4. GROUP BY
  5. ROLLUP (year_id, month_id)
  6. ORDER BY year_id, month_id;

图-5 在测试表中使用ROLLUP函数的运行结果

  1. SELECT year_id, month_id, day_id, SUM(sales_value) AS sales_value
  2. FROM sales_tab
  3. GROUP BY ROLLUP (year_id, month_id, day_id)
  4. ORDER BY year_id, month_id, day_id;

图-6 在测试表中使用ROLLUP函数的运行结果

1.5.2. CUBE

CUBE函数的语法形式:

  1. GROUP BY CUBE(a, b, c)

对cube的每个参数,都可以理解为取值为参与分组和不参与分组两个值的一个维度,所有维度取值组合的集合就是分组后的集合。对于n个参数的cube,有2^n次分组。

如果GROUP BY CUBE(a,b,c),首先对(a,b,c)进行GROUP BY,然后依次是(a,b),(a,c),(a),(b,c),(b),(c),最后对全表进行GROUP BY操作,所以一共是2^3=8次分组。

  1. SELECT a,b,c,SUM(d) FROM test GROUP BY CUBE(a,b,c);

等价于:

  1. SELECT a,b,c,SUM(d) FROM test GROUP BY a,b,c
  2. UNION ALL
  3. SELECT a,b,NULL,SUM(d) FROM test GROUP BY a,b
  4. UNION ALL
  5. SELECT a,NULL,c,SUM(d) FROM test GROUP BY a,c
  6. UNION ALL
  7. SELECT a,NULL,NULL,SUM(d) FROM test GROUP BY a
  8. UNION ALL
  9. SELECT NULL,b,c,SUM(d) FROM test GROUP BY b,c
  10. UNION ALL
  11. SELECT NULL,b,NULL,SUM(d) FROM test GROUP BY b
  12. UNION ALL
  13. SELECT NULL,NULL,c,SUM(d) FROM test GROUP BY c
  14. UNION ALL
  15. SELECT NULL,NULL,NULL,SUM(d) FROM test ;

等价于只是方便理解,其内部运行机制并不相同,其效率远高于UNION ALL。

在sales_value表中使用cube函数:

  1. SELECT year_id, month_id,
  2. SUM(sales_value) AS sales_value
  3. FROM sales_tab
  4. GROUP BY CUBE (year_id, month_id)
  5. ORDER BY year_id, month_id;

图-7在测试表中使用CUBE函数的运行结果

  1. SELECT year_id, month_id, day_id,
  2. SUM(sales_value) AS sales_value
  3. FROM sales_tab
  4. GROUP BY CUBE (year_id, month_id, day_id)
  5. ORDER BY year_id, month_id, day_id;

1.5.3. GROUPING SETS

GROUPING SETS 运算符可以生成与使用单个 GROUP BY、ROLLUP 或 CUBE 运算符所生成的结果集相同的结果集,但是使用更灵活。

如果不需要获得由完备的 ROLLUP 或 CUBE 运算符生成的全部分组,则可以使用 GROUPING SETS 仅指定所需的分组。GROUPING SETS 列表可以包含重复的分组。

GROUPING SETS示例:

  1. SELECT year_id, month_id, SUM(sales_value)
  2. FROM sales_tab
  3. GROUP BY CUBE (year_id,month_id)
  4. order by 1, 2;
  5. SELECT year_id, month_id, SUM(sales_value)
  6. FROM sales_tab
  7. GROUP BY GROUPING SETS ( (year_id), (month_id))
  8. order by 1, 2

其中分组方式示例如下:

  • 使用GROUP BY GROUPING SETS(a,b,c),则对(a),(b),(c)进行GROUP BY
  • 使用GROUP BY GROUPING SETS((a,b),c), 则对(a,b),(c)进行GROUP BY
  • GROUPING BY GROUPING SET(a,a) , 则对(a)进行2次GROUP BY, GROUPING SETS的参数允许重复

1.6. 集合操作

1.6.1. UNION、UNION ALL

图-8 集合操作

为了合并多个SELECT语句的结果,可以使用集合操作符,实现集合的并、交、差。

集合操作符包括UNION、UNION ALL、INTERSECT和MINUS。多条作集合操作的SELECT语句的列的个数和数据类型必须匹配。

ORDER BY子句只能放在最后的一个查询语句中。

集合操作的语法如下:

  1. SELECT statement1
  2. [UNION | UNION ALL | INTERSECT | MINUS]
  3. SELECT statement2;

UNION和UNION ALL用来获取两个或两个以上结果集的并集:

  • UNION操作符会自动去掉合并后的重复记录。
  • UNION ALL返回两个结果集中的所有行,包括重复的行。
  • UNION操作符对查询结果排序,UNION ALL不排序。

例如,合并职位是’MANAGER’的员工和薪水大于2500的员工集合,查看两种方式的结果差别:

  1. --Union
  2. SELECT ename, job, sal FROM emp
  3. WHERE job = 'MANAGER'
  4. SELECT ename, job, sal FROM emp
  5. WHERE sal> 2500;
  6. --Union all
  7. SELECT ename, job, sal FROM emp
  8. WHERE job = 'MANAGER'
  9. SELECT ename, job, sal FROM emp
  10. WHERE sal> 2500;

1.6.2. INTERSECT

INTERSECT函数获得两个结果集的交集,只有同时存在于两个结果集中的数据,才被显示输出。使用INTERSECT操作符后的结果集会以第一列的数据作升序排列。

例如:显示职位是’MANAGER’的员工和薪水大于2500的员工的交集:

 
  1. SELECT ename, job, sal FROM emp
  2. WHERE job = 'MANAGER'
  3. INTERSECT
  4. SELECT ename, job, sal FROM emp
  5. WHERE sal> 2500;

1.6.3. MINUS

MINUS函数获取两个结果集的差集。只有在第一个结果集中存在,在第二个结果集中不存在的数据,才能够被显示出来。也就是结果集一减去结果集二的结果。

例如:列出职位是MANAGER但薪水低于2500的员工记录:

 
  1. SELECT ename, job, sal FROM emp
  2. WHERE job = 'MANAGER'
  3. MINUS
  4. SELECT ename, job, sal FROM emp
  5. WHERE sal> 2500;

oracle 学习笔记(四)的更多相关文章

  1. Oracle学习笔记四

    一.PL/SQL编程 游标(光标Cursor) 为什么使用游标 在写java程序中有集合的概念,那么在pl/sq中也会用到多条记录,这时候我们就要用到游标,游标可以存储查询返回的多条数据. 语法: C ...

  2. Oracle学习笔记四 SQL命令(二):SQL操作语言类别

    SQL分为下列语言类别 1.数据定义语言(DDL) Create.Alter.Drop 2.数据操纵语言(DML) Insert.Select.Delete.Update 3.事务控制语言(TCL) ...

  3. Oracle学习笔记(四)

    六.约束 1.约束的作用 定义规则:什么数据可以输入,什么不可以 确保完整性:数据的精确性和可靠性 2.Oracle五个重要的约束: 非空约束.主键约束.外键约束.检查约束.唯一约束. (1)非空约束 ...

  4. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  5. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

  6. IOS学习笔记(四)之UITextField和UITextView控件学习

    IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...

  7. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  8. Learning ROS for Robotics Programming Second Edition学习笔记(四) indigo devices

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

  9. oracle学习笔记第一天

    oracle学习笔记第一天 --oracle学习的第一天 --一.几个基础的关键字   1.select select (挑选) 挑选出显示的--列--(可以多列,用“,”隔开,*表示所有列),为一条 ...

随机推荐

  1. [LeetCode] Implement Trie (Prefix Tree) 实现字典树(前缀树)

    Implement a trie with insert, search, and startsWith methods. Note:You may assume that all inputs ar ...

  2. [LeetCode] Distinct Subsequences 不同的子序列

    Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence ...

  3. [LeetCode] Jump Game II 跳跃游戏之二

    Given an array of non-negative integers, you are initially positioned at the first index of the arra ...

  4. .NET Core中ADO.NET SqlClient的使用与常见问题

    一.简介 在很多要求性能的项目中,我们都要使用传统的ADO.NET的方式来完成我们日常的工作:目前有一些网友问有关于.NET Core操作SQL Server的问题在本文中解答一下. 本文旨在指出,在 ...

  5. redis配置文件详解

    基于redis2.4版本的配置文件. # 注意单位问题:当需要设置内存大小的时候,可以使用类似1k.5GB.4M这样的常见格式:## 1k => 1000 bytes# 1kb => 10 ...

  6. 使用UITableView的分组样式

    分组样式顾名思义是对TableView中的数据行进行分组处理,每个分组都有一个header和footer. TableView中header的英文文本是大写的,footer的英文文本是小写的.如下图浅 ...

  7. sql server如何分组编号

    我们在生产实践中经常会有这样的需求:分组编号. 如下有一个城市区域表region: 我们需要对上表region按city分组,对region进行排序,得到如下结果: 具体sql如下: select c ...

  8. mysqlbinlog抽取某个表的信息

    http://blog.163.com/zhao_jw/blog/static/180587366201110293630648/

  9. blog (后续更新)

    设计Model(设计数据库) from django.db import models # Create your models here. class BlogsPost(models.Model) ...

  10. C#面向对象设计模式纵横谈——2.Singleton 单件(创建型模式)

    一:模式分类 从目的来看: 创建型(Creational)模式:负责对象创建. 结构型(Structural)模式:处理类与对象间的组合. 行为型(Behavioral)模式:类与对象交互中的职责分配 ...