零基础学习MySql数据库—4

目录

多表查询

为什么要使用多表查询?

自连接

子查询 

单行子查询

多行子查询

 多列子查询

在from子句中使用子查询 

自我复制(蠕虫复制)

删除表中重复的数据

合并查询

外键

综合案例

多表查询


  • 前几篇博客中写的 mysql 表的查询都是对一张表进行查询,在实际开发中这是远远不够的。
  • 下面我们用一个简单的公司管理系统,有三张表EMP,DEPT,SALGRADE来演示如何进行多表查询。

先将前面的基本查询进行一下加强

1.  查询工资高于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.02 sec)

2. 按照部门号升序而雇员号的工资降序排序

mysql> select * from EMP order by deptno, 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)

3. 使用年薪进行排序:

mysql> select ename, sal*12+ifnull(comm,0) as '年薪' from EMP order by 年薪 desc;
+--------+----------+
| ename  | 年薪     |
+--------+----------+
| KING   | 60000.00 |
| SCOTT  | 36000.00 |
| FORD   | 36000.00 |
| JONES  | 35700.00 |
| BLAKE  | 34200.00 |
| CLARK  | 29400.00 |
| ALLEN  | 19500.00 |
| TURNER | 18000.00 |
| MARTIN | 16400.00 |
| MILLER | 15600.00 |
| WARD   | 15500.00 |
| ADAMS  | 13200.00 |
| JAMES  | 11400.00 |
| SMITH  |  9600.00 |
+--------+----------+
14 rows in set (0.00 sec)

4. 分页查询:

语法:
select 字段from 表名where 条件limit 起始位置,记录条数

示例:按雇员的empno号升序取数,每页显示3条记录。请分别显示第一页,第二页,第三页

mysql> select * from EMP order by empno limit 0,3;
+--------+-------+----------+------+---------------------+---------+--------+--------+
| 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 |
+--------+-------+----------+------+---------------------+---------+--------+--------+
3 rows in set (0.00 sec)

5. 显示工资最高的员工的名字和工作岗位:

mysql> select ename, job from EMP where sal = (select max(sal) from EMP);
+-------+-----------+
| ename | job       |
+-------+-----------+
| KING  | PRESIDENT |
+-------+-----------+
1 row in set (0.01 sec)

6. 显示工资高于平均工资的员工信息:

mysql> select ename, sal from EMP where sal > (select avg(sal) from EMP);
+-------+---------+
| ename | sal     |
+-------+---------+
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| SCOTT | 3000.00 |
| KING  | 5000.00 |
| FORD  | 3000.00 |
+-------+---------+
6 rows in set (0.00 sec)

7. 显示每个部门的平均工资和最高工资

mysql> select deptno, format(avg(sal),2), max(sal) from EMP group by deptno;
+--------+--------------------+----------+
| deptno | format(avg(sal),2) | max(sal) |
+--------+--------------------+----------+
|     10 | 2,916.67           |  5000.00 |
|     20 | 2,175.00           |  3000.00 |
|     30 | 1,566.67           |  2850.00 |
+--------+--------------------+----------+
3 rows in set (0.00 sec)

8. 显示平均工资低于2000的部门号和它的平均工资:

mysql> select deptno, avg(sal) as avg_sal from EMP group by deptno having avg_sal<2000;
+--------+-------------+
| deptno | avg_sal     |
+--------+-------------+
|     30 | 1566.666667 |
+--------+-------------+
1 row in set (0.00 sec)

9. 显示每种岗位的雇员总数,平均工资:

mysql> select job, count(*), format(avg(sal),2) from EMP group by job;
+-----------+----------+--------------------+
| job       | count(*) | format(avg(sal),2) |
+-----------+----------+--------------------+
| ANALYST   |        2 | 3,000.00           |
| CLERK     |        4 | 1,037.50           |
| MANAGER   |        3 | 2,758.33           |
| PRESIDENT |        1 | 5,000.00           |
| SALESMAN  |        4 | 1,400.00           |
+-----------+----------+--------------------+
5 rows in set (0.00 sec)

为什么要使用多表查询?

实际开发中往往数据来自不同的表,所以需要多表查询。
例如:

  • 显示雇员名、雇员工资以及所在部门的名字

 因为上面的数据来自EMP和DEPT表,因此要联合查询。

mysql> select * from EMP, DEPT;

其实我们只要emp表中的deptno = dept表中的deptno字段的记录

