Oracle中的多表查询(笛卡尔积原理)

本次预计讲解的知识点数据库

一、 多表查询的操做、限制、笛卡尔积的问题;函数

二、 统计函数及分组统计的操做;post

三、 子查询的操做,而且结合限定查询、数据排序、多表查询、统计查询一块儿完成各个复杂查询的操做;性能

1、多表查询的基本概念学习

在以前所使用的查询操做之中,都是从一张表之中查询出所须要的内容,那么若是如今一个查询语句须要显示多张表的数据,则就必须应用到多表查询的操做,而多表查询的语法以下:测试

复制代码
SELECT [DISTINCT] * | 字段 [别名] [,字段 [别名] ,…] FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(S)] [ORDER BY 排序字段 [ASC|DESC] [,排序字段 [ASC|DESC] ,…]];
复制代码

可是若是要进行多表查询以前,首先必须先查询出几个数据 —— 雇员表和部门表中的数据量,这个操做能够经过COUNT()函数完成。大数据

范例:查询emp表中的数据量 ——返回了14条记录spa

SELECT COUNT(*) FROM emp;

范例:查询dept表中的数据量 ——4条记录code

SELECT COUNT(*) FROM dept;

额外补充一点:何为经验?blog

在往后的开发之中,不少人都确定要接触到许多新的数据库和数据表,那么在这种时候有两种作法:

  • 作法一:新人作法,上来直接输入如下的命令:
SELECT * FROM 表名称;

若是此时数据量较大的话,一上没法浏览数据,二有可能形成系统的死机;

  • 作法二:老人作法,先看一下有多少条记录:
SELECT COUNT(*) FROM 表名称;

若是此时数据量较小,则能够查询所有数据,若是数据量较大则不能直接使用SELECT查询。

如今肯定好了emp和dept表中的记录以后,下面完成一个基本的多表查询:

SELECT * FROM emp, dept;

可是如今查询以后发现一共产生了56条记录 = 雇员表的14条记录 * 部门表的4条记录,之因此会形成这样的问题,主要都是由数据库的查询机制所决定的,例如,以下图所示。

本问题在数据库的操做之中被称为笛卡尔积,就表示多张表的数据乘积的意思,可是这种查询结果确定不是用户所但愿的,那么该如何去掉笛卡尔积呢?

最简单的方式是采用关联字段的形式,emp表和dept表之间如今存在了deptno的关联字段,因此如今能够从这个字段上的判断开始。

当在查询之中,不一样的表中有了相同字段名称的时候,访问这些字段必须加上表名称,即“表.字段”。

SELECT * FROM emp WHERE emp.deptno=dept.deptno;

       此时的查询结果之中已经消除了笛卡尔积,可是如今只属于显示上的消除,而真正笛卡尔积如今依然存在,由于数据库的操做机制就属于逐行的进行数据的判断,那么若是按照这个思路理解的话,如今假设两张表的数据量都很大的话,那么使用这种多表查询的性能。

       这句话的意思是虽然消除了笛卡尔积,可是并无避免笛卡尔积的产生过程。其实消除笛卡尔积的意义就在于:挖掘有用数据,过滤掉无用数据

范例:以sh用户的大数据表为例

SELECT COUNT(*) FROM sales, costs WHERE sales.prod_id=costs.prod_id;

这两张表即使消除了笛卡尔积的显示,可是自己也会有笛卡尔积的问题,因此最终的查询结果会很慢显示,甚至是不显示,因此经过这道程序必定要记住,多表查询的性能是不好的,固然,性能差是有一个前提的:数据量大。

可是以上的程序也存在一个问题,在以前访问表中字段的时候使用的是“表.字段”名称,那么若是说如今假设表名称很长,例如“yinhexi_diqiu_yazhou_zhongguo_beijing_xicheng_ren”,因此通常在进行多表查询的时候每每都会为表起一个别名,经过别名.字段的方式进行查询。

