复合查询(重点)
前面我们讲解的mysql表的查询都是对一张表进行查询,在实际开发中这远远不够。
基本查询回顾
我们目前有新的表结构:dept 部门表是主表,emp 员工表是从表,将员工关联到部门,还有薪资等级表,通过员工的薪资和薪资等级表进行挂钩:
DROP database IF EXISTS `scott`;
CREATE database IF NOT EXISTS `scott` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `scott`;
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`deptno` int(2) unsigned zerofill NOT NULL COMMENT '部门编号',
`dname` varchar(14) DEFAULT NULL COMMENT '部门名称',
`loc` varchar(13) DEFAULT NULL COMMENT '部门所在地点'
);
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (
`empno` int(6) unsigned zerofill NOT NULL COMMENT '雇员编号',
`ename` varchar(10) DEFAULT NULL COMMENT '雇员姓名',
`job` varchar(9) DEFAULT NULL COMMENT '雇员职位',
`mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇员领导编号',
`hiredate` datetime DEFAULT NULL COMMENT '雇佣时间',
`sal` decimal(7,2) DEFAULT NULL COMMENT '工资月薪',
`comm` decimal(7,2) DEFAULT NULL COMMENT '奖金',
`deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部门编号'
);
DROP TABLE IF EXISTS `salgrade`;
CREATE TABLE `salgrade` (
`grade` int(11) DEFAULT NULL COMMENT '等级',
`losal` int(11) DEFAULT NULL COMMENT '此等级最低工资',
`hisal` int(11) DEFAULT NULL COMMENT '此等级最高工资'
);
insert into dept (deptno, dname, loc)
values (10, 'ACCOUNTING', 'NEW YORK');
insert into dept (deptno, dname, loc)
values (20, 'RESEARCH', 'DALLAS');
insert into dept (deptno, dname, loc)
values (30, 'SALES', 'CHICAGO');
insert into dept (deptno, dname, loc)
values (40, 'OPERATIONS', 'BOSTON');
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, null, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, null, 10);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7839, 'KING', 'PRESIDENT', null, '1981-11-17', 5000, null, 10);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7844, 'TURNER', 'SALESMAN', 7698,'1981-09-08', 1500, 0, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, null, 30);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, null, 20);
insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, null, 10);
insert into salgrade (grade, losal, hisal) values (1, 700, 1200);
insert into salgrade (grade, losal, hisal) values (2, 1201, 1400);
insert into salgrade (grade, losal, hisal) values (3, 1401, 2000);
insert into salgrade (grade, losal, hisal) values (4, 2001, 3000);
insert into salgrade (grade, losal, hisal) values (5, 3001, 9999);
mysql> source /home/lfz/learning-data/my-sql_data/scott_data.sql;
mysql> show tables;
+-----------------+
| Tables_in_scott |
+-----------------+
| dept |
| emp |
| salgrade |
+-----------------+
3 rows in set (0.00 sec)
mysql> select * from dept;
+--------+------------+----------+
| deptno | dname | loc |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
4 rows in set (0.00 sec)
mysql> select * from emp;
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| 007369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 | 800.00 | NULL | 20 |
| 007499 | ALLEN | SALESMAN | 7698 | 1981-02-20 00:00:00 | 1600.00 | 300.00 | 30 |
| 007521 | WARD | SALESMAN | 7698 | 1981-02-22 00:00:00 | 1250.00 | 500.00 | 30 |
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007654 | MARTIN | SALESMAN | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 | 30 |
| 007698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 007782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL | 10 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 007844 | TURNER | SALESMAN | 7698 | 1981-09-08 00:00:00 | 1500.00 | 0.00 | 30 |
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL | 20 |
| 007900 | JAMES | CLERK | 7698 | 1981-12-03 00:00:00 | 950.00 | NULL | 30 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
| 007934 | MILLER | CLERK | 7782 | 1982-01-23 00:00:00 | 1300.00 | NULL | 10 |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
14 rows in set (0.00 sec)
mysql> select * from salgrade;
+-------+-------+-------+
| grade | losal | hisal |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+
5 rows in set (0.00 sec)
查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J:
mysql> select * from emp where (sal>500 or job='MANAGER') and ename like 'J%';
+--------+-------+---------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+---------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007900 | JAMES | CLERK | 7698 | 1981-12-03 00:00:00 | 950.00 | NULL | 30 |
+--------+-------+---------+------+---------------------+---------+------+--------+
2 rows in set (0.00 sec)
mysql> select * from emp where (sal>500 or job='MANAGER') and substring(ename, 1, 1)='J';
+--------+-------+---------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+---------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007900 | JAMES | CLERK | 7698 | 1981-12-03 00:00:00 | 950.00 | NULL | 30 |
+--------+-------+---------+------+---------------------+---------+------+--------+
2 rows in set (0.00 sec)
按照部门号升序而雇员的工资降序排序:
mysql> select * from emp order by deptno asc, sal desc;
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 007782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL | 10 |
| 007934 | MILLER | CLERK | 7782 | 1982-01-23 00:00:00 | 1300.00 | NULL | 10 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL | 20 |
| 007369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 | 800.00 | NULL | 20 |
| 007698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 007499 | ALLEN | SALESMAN | 7698 | 1981-02-20 00:00:00 | 1600.00 | 300.00 | 30 |
| 007844 | TURNER | SALESMAN | 7698 | 1981-09-08 00:00:00 | 1500.00 | 0.00 | 30 |
| 007521 | WARD | SALESMAN | 7698 | 1981-02-22 00:00:00 | 1250.00 | 500.00 | 30 |
| 007654 | MARTIN | SALESMAN | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 | 30 |
| 007900 | JAMES | CLERK | 7698 | 1981-12-03 00:00:00 | 950.00 | NULL | 30 |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
14 rows in set (0.00 sec)
mysql> select ename, sal, deptno from emp order by deptno asc, sal desc;
+--------+---------+--------+
| ename | sal | deptno |
+--------+---------+--------+
| KING | 5000.00 | 10 |
| CLARK | 2450.00 | 10 |
| MILLER | 1300.00 | 10 |
| SCOTT | 3000.00 | 20 |
| FORD | 3000.00 | 20 |
| JONES | 2975.00 | 20 |
| ADAMS | 1100.00 | 20 |
| SMITH | 800.00 | 20 |
| BLAKE | 2850.00 | 30 |
| ALLEN | 1600.00 | 30 |
| TURNER | 1500.00 | 30 |
| WARD | 1250.00 | 30 |
| MARTIN | 1250.00 | 30 |
| JAMES | 950.00 | 30 |
+--------+---------+--------+
14 rows in set (0.00 sec)
使用年薪进行降序排序:表中除了月薪sal之外,还有奖金comm:我们要注意NULL和任何数运算完都是NULL!
mysql> select sal*12+ifnull(comm, 0) as '年薪' from emp order by '年薪' desc;
+----------+
| 年薪 |
+----------+
| 9600.00 |
| 19500.00 |
| 15500.00 |
| 35700.00 |
| 16400.00 |
| 34200.00 |
| 29400.00 |
| 36000.00 |
| 60000.00 |
| 18000.00 |
| 13200.00 |
| 11400.00 |
| 36000.00 |
| 15600.00 |
+----------+
14 rows in set (0.00 sec)
显示工资最高的员工的名字和工作岗位:
从最高工资来说,我们可以使用max聚合函数做统计,但是聚合只是对一列的相同数据做聚合,我们还需要带上名字的,所以这里直接单纯的做聚合是不合适的,聚合只是统计最高薪资是几,但是具体是谁并不知道!
mysql> select max(sal) from emp;
+----------+
| max(sal) |
+----------+
| 5000.00 |
+----------+
1 row in set (0.00 sec)
mysql> select * from emp where sal=5000;
+--------+-------+-----------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+-----------+------+---------------------+---------+------+--------+
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
+--------+-------+-----------+------+---------------------+---------+------+--------+
1 row in set (0.00 sec)
可是这样查询有点不好了,还分两句。
其实MySQL 支持我们select进行嵌套,执行的时候先执行子select,也就是先执行嵌套的select:可以看成函数调用的返回了!
我们在where当中作为筛查的语句!
mysql> select * from emp where sal=(select max(sal) from emp);
+--------+-------+-----------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+-----------+------+---------------------+---------+------+--------+
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
+--------+-------+-----------+------+---------------------+---------+------+--------+
1 row in set (0.00 sec)
显示工资高于平均工资的员工信息:
mysql> select * from emp where sal>(select avg(sal) from emp);
+--------+-------+-----------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+-----------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 007782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL | 10 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
+--------+-------+-----------+------+---------------------+---------+------+--------+
6 rows in set (0.00 sec)
显示每个部门的平均工资和最高工资:--- 要分组了!分组就是在逻辑上拆成了三个子表而已,然后针对子表进行聚合统计:
mysql> select deptno, format(max(sal), 2) as '最高', format(avg(sal), 2) as '平均' from emp group by deptno;
+--------+----------+----------+
| deptno | 最高 | 平均 |
+--------+----------+----------+
| 20 | 3,000.00 | 2,175.00 |
| 30 | 2,850.00 | 1,566.67 |
| 10 | 5,000.00 | 2,916.67 |
+--------+----------+----------+
3 rows in set (0.00 sec)
显示平均工资低于2000的部门号和该部门的的平均工资:
mysql> select deptno, avg(sal) as 平均工资 from emp group by deptno having 平均工资<=2000;
+--------+--------------+
| deptno | 平均工资 |
+--------+--------------+
| 30 | 1566.666667 |
+--------+--------------+
1 row in set (0.00 sec)
显示每种岗位的雇员总数,平均工资:
mysql> select job, count(job), avg(sal) from emp group by job;
+-----------+------------+-------------+
| job | count(job) | avg(sal) |
+-----------+------------+-------------+
| CLERK | 4 | 1037.500000 |
| SALESMAN | 4 | 1400.000000 |
| MANAGER | 3 | 2758.333333 |
| ANALYST | 2 | 3000.000000 |
| PRESIDENT | 1 | 5000.000000 |
+-----------+------------+-------------+
5 rows in set (0.00 sec)
多表查询
实际开发中往往数据来自不同的表,所以需要多表查询。本节我们用一个简单的公司管理系统,有三张表EMP,DEPT,SALGRADE来演示如何进行多表查询。
案例:
我们整合的直接结论就是:两个表进行聚合拼接而已!!! 只是将数据进行穷举组合了而已,也就是进行了笛卡尔积!
但是整合成了还是依旧是一张表,所以我们可以对笛卡尔积后的表进行条件筛选了!--- 把多张先无条件地拼成一张“大表”(笛卡尔积),再用 WHERE
/ON
把无效组合筛掉,只留下业务需要的行。
显示雇员名、雇员工资以及所在部门的名字,因为上面的数据来自EMP和DEPT表,因此要联合查询,其实我们只要emp表中的deptno = dept表中的deptno字段的记录:
mysql> select * from emp,dept where emp.deptno=dept.deptno;
+--------+--------+-----------+------+---------------------+---------+---------+--------+--------+------------+----------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno | deptno | dname | loc |
+--------+--------+-----------+------+---------------------+---------+---------+--------+--------+------------+----------+
| 007369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 | 800.00 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 007499 | ALLEN | SALESMAN | 7698 | 1981-02-20 00:00:00 | 1600.00 | 300.00 | 30 | 30 | SALES | CHICAGO |
| 007521 | WARD | SALESMAN | 7698 | 1981-02-22 00:00:00 | 1250.00 | 500.00 | 30 | 30 | SALES | CHICAGO |
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 007654 | MARTIN | SALESMAN | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 | 30 | 30 | SALES | CHICAGO |
| 007698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 | 30 | SALES | CHICAGO |
| 007782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 007844 | TURNER | SALESMAN | 7698 | 1981-09-08 00:00:00 | 1500.00 | 0.00 | 30 | 30 | SALES | CHICAGO |
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 007900 | JAMES | CLERK | 7698 | 1981-12-03 00:00:00 | 950.00 | NULL | 30 | 30 | SALES | CHICAGO |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 007934 | MILLER | CLERK | 7782 | 1982-01-23 00:00:00 | 1300.00 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
+--------+--------+-----------+------+---------------------+---------+---------+--------+--------+------------+----------+
14 rows in set (0.00 sec)
显示部门号为10的部门名,员工名和工资:
mysql> select ename, sal, dname, emp.deptno from emp,dept where emp.deptno=dept.deptno and emp.deptno=10;
+--------+---------+------------+--------+
| ename | sal | dname | deptno |
+--------+---------+------------+--------+
| CLARK | 2450.00 | ACCOUNTING | 10 |
| KING | 5000.00 | ACCOUNTING | 10 |
| MILLER | 1300.00 | ACCOUNTING | 10 |
+--------+---------+------------+--------+
3 rows in set (0.01 sec)
显示各个员工的姓名,工资,及工资级别:
mysql> select ename, sal, grade from emp, salgrade where sal between losal and hisal;
+--------+---------+-------+
| ename | sal | grade |
+--------+---------+-------+
| SMITH | 800.00 | 1 |
| ALLEN | 1600.00 | 3 |
| WARD | 1250.00 | 2 |
| JONES | 2975.00 | 4 |
| MARTIN | 1250.00 | 2 |
| BLAKE | 2850.00 | 4 |
| CLARK | 2450.00 | 4 |
| SCOTT | 3000.00 | 4 |
| KING | 5000.00 | 5 |
| TURNER | 1500.00 | 3 |
| ADAMS | 1100.00 | 1 |
| JAMES | 950.00 | 1 |
| FORD | 3000.00 | 4 |
| MILLER | 1300.00 | 2 |
+--------+---------+-------+
14 rows in set (0.00 sec)
自连接
自连接是指在同一张表连接查询,也就是对同一张表做笛卡尔积!
我们对同一张表进行操作,还是需要进行重命名的比较好,我们也可以在 from 中做重命名,因为要知道具体是从拿一张表中拿数据!
案例:
显示员工FORD的上级领导的编号和姓名(mgr是员工领导的编号--empno)
- 使用的子查询
mysql> select ename, empno from emp where empno=(select mgr from emp where ename='FORD');
+-------+--------+
| ename | empno |
+-------+--------+
| JONES | 007566 |
+-------+--------+
1 row in set (0.00 sec)
- 使用多表查询(自查询)
mysql> select e1.ename, e1.empno from emp e1, emp e2 where e1.ename='FORD' and e1.mgr=e2.empno;
+-------+--------+
| ename | empno |
+-------+--------+
| FORD | 007902 |
+-------+--------+
1 row in set (0.00 sec)
子查询
子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询
单行子查询
返回一行记录的子查询:也就是子查询的结果是单列单行,即一个结果
显示SMITH同一部门的员工:我们就需要先分出 SMITH 的所在部分是什么,然后将部门的人全部都筛选出来就可以了!
mysql> select * from emp where deptno=(select deptno from emp where ename='SMITH');
+--------+-------+---------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+---------+------+---------------------+---------+------+--------+
| 007369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 | 800.00 | NULL | 20 |
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL | 20 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
+--------+-------+---------+------+---------------------+---------+------+--------+
5 rows in set (0.00 sec)
多行子查询
返回多行记录的子查询:也就是子查询的结果是单列多行
in关键字:查询和10号部门的工作岗位相同的雇员的名字,岗位,工资,部门名称,但是不包含10号部门自己的:
mysql> select distinct job from emp where deptno=10;
+-----------+
| job |
+-----------+
| MANAGER |
| PRESIDENT |
| CLERK |
+-----------+
3 rows in set (0.00 sec)
mysql> select ename, job, deptno from emp where job in (select distinct job from emp where deptno=10);
+--------+-----------+--------+
| ename | job | deptno |
+--------+-----------+--------+
| SMITH | CLERK | 20 |
| JONES | MANAGER | 20 |
| BLAKE | MANAGER | 30 |
| CLARK | MANAGER | 10 |
| KING | PRESIDENT | 10 |
| ADAMS | CLERK | 20 |
| JAMES | CLERK | 30 |
| MILLER | CLERK | 10 |
+--------+-----------+--------+
8 rows in set (0.00 sec)
mysql> select ename, job, deptno from emp where job in (select distinct job from emp where deptno=10) and deptno!=10;
#这么写也可以
#mysql> select ename, job, deptno from emp where job in (select distinct job from emp where deptno=10) and deptno<>10;
+-------+---------+--------+
| ename | job | deptno |
+-------+---------+--------+
| SMITH | CLERK | 20 |
| JONES | MANAGER | 20 |
| BLAKE | MANAGER | 30 |
| ADAMS | CLERK | 20 |
| JAMES | CLERK | 30 |
+-------+---------+--------+
5 rows in set (0.00 sec)
mysql> select ename, job, sal, dname from (select ename, job, sal, deptno from emp where job in (select distinct job from emp where deptno=10) andd deptno<>10) as tmp, dept where dept.deptno=tmp.deptno;
+-------+---------+---------+----------+
| ename | job | sal | dname |
+-------+---------+---------+----------+
| ADAMS | CLERK | 1100.00 | RESEARCH |
| JONES | MANAGER | 2975.00 | RESEARCH |
| SMITH | CLERK | 800.00 | RESEARCH |
| JAMES | CLERK | 950.00 | SALES |
| BLAKE | MANAGER | 2850.00 | SALES |
+-------+---------+---------+----------+
5 rows in set (0.00 sec)
一个SQL的查询结构也可以作为一张表,同样的可以做笛卡尔积!还有子查询不仅仅可以在where后,也可以在from后充当新的表结构!
all关键字:显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号:all 的对象是这个多行一列的数据!所以我们有两种解法:
mysql> select * from emp where sal > all (select distinct sal from emp where deptno=30);
+--------+-------+-----------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+-----------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
+--------+-------+-----------+------+---------------------+---------+------+--------+
4 rows in set (0.00 sec)
mysql> select * from emp where sal>(select max(sal) from emp where deptno=30);
+--------+-------+-----------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+-----------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
+--------+-------+-----------+------+---------------------+---------+------+--------+
4 rows in set (0.00 sec)
any关键字:显示工资比部门30的任意一名员工的工资高的员工的姓名、工资和部门号(包含自己部门的员工)(这里的任意是比其中一个人高就好了,也就是比最小的高就可以了)
mysql> select * from emp where sal > any(select distinct sal from emp where deptno=30);
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| 007499 | ALLEN | SALESMAN | 7698 | 1981-02-20 00:00:00 | 1600.00 | 300.00 | 30 |
| 007521 | WARD | SALESMAN | 7698 | 1981-02-22 00:00:00 | 1250.00 | 500.00 | 30 |
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007654 | MARTIN | SALESMAN | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 | 30 |
| 007698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 007782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL | 10 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 007844 | TURNER | SALESMAN | 7698 | 1981-09-08 00:00:00 | 1500.00 | 0.00 | 30 |
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL | 20 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
| 007934 | MILLER | CLERK | 7782 | 1982-01-23 00:00:00 | 1300.00 | NULL | 10 |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
12 rows in set (0.00 sec)
mysql> select * from emp where sal>(select min(sal) from emp where deptno=30);
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
| 007499 | ALLEN | SALESMAN | 7698 | 1981-02-20 00:00:00 | 1600.00 | 300.00 | 30 |
| 007521 | WARD | SALESMAN | 7698 | 1981-02-22 00:00:00 | 1250.00 | 500.00 | 30 |
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007654 | MARTIN | SALESMAN | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 | 30 |
| 007698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 007782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL | 10 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 007844 | TURNER | SALESMAN | 7698 | 1981-09-08 00:00:00 | 1500.00 | 0.00 | 30 |
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL | 20 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
| 007934 | MILLER | CLERK | 7782 | 1982-01-23 00:00:00 | 1300.00 | NULL | 10 |
+--------+--------+-----------+------+---------------------+---------+---------+--------+
12 rows in set (0.00 sec)
多列子查询
单列子查询是值只查询只返回单列,单行数据:
mysql> select distinct max(sal) from emp where deptno=10;
+----------+
| max(sal) |
+----------+
| 5000.00 |
+----------+
1 row in set (0.00 sec)
多行子查询是指返回单列多行数据,都是针对单列而言的:
mysql> select distinct sal from emp where deptno=10;
+---------+
| sal |
+---------+
| 2450.00 |
| 5000.00 |
| 1300.00 |
+---------+
3 rows in set (0.00 sec)
多列子查询是指查询返回多个列数据的子查询语句:
mysql> select * from emp where ename="SMITH";
+--------+-------+-------+------+---------------------+--------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+-------+------+---------------------+--------+------+--------+
| 007369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 | 800.00 | NULL | 20 |
+--------+-------+-------+------+---------------------+--------+------+--------+
1 row in set (0.00 sec)
案例:查询和SMITH的部门和岗位完全相同的所有雇员,不含SMITH本人:
mysql> select * from emp where(deptno, job) = (select deptno, job from emp where ename="SMITH") and ename<>'SMITH';
+--------+-------+-------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+-------+------+---------------------+---------+------+--------+
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL | 20 |
+--------+-------+-------+------+---------------------+---------+------+--------+
1 row in set (0.00 sec)
我们也可以试一下 in:
mysql> select * from emp where(deptno, job) in (select deptno, job from emp where ename="SMITH") and ename<>'SMITH';
+--------+-------+-------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+-------+------+---------------------+---------+------+--------+
| 007876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | NULL | 20 |
+--------+-------+-------+------+---------------------+---------+------+--------+
1 row in set (0.00 sec)
我们发现是同样可行的,我们就可以想想对于多行多列的自查询也是可行的,我们可以使用in,虽然查出来的信息是成对出现的,但是可以当成一个集合,通过in判断子集有没有在真个集合中!
换句话说,多列子查询是既可以处理多列单行,也可以处理多列多行,只不过后者得场景比较少!
在from子句中使用子查询 --- 充当笛卡尔积
子查询语句出现在from子句中。这里要用到数据查询的技巧,把一个子查询当做一个临时表使用,不过就是要起别名罢了。就像group by 是叫做分组,但是我更喜欢称之为分成多张表!
案例:
显示每个高于自己部门平均工资的员工的姓名、部门号、工资、平均工资、部门所在地:
mysql> select * from emp, (select deptno, avg(sal) from emp group by deptno) tmp;
//
mysql> select * from emp, (select deptno, avg(sal) from emp group by deptno) tmp where emp.deptno=tmp.deptno;
//
mysql> select * from emp, (select deptno, avg(sal) from emp group by deptno) tmp where emp.deptno=tmp.deptno and emp.sal>tmp.avg(sal);
ERROR 1630 (42000): FUNCTION tmp.avg does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual
mysql> select * from emp, (select deptno, avg(sal) avg_sal from emp group by deptno) tmp where emp.deptno=tmp.deptno and emp.sal>tmp.avg_sal;
+--------+-------+-----------+------+---------------------+---------+--------+--------+--------+-------------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno | deptno | avg_sal |
+--------+-------+-----------+------+---------------------+---------+--------+--------+--------+-------------+
| 007499 | ALLEN | SALESMAN | 7698 | 1981-02-20 00:00:00 | 1600.00 | 300.00 | 30 | 30 | 1566.666667 |
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 | 20 | 2175.000000 |
| 007698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 | 30 | 1566.666667 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 | 20 | 2175.000000 |
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 | 10 | 2916.666667 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 | 20 | 2175.000000 |
+--------+-------+-----------+------+---------------------+---------+--------+--------+--------+-------------+
6 rows in set (0.00 sec)
mysql> select ename, emp.deptno, emp.sal, tmp.avg_sal from emp, (select deptno, avg(sal) avg_sal from emp group by deptno) tmp where emp.deptno=tmmp.deptno and emp.sal>tmp.avg_sal;
+-------+--------+---------+-------------+
| ename | deptno | sal | avg_sal |
+-------+--------+---------+-------------+
| ALLEN | 30 | 1600.00 | 1566.666667 |
| JONES | 20 | 2975.00 | 2175.000000 |
| BLAKE | 30 | 2850.00 | 1566.666667 |
| SCOTT | 20 | 3000.00 | 2175.000000 |
| KING | 10 | 5000.00 | 2916.666667 |
| FORD | 20 | 3000.00 | 2175.000000 |
+-------+--------+---------+-------------+
6 rows in set (0.00 sec)
然后我们再次拿着我们上面获得得临时表和下面的部门表进行笛卡尔积,得到答案:
mysql> select * from dept, (select ename, emp.deptno, emp.sal, tmp.avg_sal from emp, (select deptno, avg(sal) avg_sal from emp group by deptno) tmmp where emp.deptno=tmp.deptno and emp.sal>tmp.avg_sal) t1;
//
mysql> select * from dept, (select ename, emp.deptno, emp.sal, tmp.avg_sal from emp, (select deptno, avg(sal) avg_sal from emp group by deptno) tmp where emp.deptno=tmp.deptno and emp.sal>tmp.avg_sal) t1 where dept.deptno=t1.deptno;
//
mysql> select t1.ename, t1.deptno, t1.sal, t1.avg_sal, dept.loc from dept, (select ename, emp.deptno, emp.sal, tmp.avg_sal from emp, (select deptnno, avg(sal) avg_sal from emp group by deptno) tmp where emp.deptno=tmp.deptno and emp.sal>tmp.avg_sal) t1 where dept.deptno=t1.deptno;
+-------+--------+---------+-------------+----------+
| ename | deptno | sal | avg_sal | loc |
+-------+--------+---------+-------------+----------+
| ALLEN | 30 | 1600.00 | 1566.666667 | CHICAGO |
| JONES | 20 | 2975.00 | 2175.000000 | DALLAS |
| BLAKE | 30 | 2850.00 | 1566.666667 | CHICAGO |
| SCOTT | 20 | 3000.00 | 2175.000000 | DALLAS |
| KING | 10 | 5000.00 | 2916.666667 | NEW YORK |
| FORD | 20 | 3000.00 | 2175.000000 | DALLAS |
+-------+--------+---------+-------------+----------+
6 rows in set (0.00 sec)
我们对于这种复杂问题,都是不断的任务分解的!!!
查找每个部门工资最高的人的姓名、工资、部门、最高工资:
mysql> select max(sal) from emp group by deptno;
//
mysql> select * from emp, (select max(sal) from emp group by deptno) as tmp;
//
mysql> select * from emp, (select deptno, max(sal) from emp group by deptno) as tmp where emp.deptno=tmp.deptno;
//
mysql> select * from emp, (select deptno, max(sal) as avg_sal from emp group by deptno) as tmp where emp.deptno=tmp.deptno and emp.sal=tmp.avg_sal;
//
mysql> select emp.ename, emp.sal, tmp.max_sal from emp, (select deptno, max(sal) as max_sal from emp group by deptno) as tmp where emp.deptno=tmp..deptno and emp.sal=tmp.max_sal;
+-------+---------+---------+
| ename | sal | max_sal |
+-------+---------+---------+
| BLAKE | 2850.00 | 2850.00 |
| SCOTT | 3000.00 | 3000.00 |
| KING | 5000.00 | 5000.00 |
| FORD | 3000.00 | 3000.00 |
+-------+---------+---------+
4 rows in set (0.01 sec)
显示每个部门的信息(部门名,编号,地址)和人员数量:
我们可以使用多表进行笛卡尔积查询:
mysql> select dept.dname, dept.deptno, dept.loc, count(*) '部门人数' from emp, dept where emp.deptno=dept.deptno group by dept.deptno, dept.dname,, dept.loc;
+------------+--------+----------+--------------+
| dname | deptno | loc | 部门人数 |
+------------+--------+----------+--------------+
| RESEARCH | 20 | DALLAS | 5 |
| SALES | 30 | CHICAGO | 6 |
| ACCOUNTING | 10 | NEW YORK | 3 |
+------------+--------+----------+--------------+
3 rows in set (0.00 sec)
FROM 子句:生成笛卡儿积
FROM emp, dept
把 emp 表和 dept 表所有行全部两两组合,形成一个临时结果集(笛卡儿积)。
WHERE 子句:过滤行
WHERE emp.deptno = dept.deptno
在笛卡儿积中只保留 emp.deptno 与 dept.deptno 相等的行,相当于做了一次内连接。
SELECT 子句:选出需要的列
SELECT dept.dname, dept.deptno, dept.loc, COUNT(*) AS '部门人数'
对过滤后的结果,选出:
dept.dname(部门名称)
dept.deptno(部门编号)
dept.loc(部门地点)
COUNT(*):统计每个部门在该结果集里的行数(即该部门的人数)
GROUP BY 子句:分组聚合
GROUP BY dept.deptno, dept.dname, dept.loc
把上一步的结果按 (dept.deptno, dept.dname, dept.loc) 三个字段分组,每组计算一次 COUNT(*)。
最终结果
得到一张列表:每个部门一条记录,列出部门名、部门编号、部门地点以及该部门的人数。
我么也可以使用子查询:和上面一样的啦
mysql> select deptno, count(*) from emp group by deptno;
//
mysql> select * from dept, (select deptno, count(*) from emp group by deptno) tmp where dept.deptno=tmp.deptno;
//
mysql> select dept.deptno, dept.dname, dept.loc, tmp.number from dept, (select deptno, count(*) number from emp group by deptno) tmp where dept.de
eptno=tmp.deptno;
+--------+------------+----------+--------+
| deptno | dname | loc | number |
+--------+------------+----------+--------+
| 10 | ACCOUNTING | NEW YORK | 3 |
| 20 | RESEARCH | DALLAS | 5 |
| 30 | SALES | CHICAGO | 6 |
+--------+------------+----------+--------+
3 rows in set (0.00 sec)
合并查询
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union,union all
合并并不是做笛卡尔积,而只是单纯的将两个表合起来!
union
该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。
案例:将工资大于2500或职位是MANAGER的人找出来:
mysql> select * from emp where job='MANAGER';
+--------+-------+---------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+---------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 007782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL | 10 |
+--------+-------+---------+------+---------------------+---------+------+--------+
3 rows in set (0.00 sec)
mysql> select * from emp where sal>2500;
+--------+-------+-----------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+--------+-------+-----------+------+---------------------+---------+------+--------+
| 007566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 007698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 007788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 007839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 007902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
+--------+-------+-----------+------+---------------------+---------+------+--------+
5 rows in set (0.00 sec)
mysql> select * from emp where sal>2500 union select * from emp where job='MANAGER';
+-------+-------+-----------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+-------+-----------+------+---------------------+---------+------+--------+
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL | 10 |
+-------+-------+-----------+------+---------------------+---------+------+--------+
6 rows in set (0.00 sec)
我们发现是有去重的!
union all
该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。
案例:将工资大于25000或职位是MANAGER的人找出来:
mysql> select * from emp where sal>2500 union all select * from emp where job='MANAGER';
+-------+-------+-----------+------+---------------------+---------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+-------+-----------+------+---------------------+---------+------+--------+
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 00:00:00 | 5000.00 | NULL | 10 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | NULL | 20 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | NULL | 20 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | NULL | 10 |
+-------+-------+-----------+------+---------------------+---------+------+--------+
8 rows in set (0.00 sec)
这个是没有进行去重的!
我们要注意我们筛选的两个结果之间的列信息是要一样的,属性,数量等等
这个其实没什么用。
实战OJ(牛客)
获取所有员工当前的manager,如果当前的manager是自己的话结果不显示,当前表示to_date='9999-01-01'