mysql> select EMP.ename,EMP.sal,DEPT.dname from EMP,DEPT where EMP.deptno = DEPT.deptno;
+--------+---------+------------+
| ename  | sal     | dname      |
+--------+---------+------------+
| SMITH  |  800.00 | RESEARCH   |
| ALLEN  | 1600.00 | SALES      |
| WARD   | 1250.00 | SALES      |
| JONES  | 2975.00 | RESEARCH   |
| MARTIN | 1250.00 | SALES      |
| BLAKE  | 2850.00 | SALES      |
| CLARK  | 2450.00 | ACCOUNTING |
| SCOTT  | 3000.00 | RESEARCH   |
| KING   | 5000.00 | ACCOUNTING |
| TURNER | 1500.00 | SALES      |
| ADAMS  | 1100.00 | RESEARCH   |
| JAMES  |  950.00 | SALES      |
| FORD   | 3000.00 | RESEARCH   |
| MILLER | 1300.00 | ACCOUNTING |
+--------+---------+------------+
14 rows in set (0.00 sec)

显示部门号为10的部门名,员工名和工资:

mysql> select ename,sal,dname from EMP,DEPT where EMP.deptno=DEPT.deptno and DEPT.deptno = 10;
+--------+---------+------------+
| ename  | sal     | dname      |
+--------+---------+------------+
| CLARK  | 2450.00 | ACCOUNTING |
| KING   | 5000.00 | ACCOUNTING |
| MILLER | 1300.00 | ACCOUNTING |
+--------+---------+------------+
3 rows in set (0.00 sec)

显示各个员工的姓名,工资,及工资级别

mysql> select ename,sal,grade from EMP,SALGRADE where EMP.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.01 sec)

自连接

自连接是指在同一张表连接查询

示例:显示员工FORD的上级领导的姓名

这里没有用多表,用的是子查询

mysql> select ename from EMP where empno=(select mgr from EMP where ename='FORD');
+-------+
| ename |
+-------+
| JONES |
+-------+
1 row in set (0.00 sec)

子查询 

子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询。

单行子查询

返回一行记录的子查询

显示 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 * from EMP where job in (select distinct job from EMP where deptno=10) and deptno <> 10;
+--------+-------+---------+------+---------------------+---------+------+--------+
| 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 |
| 007369 | SMITH | CLERK   | 7902 | 1980-12-17 00:00:00 |  800.00 | NULL |     20 |
| 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 |
+--------+-------+---------+------+---------------------+---------+------+--------+
5 rows in set (0.00 sec)
  •  在多行子查询中使用all操作符

显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号