SELECT * FROM emp e, dept d WHERE e.deptno=d.deptno;

范例:查询出每一位雇员的编号、姓名、职位、部门名称、位置

一、肯定所须要的数据表:

  • emp表:能够查询出雇员的编号、姓名、职位;
  • dept表:能够查询出部门名称和位置;

二、肯定表的关联字段:emp.deptno=dept.deptno;

第一步:查询出每一位雇员的编号、姓名、职位

SELECT e.empno, e.ename, e.job FROM emp e;

第二步:为查询中引入部门表,同时须要增长一个消除笛卡尔积的条件

SELECT e.empno, e.ename, e.job, d.dname, d.loc FROM emp e, dept, d WHERE e.deptno=d.deptno;

之后遇到问题,发现没有解决问题的思路,就按照上面的步骤进行,慢慢的分析解决,由于多表查询不可能一次性所有写出,须要逐步分析的。

范例:要求查询出每一位雇员的姓名、职位、领导的姓名。

如今确定要准备出两个emp表,因此这个时候能够称为emp表的自身关联,按照以前的分析以下:

一、肯定所须要的数据表:

  • emp表(雇员):取得雇员的姓名、职位、领导编号;
  • emp表(领导):取得雇员的姓名(领导的姓名);

二、肯定关联字段:emp.mgr=memp.empno(雇员的领导编号 = 领导(雇员)的雇员编号)

第一步:查询每一位雇员的姓名、职位

SELECT e.ename, e.job FROM emp e;

第二步:查询领导信息,加入自身关联

SELECT e.ename, e.job, m.ename FROM emp e, emp m WHERE e.mgr=m.empno;

此时的查询结果之中缺乏了“KING”的记录,由于KING没有领导,而要想解决这个问题,就须要等待以后讲解的左、右链接的问题了。

范例:查询出每一个雇员的编号、姓名、基本工资、职位、领导的姓名、部门名称及位置。

一、肯定所须要的数据表:

  • emp表:每一个雇员的编号、姓名、基本工资、职位;
  • emp表(领导):领导的姓名;
  • dept表:部门的名称及位置。

二、肯定已知的关联字段:

  • 雇员和部门:emp.deptno=dept.deptno;
  • 雇员和领导:emp.mgr=memp.empno;

第一步:查询出每一个雇员的编号、姓名、基本工资、职位

SELECT empno, ename, sal, job FROM emp;

第二步:加入领导的信息,引入自身关联,同时增长消除笛卡尔积的条件

SELECT e.empno, e.ename, e.sal, e.job, m.ename FROM emp e, emp m WHERE e.mgr=m.empno;

第三步:加入部门的信息,引入dept表,既然有新的表进来,则须要继续增长消除笛卡尔积的条件

SELECT e.empno, e.ename, e.sal, e.job, m.ename, d.dname, d.loc FROM emp e, emp m, dept d WHERE e.mgr=m.empno AND e.deptno=d.deptno;

因此之后的全部相似的问题最好都可以按照如上的方式编写,造成本身的思路。

思考题:如今要求查询出每个雇员的编号、姓名、工资、部门名称、工资所在公司的工资等级。

一、肯定所须要的数据表:

  • emp表:雇员的编号、姓名、工资;
  • dept表:部门名称;
  • salgrade表:工资等级;

二、肯定已知的关联字段:

  • 雇员和部门:emp.deptno=dept.deptno;
  • 雇员和工资等级:emp.sal BETWEEN salgrade.losal AND salgrade.hisal;

第一步:查询出每个雇员的编号、姓名、工资

SELECT e.empno, e.ename, e.sal FROM emp e;

第二步:引入部门表,同时增长一个消除笛卡尔积的条件

SELECT e.empno, e.ename, e.sal, d.dname FROM emp e, dept d WHERE e.deptno=d.deptno;

第三步:引入工资等级表,继续增长消除笛卡尔积的条件

SELECT e.empno, e.ename, e.sal, d.dname, s.grade FROM emp e, dept d, salgrade s WHERE e.deptno=d.deptno AND e.sal BETWEEN s.losal AND s.hisal;

若是如今有以下的进一步要求:将每个工资等级替换成具体的文字信息,例如:

1 替换成 第五等工资、2 替换成 第四等工资、3 替换成 第三等工资,依次类推 --> 依靠DECODE()实现

SELECT e.empno, e.ename, e.sal, d.dname DECODE(s.grade,1,’第五等工资’,2,’第四等工资’,3,’第三等工资’,4,’第二等工资’,5,’第一等工资’) gradeinfo FROM emp e, dept d, salgrade s WHERE e.deptno=d.deptno AND e.sal BETWEEN s.losal AND s.hisal;

之后的全部的题目都按照相似的方式分析,只要是表关联,确定有关联字段,用于消除笛卡尔积,只是这种关联字段须要根据状况使用不一样的限定符号。

2、左、右链接

关于左、右链接指的是查询判断条件的参考方向,例如,下面有以下查询:

SELECT * FROM emp e, dept d WHERE e.deptno=d.deptno;
复制代码
     EMPNO ENAME      JOB              MGR HIREDATE              SAL       COMM     DEPTNO     DEPTNO DNAME          LOC
---------- ---------- --------- ---------- -------------- ---------- ---------- ---------- ---------
      7782 CLARK      MANAGER         7839 09-6月 -81           2450                    10         10 ACCOUNTING     NEW YORK
      7839 KING       PRESIDENT            17-11月-81           5000                    10         10 ACCOUNTING     NEW YORK
      7934 MILLER     CLERK           7782 23-1月 -82           1300                    10         10 ACCOUNTING     NEW YORK
      7369 SMITH      CLERK           7902 17-12月-80            800                    20         20 RESEARCH       DALLAS
      7876 ADAMS      CLERK           7788 23-5月 -87           1100                    20         20 RESEARCH       DALLAS
      7902 FORD       ANALYST         7566 03-12月-81           3000                    20         20 RESEARCH       DALLAS
      7788 SCOTT      ANALYST         7566 19-4月 -87           3000                    20         20 RESEARCH       DALLAS
      7566 JONES      MANAGER         7839 02-4月 -81           2975                    20         20 RESEARCH       DALLAS
      7499 ALLEN      SALESMAN        7698 20-2月 -81           1600        300         30         30 SALES          CHICAGO
      7698 BLAKE      MANAGER         7839 01-5月 -81           2850                    30         30 SALES          CHICAGO
      7654 MARTIN     SALESMAN        7698 28-9月 -81           1250       1400         30         30 SALES          CHICAGO
      7900 JAMES      CLERK           7698 03-12月-81            950                    30         30 SALES          CHICAGO
      7844 TURNER     SALESMAN        7698 08-9月 -81           1500          0         30         30 SALES          CHICAGO
      7521 WARD       SALESMAN        7698 22-2月 -81           1250        500         30         30 SALES          CHICAGO

已选择14行。
复制代码

部门一共有四个,可是如今只返回了三个部门的信息,缺乏40部门,由于在雇员表之中没有一条记录是属于40部门的,因此如今不会显示40部门的信息,即:如今的查询以emp表为参考,那么若是说如今非要显示40部门呢?就必须改变这种参考的方向,就须要用使用左、右链接。