mysql> select ename,sal,deptno from EMP where sal > all(select sal from EMP where deptno=30);
+-------+---------+--------+
| ename | sal     | deptno |
+-------+---------+--------+
| JONES | 2975.00 |     20 |
| SCOTT | 3000.00 |     20 |
| KING  | 5000.00 |     10 |
| FORD  | 3000.00 |     20 |
+-------+---------+--------+
4 rows in set (0.00 sec
  • 在多行子查询中使用any操作符

显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号

mysql> select ename,sal,deptno from EMP where sal > any(select sal from EMP where deptno=30);
+--------+---------+--------+
| ename  | sal     | deptno |
+--------+---------+--------+
| ALLEN  | 1600.00 |     30 |
| WARD   | 1250.00 |     30 |
| JONES  | 2975.00 |     20 |
| MARTIN | 1250.00 |     30 |
| BLAKE  | 2850.00 |     30 |
| CLARK  | 2450.00 |     10 |
| SCOTT  | 3000.00 |     20 |
| KING   | 5000.00 |     10 |
| TURNER | 1500.00 |     30 |
| ADAMS  | 1100.00 |     20 |
| FORD   | 3000.00 |     20 |
| MILLER | 1300.00 |     10 |
+--------+---------+--------+
12 rows in set (0.00 sec)

 多列子查询

单行子查询是指子查询只返回单列,单行数据,多行子查询是指返回单列多行数据,都是针对单列而言的,而多列子查询则是指查询返回多个列数据的子查询语句。

示例:查询和SMITH的部门和岗位完全相同的所有雇员,不含SMITH本人

mysql> select ename from EMP where (deptno, job)=(select deptno,job from EMP where
 -> ename='SMITH') and ename <> 'SMITH';
+-------+
| ename |
+-------+
| ADAMS |
+-------+
1 row in set (0.00 sec)

在from子句中使用子查询 

子查询语句出现在from子句中。这里要用到数据查询的技巧,把一个子查询当做一个临时表使用。
示例:显示高于自己部门平均工资的员工的信息 

mysql> select ename,deptno,sal,asal from EMP,
    -> (select avg(sal) asal, deptno dt from EMP group by deptno) tmp
    -> where EMP.sal > tmp.asal and EMP.deptno=tmp.dt;
+-------+--------+---------+-------------+
| ename | deptno | sal     | asal        |
+-------+--------+---------+-------------+
| KING  |     10 | 5000.00 | 2916.666667 |
| JONES |     20 | 2975.00 | 2175.000000 |
| SCOTT |     20 | 3000.00 | 2175.000000 |
| FORD  |     20 | 3000.00 | 2175.000000 |
| ALLEN |     30 | 1600.00 | 1566.666667 |
| BLAKE |     30 | 2850.00 | 1566.666667 |
+-------+--------+---------+-------------+
6 rows in set (0.00 sec)

查找每个部门工资最高的人的详细资料:

mysql> select EMP.ename,EMP.sal,EMP.deptno,ms from EMP,
    -> (select max(sal) ms,deptno from EMP group by deptno) tmp
    -> where EMP.deptno=tmp.deptno and EMP.sal=tmp.ms;
+-------+---------+--------+---------+
| ename | sal     | deptno | ms      |
+-------+---------+--------+---------+
| BLAKE | 2850.00 |     30 | 2850.00 |
| SCOTT | 3000.00 |     20 | 3000.00 |
| KING  | 5000.00 |     10 | 5000.00 |
| FORD  | 3000.00 |     20 | 3000.00 |
+-------+---------+--------+---------+
4 rows in set (0.00 sec)

显示每个部门的信息(部门名,编号,地址)和人员数量。
方法1:使用多表

mysql> select dname, DEPT.deptno,loc,count(*) '部门人数' from EMP,DEPT
    -> where EMP.deptno=DEPT.deptno
    -> group by DEPT.deptno;
+------------+--------+----------+----------+
| dname      | deptno | loc      | 部门人数 |
+------------+--------+----------+----------+
| ACCOUNTING |     10 | NEW YORK |        3 |
| RESEARCH   |     20 | DALLAS   |        5 |
| SALES      |     30 | CHICAGO  |        6 |
+------------+--------+----------+----------+
3 rows in set (0.00 sec)

方法2:使用子查询

-- 1. 对EMP表进行人员统计
select count(*), deptno from EMP group by deptno;

-- 2. 将上面的表看作临时表
mysql> select DEPT.deptno,dname,mycnt,loc from DEPT,
    -> (select count(*) mycnt,deptno from EMP group by deptno) tmp
    -> where DEPT.deptno=tmp.deptno;
+--------+------------+-------+----------+
| deptno | dname      | mycnt | loc      |
+--------+------------+-------+----------+
|     10 | ACCOUNTING |     3 | NEW YORK |
|     20 | RESEARCH   |     5 | DALLAS   |
|     30 | SALES      |     6 | CHICAGO  |
+--------+------------+-------+----------+
3 rows in set (0.00 sec)

自我复制(蠕虫复制)

上面使用了多表和子查询两种方式进行查询,到底哪个效率高呢?我们需要弄大量数据来进行测试。可以使用自我复制创建海量数据。

示例:把 EMP 表的数据快速变成 160000

mysql> create table tmp like EMP;    -- 为了做测试,创建一个tmp表
Query OK, 0 rows affected (0.06 sec)

mysql> insert into tmp select * from EMP;  -- 将EMP表中的数据插入tmp表中
Query OK, 14 rows affected (0.03 sec) 
Records: 14  Duplicates: 0  Warnings: 0

mysql> insert into tmp select * from tmp;   -- 自我复制,重复多次,每次数量增加一倍

mysql> select count(*) from tmp;  -- 快速的创建了20多万条记录
+----------+
| count(*) |
+----------+
|   229376 |
+----------+
1 row in set (0.20 sec)

删除表中重复的数据

创建一张表用于测试:

mysql> create table tt(id int, name varchar(20));
Query OK, 0 rows affected (0.06 sec)

-- 省略插入数据语句

mysql> select * from tt;
+------+------+
| id   | name |
+------+------+
|  100 | aaa  |
|  100 | aaa  |
|  200 | bbb  |
|  200 | bbb  |
|  300 | ccc  |
+------+------+
5 rows in set (0.00 sec)

要求:重复的数据只能有一份

思路:

1. 创建一张空表tmp_tt,空表的结构和tt一样
mysql> create table tmp_tt like tt;
2. 将tt表进行distinct,把数据导入空表tmp_tt
mysql> insert into tmp_tt select distinct * from tt;
3. 删除tt表
mysql> drop table tt;
4. 将tmp_tt改名成tt
mysql> alter table tmp_tt rename tt;

mysql> select * from tt;
+------+------+
| id   | name |
+------+------+
|  100 | aaa  |
|  200 | bbb  |
|  300 | ccc  |
+------+------+
3 rows in set (0.00 sec)

合并查询

在实际应用中,为了合并多个select的执行结果,可以使用集合操作符union,union all
1. union
该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行。
示例:将工资大于2500和职位是MANAGER的人找出来

mysql> select ename, sal, job from EMP where sal>25000 union
    -> select ename, sal, job from EMP where job='MANAGER';
+-------+---------+---------+
| ename | sal     | job     |
+-------+---------+---------+
| JONES | 2975.00 | MANAGER |
| BLAKE | 2850.00 | MANAGER |
| CLARK | 2450.00 | MANAGER |
+-------+---------+---------+
3 rows in set (0.00 sec)

2. union all
该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。

mysql> select ename, sal, job from EMP where sal>2500 union all
-> select ename, sal, job from EMP where job='MANAGER';
+-------+---------+-----------+
| ename | sal     | job       |
+-------+---------+-----------+
| JONES | 2975.00 | MANAGER   |
| BLAKE | 2850.00 | MANAGER   |
| SCOTT | 3000.00 | ANALYST   |
| KING  | 5000.00 | PRESIDENT |
| FORD  | 3000.00 | ANALYST   |
| JONES | 2975.00 | MANAGER   |
| BLAKE | 2850.00 | MANAGER   |
| CLARK | 2450.00 | MANAGER   |
+-------+---------+-----------+

外键

外键用于定义主表和从表之间的关系:外键约束主要定义在从表上,主表则必须是有主键约束或unique约束。当定义外键后,要求外键列数据必须在主表的主键列存在或为null。

语法:

foreign key (字段名) references 主表(列)

对上面的示意图进行设计:
先创建主键表:

create table myclass (
id int primary key,
name varchar(30) not null comment'班级名'
);

 再创建从表:

create table stu (
id int primary key,
name varchar(30) not null comment '学生名',
class_id int,
foreign key (class_id) references myclass(id)
);

正常插入数据:

mysql> insert into myclass values(10, '计算机1班'),(20, '计算机2班');
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into stu values(100, '张三', 10),(101, '李四', 20);
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

我们现在要插入一个班级号为30的学生,因为没有这个班级,所以插入不成功

mysql> insert into stu values(102, '王五',30);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails 
(`scott`.`stu`, CONSTRAINT `stu_ibfk_1` FOREIGN KEY (`class_id`) REFERENCES `myclass` (`id`))

但是可以让班级id为null,比如来了一个学生,目前还没有分配班级

mysql> insert into stu values(102, '王五', null);
Query OK, 1 row affected (0.01 sec)

综合案例

有一个商店的数据,记录客户及购物情况,有以下三个表组成:
1. 商品goods(商品编号goods_ id,商品名goods_ name, 单价unitprice, 商品类别category, 供应商provider)
2. 客户customer(客户号customer_ id,姓名name,住址address,邮箱email,性别sex,身份证card_id)
3. 购买purchase(购买订单号order_ id,客户号customer_ id,商品号goods_ id,购买数量nums)

要求:

  • 每个表的主外键
  • 客户的姓名不能为空值
  • 邮箱不能重复
  • 客户的性别(男,女)
-- 创建goods表
drop table if exists goods;
create table if not exists goods(
	goods_id int unsigned primary key auto_increment comment '商品编号',
	goods_name varchar(100) not null default '' comment '商品名称',
	unitprice decimal(10,2) not null default 0.0 comment '单价',
	category smallint not null default 0 comment '商品类别',
	provider varchar(100) not null default '' comment '供应商'
);

-- 创建customer
drop table if exists customer;
create table if not exists customer (
    customer_id int UNSIGNED primary key AUTO_INCREMENT comment '客户编号',
    name varchar(50) not null default '' comment '姓名',
    address varchar(100) not null default '' comment '地址',
    email varchar(60) not null unique comment '邮箱',
    sex enum('男', '女') not null default '男' comment '性别',
    card_id varchar(20) not null unique comment '身份证'
);

-- 创建purchase
drop table if exists purchase;
create table if not exists purchase (
    order_id varchar(30) not null primary key comment '购买订单编号',
    customer_id int UNSIGNED comment '客户编号',
    goods_id int UNSIGNED  comment '产品编号',
    nums int not null default 0 comment '购买数量',
    foreign key (customer_id) references customer (customer_id),
    foreign key (goods_id)    references goods (goods_id)
);

猜你喜欢

转载自blog.csdn.net/yubujian_l/article/details/81590024