SELECT * FROM emp e, dept d WHERE e.deptno(+)=d.deptno;
复制代码
 EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO DEPTNO DNAME LOC ---------- ---------- --------- ---------- -------------- ---------- ---------- ---------- --------- 7782 CLARK MANAGER 7839 09-6月 -81 2450 10 10 ACCOUNTING NEW YORK 7839 KING PRESIDENT 17-11月-81 5000 10 10 ACCOUNTING NEW YORK 7934 MILLER CLERK 7782 23-1月 -82 1300 10 10 ACCOUNTING NEW YORK 7369 SMITH CLERK 7902 17-12月-80 800 20 20 RESEARCH DALLAS 7876 ADAMS CLERK 7788 23-5月 -87 1100 20 20 RESEARCH DALLAS 7902 FORD ANALYST 7566 03-12月-81 3000 20 20 RESEARCH DALLAS 7788 SCOTT ANALYST 7566 19-4月 -87 3000 20 20 RESEARCH DALLAS 7566 JONES MANAGER 7839 02-4月 -81 2975 20 20 RESEARCH DALLAS 7499 ALLEN SALESMAN 7698 20-2月 -81 1600 300 30 30 SALES CHICAGO 7698 BLAKE MANAGER 7839 01-5月 -81 2850 30 30 SALES CHICAGO 7654 MARTIN SALESMAN 7698 28-9月 -81 1250 1400 30 30 SALES CHICAGO 7900 JAMES CLERK 7698 03-12月-81 950 30 30 SALES CHICAGO 7844 TURNER SALESMAN 7698 08-9月 -81 1500 0 30 30 SALES CHICAGO 7521 WARD SALESMAN 7698 22-2月 -81 1250 500 30 30 SALES CHICAGO 40 OPERATIONS BOSTON 已选择15行。
复制代码

如今发现40部门出现了,因此发现参考的方向已经改变了,而“(+)”就用于左、右链接的更改,这种符号有如下两种使用状况:

  • (+)=:放在了等号的左边,表示的是右链接;
  • =(+):放在了等号的右边,表示的是左链接;

可是不用去刻意的区分是左仍是右,只是根据查询结果而定,若是发现有些须要的数据没有显示出来,就使用此符号更改链接方向。

范例:查询每一个雇员的姓名和领导的姓名

SELECT e.ename, e.job, m.ename FROM emp e, emp m WHERE e.mgr=m.empno(+);

但是这种符号是Oracle数据库本身所独有的,其余数据库不能使用。

3、SQL:1999语法

除了以上的表链接操做以外,在SQL语法之中,也提供了另一套用于表链接的操做SQL,格式以下:

复制代码
SELECT table1.column,table2.column FROM table1 [CROSS JOIN table2]| [NATURAL JOIN table2]| [JOIN table2 USING(column_name)]| [JOIN table2 ON(table1.column_name=table2.column_name)]| [LEFT|RIGHT|FULL OUTER JOIN table2 ON(table1.column_name=table2.column_name)];
复制代码

以上其实是属于多个语法的联合,下面分块说明语法的使用。

一、交叉链接(CROSS JOIN):用于产生笛卡尔积

SELECT * FROM emp CROSS JOIN dept;

笛卡尔积自己并非属于无用的内容,在某些状况下仍是须要使用的。

二、天然链接(NATURAL JOIN):自动找到匹配的关联字段,消除掉笛卡尔积

SELECT * FROM emp NATURAL JOIN dept;

可是并非全部的字段都是关联字段,设置关联字段须要经过约束指定;

三、JOIN…USING子句:用户本身指定一个消除笛卡尔积的关联字段

SELECT * FROM emp JOIN dept USING(deptno);

四、JOIN…ON子句:用户本身指定一个能够消除笛卡尔积的关联条件

SELECT * FROM emp JOIN dept ON(emp.deptno=dept.deptno);

五、链接方向的改变:

  • 左(外)链接:LEFT OUTER JOIN…ON;
  • 右(外)链接:RIGHT OUTER JOIN…ON;
  • 全(外)链接:FULL OUTER JOIN…ON; --> 把两张表中没有的数据都显示
SELECT * FROM emp RIGHT OUTER JOIN dept ON(emp.deptno=dept.deptno);

在Oracle以外的数据库都使用以上的SQL:1999语法操做,因此这个语法还必须会一些(若是你一直使用的都是Oracle就能够不会了)。

再次强调:多表查询的性能确定不高,并且性能必定要在大数据量的状况下才可以发现。

4、统计函数及分组查询

一、统计函数

在以前学习过一个COUNT()函数,此函数的功能能够统计出表中的数据量,实际上这个就是一个统计函数,而经常使用的统计函数有以下几个:

  • COUNT():查询表中的数据记录;
  • AVG():求出平均值;
  • SUM():求和;
  • MAX():求出最大值;
  • MIN():求出最小值;

范例:测试COUNT()、AVG()、SUM()

统计出公司的全部雇员,每月支付的平均工资及总工资。

SELECT MAX(sal),MIN(sal) FROM emp;

注意点:关于COUNT()函数

COUNT()函数的主要功能是进行数据的统计,可是在进行数据统计的时候,若是一张表中没有统计记录,COUNT()也会返回数据,只是这个数据是“0”。

SELECT COUNT(ename) FROM BONUS;

若是使用的是其余函数,则有可能返回null,可是COUNT()永远都会返回一个具体的数字,这一点之后在开发之中都会使用到。

二、分组查询

在讲解分组操做以前首先必须先明确一点,什么状况下可能分组,例如:

  • 公司的全部雇员,要求男性一组,女性一组,以后能够统计男性和女性的数量;
  • 按照年龄分组,18岁以上的分一组,18岁如下的分一组;
  • 按照地区分组:北京人一组,上海人一组,四川一组;

这些信息若是都保存了数据库之中,确定在数据的某一列上会存在重复的内容,例如:按照性别分组的时候,性别确定有重复(男和女),按照年龄分组(有一个范围的重复),按照地区分组有一个地区的信息重复。

因此分组之中有一个不成文的规定:当数据重复的时候分组才有意义,由于一我的也能够一组(没什么意义)。

复制代码
SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]];
复制代码

范例:按照部门编号分组,求出每一个部门的人数,平均工资

SELECT deptno, COUNT(empno), AVG(sal) FROM emp GROUP BY deptno;

范例:按照职位分组,求出每一个职位的最高和最低工资

SELECT job, MAX(sal), MIN(sal) FROM emp GROUP BY job;

可是如今一旦分组以后,实际上对于语法上就会出现了新的限制,对于分组有如下要求:

  • 分组函数能够在没有分组的时候单独用使用,但是却不能出现其余的查询字段;

分组函数单独使用:

SELECT COUNT(empno) FROM emp;

错误的使用,出现了其余字段:

SELECT empno,COUNT(empno) FROM emp;

  • 若是如今要进行分组的话,则SELECT子句以后,只能出现分组的字段和统计函数,其余的字段不能出现:

正确作法:

SELECT job,COUNT(empno),AVG(sal)

FROM emp

GROUP BY job;

错误的作法:

SELECT deptno,job,COUNT(empno),AVG(sal)

FROM emp

GROUP BY job;

  • 分组函数容许嵌套,可是嵌套以后的分组函数的查询之中不能再出现任何的其余字段。

范例:按照职位分组,统计平均工资最高的工资

一、先统计出各个职位的平均工资

SELECT job,AVG(sal) FROM emp GROUP BY job;

二、平均工资最高的工资

SELECT MAX(AVG(sal)) FROM emp GROUP BY job;

范例:查询出每一个部门的名称、部门的人数、平均工资

一、肯定所须要的数据表:

  • dept表:每一个部门的名称;
  • emp表:统计出部门的人数、平均工资;

二、肯定已知的关联字段:emp.deptno=dept.deptno;

范例:将dept表和emp表的数据关联

SELECT d.dname,e.empno,e.sal FROM dept d, emp e WHERE d.deptno=e.deptno;
复制代码
DNAME EMPNO SAL -------------- ---------- ---------- ACCOUNTING 7782 2450 ACCOUNTING 7839 5000 ACCOUNTING 7934 1300 RESEARCH 7369 800 RESEARCH 7876 1100 RESEARCH 7902 3000 RESEARCH 7788 3000 RESEARCH 7566 2975 SALES 7499 1600 SALES 7698 2850 SALES 7654 1250 SALES 7900 950 SALES 7844 1500 SALES 7521 1250 已选择14行。
复制代码

此时的查询结果中,能够发如今dname字段上显示出了重复的数据,按照以前对分组的理解,只要数据重复了,那么就有可能进行分组的查询操做,可是此时与以前的分组不太同样,以前的分组是针对于一张实体表进行的分组(emp、dept都属于实体表),可是对于以上的数据是经过查询结果显示的,因此是一张临时的虚拟表,可是无论是不是实体表仍是虚拟表,只要是有重复,那么就直接进行分组。

SELECT d.dname,COUNT(e.empno),AVG(e.sal) FROM dept d, emp e WHERE d.deptno=e.deptno GROUP BY d.dname;

可是这个分组并不合适,由于部门一共有四个部门(由于如今已经引入了dept表,dept表存在了四个部门的信息),因此应该经过左右链接改变查询的结果。

SELECT d.dname,COUNT(e.empno),NVL(AVG(e.sal),0) FROM dept d, emp e WHERE d.deptno=e.deptno(+) GROUP BY d.dname;

以前的全部操做都是针对于单个字段分组的,而实际上分组操做之中也能够实现多字段分组。

范例:要求显示每一个部门的编号、名称、位置、部门的人数、平均工资

一、肯定所须要的数据表:

  • dept表:每一个部门的名称;
  • emp表:统计出部门的人数、平均工资;

二、肯定已知的关联字段:emp.deptno=dept.deptno;

范例:将emp表和dept表关联查询

SELECT d.deptno,d.dname,d.loc,e.empno,e.sal FROM dept d,emp e WHERE d.deptno=e.deptno(+);
复制代码
 DEPTNO DNAME LOC EMPNO SAL ---------- -------------- ------------- ---------- ---------- 10 ACCOUNTING NEW YORK 7782 2450 10 ACCOUNTING NEW YORK 7839 5000 10 ACCOUNTING NEW YORK 7934 1300 20 RESEARCH DALLAS 7369 800 20 RESEARCH DALLAS 7876 1100 20 RESEARCH DALLAS 7902 3000 20 RESEARCH DALLAS 7788 3000 20 RESEARCH DALLAS 7566 2975 30 SALES CHICAGO 7499 1600 30 SALES CHICAGO 7698 2850 30 SALES CHICAGO 7654 1250 30 SALES CHICAGO 7900 950 30 SALES CHICAGO 7844 1500 30 SALES CHICAGO 7521 1250 40 OPERATIONS BOSTON 已选择15行。
复制代码

此时存在了重复数据,并且这个重复的数据平均在了三列上(deptno,dname,loc),因此在分组上的GROUP BY子句中就能够写上三个字段:

SELECT d.deptno,d.dname,d.loc,COUNT(e.empno),NVL(AVG(e.sal),0) FROM dept d,emp e WHERE d.deptno=e.deptno(+) GROUP BY d.deptno,d.dname,d.loc;

以上就是多字段分组,可是无论是单字段仍是多字段,必定要有一个前提,存在了重复数据。

范例:要求统计出每一个部门的详细信息,而且要求这些部门的平均工资高于2000;

在以上程序的基础上完成开发,在以前惟一所学习的限定查询的语法只有WHERE子句,因此下面先使用WHERE完成要求。

SELECT d.deptno,d.dname,d.loc,COUNT(e.empno) mycount,NVL(AVG(e.sal),0) myavg FROM dept d,emp e WHERE d.deptno=e.deptno(+) AND AVG(e.sal)>2000 GROUP BY d.deptno,d.dname,d.loc;

如今出现了以下的错误提示:

WHERE d.deptno=e.deptno(+) AND AVG(e.sal)>2000 * 第 3 行出现错误: ORA-00934: 此处不容许使用分组函数

本错误提示的核心意思就是在WHERE子句之中不能使用统计函数,之因此在WHERE子句之中不能使用,实际上跟WHERE子句的主要功能有关,WHERE的主要功能是从所有的数据之中取出部分数据。

此时若是要对分组后的数据再次进行过滤,则使用HAVING子句完成,那么此时的SQL语法格式以下:

复制代码
SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]];
复制代码

下面使用HAVING进行过滤。

SELECT d.deptno,d.dname,d.loc,COUNT(e.empno) mycount,NVL(AVG(e.sal),0) myavg FROM dept d,emp e WHERE d.deptno=e.deptno(+) GROUP BY d.deptno,d.dname,d.loc HAVING AVG(sal)>2000;

注意点:WHERE和HAVING的区别

  • WHERE:是在执行GROUP BY操做以前进行的过滤,表示从所有数据之中筛选出部分的数据,在WHERE之中不能使用统计函数;
  • HAVING:是在GROUP BY分组以后的再次过滤,能够在HAVING子句中使用统计函数;

思考题:显示非销售人员工做名称以及从事同一工做雇员的月工资的总和,而且要知足从事同一工做的雇员的月工资合计大于$5000,输出结果按月工资的合计升序排列:

第一步:查询出全部的非销售人员的信息

SELECT * FROM emp WHERE job<>'SALESMAN';

第二步:按照职位进行分组,而且使用SUM函数统计

SELECT job,SUM(sal) FROM emp WHERE job<>'SALESMAN' GROUP BY job;

第三步:月工资的合计是经过统计函数查询的,因此如今这个对分组后的过滤要使用HAVING子句完成

SELECT job,SUM(sal) FROM emp WHERE job<>'SALESMAN' GROUP BY job HAVING SUM(sal)>5000;

第四步:按照升序排列

复制代码
SELECT job,SUM(sal) sum FROM emp WHERE job<>'SALESMAN' GROUP BY job HAVING SUM(sal)>5000 ORDER BY sum ASC;
复制代码

以上的题目就融合分组操做的大部分语法的使用,并且之后遇到问题,要慢慢分析。

5、子查询

子查询 = 简单查询 + 限定查询 + 多表查询 + 统计查询的综合体;

在以前强调过多表查询不建议你们使用,由于性能不好,可是多表查询最有利的替代者就是子查询,因此子查询在实际的开发之中使用的至关的多;

所谓的子查询指的就是在一个查询之中嵌套了其余的若干查询,嵌套子查询以后的查询SQL语句以下:

复制代码
SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 ,( SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]]) FROM 表名称 [别名], [表名称 [别名] ,…] ,( SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]]) [WHERE 条件(s) ( SELECT [DISTINCT] *|分组字段1 [别名] [,分组字段2 [别名] ,…] | 统计函数 FROM 表名称 [别名], [表名称 [别名] ,…] [WHERE 条件(s)] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]])] [GROUP BY 分组字段1 [,分组字段2 ,…]] [HAVING 分组后的过滤条件(可使用统计函数)] [ORDER BY 排序字段 ASC | DESC [,排序字段 ASC | DESC]];
复制代码

理论上子查询能够出如今查询语句的任意位置上,可是从我的而言,子查询出如今WHERE和FROM子句之中较多;

如下的使用特色为我的总结,不是官方声明的:

  • WHERE:子查询通常只返回单行列、多行单列、单行多列的数据;
  • FROM:子查询返回的通常是多行的数据,看成一张临时表出现。

范例:要求查询出工资比SMITH还要高的所有雇员信息

要想完成本程序,首先必需要知道SMITH的工资是多少:

SELECT sal FROM emp WHERE ename='SMITH';

因为此时返回的是单列的数据,因此这个子句查询能够在WHERE中出现。

SELECT * FROM emp WHERE sal>( SELECT sal FROM emp WHERE ename='SMITH');

范例:要求查询出高于公司平均工资的所有雇员信息

公司的平均工资应该使用AVG()函数求出。

SELECT AVG(sal) FROM emp;

此时数据的返回结果是单行单列的数据,在WHERE之中出现。

SELECT * FROM emp WHERE sal>( SELECT AVG(sal) FROM emp);

以上所返回的是单行单列,可是在子查询之中,也能够返回单行多列的数据,只是这种子查询不多出现。

范例:子查询返回单行多列数据

SELECT * FROM emp WHERE (job,sal)=( SELECT job,sal FROM emp WHERE ename='ALLEN');

若是如今的子查询返回的是多行单列数据的话,这个时候就须要使用三种判断符判断了:IN、ANY、ALL;

一、 IN操做符:用于指定一个子查询的判断范围

这个操做符的使用实际上与以前讲解的IN是同样的,惟一不一样的是,里面的范围由子查询指定了。

SELECT * FROM emp WHERE sal in ( SELECT sal FROM emp WHERE job='MANAGER');

可是在使用IN的时候还要注意NOT IN的问题,若是使用NOT IN操做,在子查询之中,若是有一个内容是null,则不会查询出任何的结果。

二、 ANY操做符:与每个内容想匹配,有三种匹配形式

  • =ANY:功能与IN操做符是彻底同样的;
SELECT * FROM emp WHERE sal=ANY ( SELECT sal FROM emp WHERE job='MANAGER');
  • >ANY:比子查询中返回记录最小的还要大的数据;
SELECT * FROM emp WHERE sal>ANY ( SELECT sal FROM emp WHERE job='MANAGER');
  • <ANY:比子查询中返回记录的最大的还要小;
SELECT * FROM emp WHERE sal<ANY ( SELECT sal FROM emp WHERE job='MANAGER');

三、 ALL操做符:与每个内容相匹配,有两种匹配形式:

  • >ALL:比子查询中返回的最大的记录还要大
SELECT * FROM emp WHERE sal>ALL ( SELECT sal FROM emp WHERE job='MANAGER');
  • <ALL:比子查询中返回的最小的记录还要小
SELECT * FROM emp WHERE sal<ALL ( SELECT sal FROM emp WHERE job='MANAGER');

以上的全部子查询都是在WHERE子句中出现的,那么下面再来观察在FROM子句中出现的查询,这个子查询通常返回的是多行多列的数据,看成一张临时表的方式来处理。

范例:查询出每一个部门的编号、名称、位置、部门人数、平均工资

  • 回顾:最先的时候使用的是多字段分组统计完成的:
SELECT d.deptno,d.dname,d.loc,COUNT(e.empno),AVG(e.sal) FROM emp e,dept d WHERE e.deptno(+)=d.deptno GROUP BY d.deptno,d.dname,d.loc;

这个时候其实是产生了笛卡尔积,一共产生了56条记录;

  • 新的解决方案:经过子查询完成,全部的统计查询只能在GROUP BY中出现,因此在子查询之中负责统计数据,而在外部的查询之中,负责将统计数据和dept表数据相统一。
复制代码
SELECT d.deptno,d.dname,d.loc,temp.count,temp.avg FROM dept d,( SELECT deptno dno,COUNT(empno) count,AVG(sal) avg FROM emp GROUP BY deptno) temp WHERE d.deptno=temp.dno(+);
复制代码

如今的程序中所操做的数据量:

  • 子查询中统计的记录是14条记录,最终统计的显示结果是3条记录;
  • dept表之中一共有4条记录;
  • 若是如今产生笛卡尔积的话只有12条记录,再加上雇员的14条记录,一共才26条记录;

经过如上的分析,能够发现,使用子查询的确要比使用多表查询更加节省性能,因此在开发之中子查询出现是最多的,并且在给出一个不成文的规定:大部分状况下,若是最终的查询结果之中须要出现SELECT子句,可是又不能直接使用统计函数的时候,就在子查询中统计信息,即:有复杂统计的地方大部分都须要子查询。

相关文章
相关标签/搜索