6210 字
31 分钟
MySQL笔记
2025-04-13

MySQL#

MySQL表查询#

在实际开发的过程中,我们需要用到的查询不只有前面的简单查询,我们还需要学习一些更为符合实际的查询方式,如日期查询,多表查询等

相关表格#

我们先来建立几个表格,填入响应数据

员工表emp#

CREATE TABLE emp (
	`empno` INT,
	`ename` VARCHAR ( 30 ),
	`job` VARCHAR ( 20 ),
	`mgr` INT,
	`hiredate` DATE,
	`sal` DOUBLE,
	`comm` DOUBLE,
`deptno` INT 
);
INSERT INTO emp
VALUES
	( 7396, 'SMITH', 'CLERK', 7902, '1990-12-17', 800.00, NULL, 20 ),
	( 7499, 'ALLEN', 'SALESMAN', 7902, '1991-02-20', 1600.00, 300.00, 30 ),
	( 7596, 'WARD', 'SALESMAN', 7896, '1993-02-07', 1250.00, 500.00, 30 ),
	( 7696, 'JOOES', 'MANAGER', 7839, '1980-02-01', 2975.00, NULL, 20 ),
	( 7796, 'MARTIN', 'SALESMAN', 7698, '1999-04-17', 1250.00, 1400.00, 30 ),
	( 7896, 'BLAKE', 'MANAGER', 7839, '1990-12-17', 2850.00, NULL, 30 );

工作部门表dept#

CREATE TABLE dept ( 
`deptno` INT, 
`dname` VARCHAR ( 30 ), 
`loc` VARCHAR ( 30 ) 
);
INSERT INTO dept
VALUES
	( 10, 'ACCOUNTING', 'NEW YORK' ),
	( 20, 'RESEARCH', 'DALLAS' ),
	( 30, 'SALES', 'CHICAGO' ),
	( 40, 'OPERATIONS', 'BOSTON' );

薪水表格salgrade#

CREATE TABLE salgrade ( 
    `grade` INT, 
    `losal` DOUBLE, 
    `hisal` DOUBLE 
);
INSERT INTO salgrade
VALUES
	( 1, 700.00, 1200.00 ),
	( 2, 1200.00, 1400.00 ),
	( 3, 1400.00, 3000.00 ),
	( 4, 3000.00, 9000.00 );

查询加强#

where筛选日期#

查找1992.1.1后入职的员工

需要至于格式01-01

select ename,hiredate from emp where hiredate > '1992-01-01';
+--------+------------+
| ename  | hiredate   |
+--------+------------+
| WARD   | 1993-02-07 |
| MARTIN | 1999-04-17 |
+--------+------------+

like模糊查询#

​ %:表示0到多个任意字符

​ _ :表示单个任意字符

如何显示首字符为s的员工共姓名和工资

select ename,sal from emp where ename like 's%';
+-------+-----+
| ename | sal |
+-------+-----+
| SMITH | 800 |
+-------+-----+

如何显示第三个字符为大写o的所有员工姓名和工资

select ename,sal from emp where ename like '__o%';
+-------+------+
| ename | sal  |
+-------+------+
| JOOES | 2975 |
+-------+------+

order by排序#

从工资低到高显示员工

select * from emp order by sal asc;
+-------+--------+----------+------+------------+------+------+--------+
| empno | ename  | job      | mgr  | hiredate   | sal  | comm | deptno |
+-------+--------+----------+------+------------+------+------+--------+
|  7396 | SMITH  | CLERK    | 7902 | 1990-12-17 |  800 | NULL |     20 |
|  7596 | WARD   | SALESMAN | 7902 | 1993-02-07 | 1250 |  500 |     30 |
|  7796 | MARTIN | SALESMAN | 7698 | 1999-04-17 | 1250 | 1400 |     30 |
|  7499 | ALLEN  | SALESMAN | 7902 | 1991-02-20 | 1600 |  300 |     30 |
|  7896 | BLAKE  | MANAGER  | 7839 | 1990-12-17 | 2850 | NULL |     30 |
|  7696 | JOOES  | MANAGER  | 7839 | 1980-02-01 | 2975 | NULL |     20 |
+-------+--------+----------+------+------------+------+------+--------+

按照部门编号升序,员工工资降序排序(中间用,隔开)

select * from emp order by deptno asc , sal desc;
+-------+--------+----------+------+------------+------+------+--------+
| empno | ename  | job      | mgr  | hiredate   | sal  | comm | deptno |
+-------+--------+----------+------+------------+------+------+--------+
|  7696 | JOOES  | MANAGER  | 7839 | 1980-02-01 | 2975 | NULL |     20 |
|  7396 | SMITH  | CLERK    | 7902 | 1990-12-17 |  800 | NULL |     20 |
|  7896 | BLAKE  | MANAGER  | 7839 | 1990-12-17 | 2850 | NULL |     30 |
|  7499 | ALLEN  | SALESMAN | 7902 | 1991-02-20 | 1600 |  300 |     30 |
|  7596 | WARD   | SALESMAN | 7902 | 1993-02-07 | 1250 |  500 |     30 |
|  7796 | MARTIN | SALESMAN | 7698 | 1999-04-17 | 1250 | 1400 |     30 |
+-------+--------+----------+------+------------+------+------+--------+

分页查询#

在实际开发中我们数据量往往很大,我们会用到分页来进行查询,这里介绍一个关键字limit

基本语法

select ... limit start, rows

表示从start + 1 行开始取,取出rows行,例如上述的例子中,我们需要取出前5行结果使用如下的语句即可,从第1行开始取5行

select * from emp order by deptno asc , sal desc limit 0, 5;

我们是实际中使用我们就可以运用limit限制每一次查询的范围

要将一个大表范围若干个小表可以这么做

select * from emp order by deptno asc , sal desc limit 0, 4;
select * from emp order by deptno asc , sal desc limit 4, 4;
select * from emp order by deptno asc , sal desc limit 8, 4;
...

group by分组#

我们有如下的几个题目

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

select count(*), avg(sal), job from emp group by job;

2.显示雇员的总数,以及获得补助的雇员数

这里输入comm,comm字段为空则不参与统计

select count(*), count(comm) from emp;

如果我们扩展一下

没有获得补助的员工

select count(*), count(*)-count(comm) from emp;
select count(*), count(if(comm is null, 1, null)) from emp;

3.显示管理者的总人数

先count再去重

select count(distinct mgr) from emp;

4.显示雇员工资的最大差值

select max(sal)-min(sal) from emp;

多子句查询#

顺序:如果一个select语句同时包含了group by,having,limit,order by,那么它们的顺序应该为:group by,having,order by,limit

例如统计各个部门的平均工资,并且是大于1000的,按照平均工资从高到低排序,取出前两行记录

select avg(sal),deptno from emp 
group by deptno 
having avg(sal)>1000 
order by avg(sal) desc 
limit 0, 2;

其实更建议使用as重命名,可以增快速度

select avg(sal) as asql, deptno from emp 
group by deptno 
having asql>1000 
order by asql desc 
limit 0, 2;
+----------+--------+
| avg(sal) | deptno |
+----------+--------+
|   1887.5 |     20 |
|   1737.5 |     30 |
+----------+--------+

多表查询#

多表查询是指基于两个和两个以上的表的查询,在实际的应用中,查询单个表可能不能满足需求

例如说显示雇员名,雇员工资,所在部门的名字,就需要员工表和工作部门表

多表笛卡尔集#

select * from emp, dept;

可见笛卡尔集需要将两种表做笛卡尔积(在不加条件的结果非常夸张)

+-------+--------+----------+------+------------+------+------+--------+--------+------------+----------+
| empno | ename  | job      | mgr  | hiredate   | sal  | comm | deptno | deptno | dname      | loc      |
+-------+--------+----------+------+------------+------+------+--------+--------+------------+----------+
|  7396 | SMITH  | CLERK    | 7902 | 1990-12-17 |  800 | NULL |     20 |     40 | OPERATIONS | BOSTON   |
|  7396 | SMITH  | CLERK    | 7902 | 1990-12-17 |  800 | NULL |     20 |     30 | SALES      | CHICAGO  |
|  7396 | SMITH  | CLERK    | 7902 | 1990-12-17 |  800 | NULL |     20 |     20 | RESEARCH   | DALLAS   |
|  7396 | SMITH  | CLERK    | 7902 | 1990-12-17 |  800 | NULL |     20 |     10 | ACCOUNTING | NEW YORK |
|  7499 | ALLEN  | SALESMAN | 7902 | 1991-02-20 | 1600 |  300 |     30 |     40 | OPERATIONS | BOSTON   |
|  7499 | ALLEN  | SALESMAN | 7902 | 1991-02-20 | 1600 |  300 |     30 |     30 | SALES      | CHICAGO  |
|  7499 | ALLEN  | SALESMAN | 7902 | 1991-02-20 | 1600 |  300 |     30 |     20 | RESEARCH   | DALLAS   |
|  7499 | ALLEN  | SALESMAN | 7902 | 1991-02-20 | 1600 |  300 |     30 |     10 | ACCOUNTING | NEW YORK |
|  7596 | WARD   | SALESMAN | 7902 | 1993-02-07 | 1250 |  500 |     30 |     40 | OPERATIONS | BOSTON   |
|  7596 | WARD   | SALESMAN | 7902 | 1993-02-07 | 1250 |  500 |     30 |     30 | SALES      | CHICAGO  |
|  7596 | WARD   | SALESMAN | 7902 | 1993-02-07 | 1250 |  500 |     30 |     20 | RESEARCH   | DALLAS   |
|  7596 | WARD   | SALESMAN | 7902 | 1993-02-07 | 1250 |  500 |     30 |     10 | ACCOUNTING | NEW YORK |
|  7696 | JOOES  | MANAGER  | 7839 | 1980-02-01 | 2975 |  100 |     20 |     40 | OPERATIONS | BOSTON   |
|  7696 | JOOES  | MANAGER  | 7839 | 1980-02-01 | 2975 |  100 |     20 |     30 | SALES      | CHICAGO  |
|  7696 | JOOES  | MANAGER  | 7839 | 1980-02-01 | 2975 |  100 |     20 |     20 | RESEARCH   | DALLAS   |
|  7696 | JOOES  | MANAGER  | 7839 | 1980-02-01 | 2975 |  100 |     20 |     10 | ACCOUNTING | NEW YORK |
|  7796 | MARTIN | SALESMAN | 7698 | 1999-04-17 | 1250 | 1400 |     30 |     40 | OPERATIONS | BOSTON   |
|  7796 | MARTIN | SALESMAN | 7698 | 1999-04-17 | 1250 | 1400 |     30 |     30 | SALES      | CHICAGO  |
|  7796 | MARTIN | SALESMAN | 7698 | 1999-04-17 | 1250 | 1400 |     30 |     20 | RESEARCH   | DALLAS   |
|  7796 | MARTIN | SALESMAN | 7698 | 1999-04-17 | 1250 | 1400 |     30 |     10 | ACCOUNTING | NEW YORK |
|  7896 | BLAKE  | MANAGER  | 7839 | 1990-12-17 | 2850 | NULL |     30 |     40 | OPERATIONS | BOSTON   |
|  7896 | BLAKE  | MANAGER  | 7839 | 1990-12-17 | 2850 | NULL |     30 |     30 | SALES      | CHICAGO  |
|  7896 | BLAKE  | MANAGER  | 7839 | 1990-12-17 | 2850 | NULL |     30 |     20 | RESEARCH   | DALLAS   |
|  7896 | BLAKE  | MANAGER  | 7839 | 1990-12-17 | 2850 | NULL |     30 |     10 | ACCOUNTING | NEW YORK |
+-------+--------+----------+------+------------+------+------+--------+--------+------------+----------+

当不加限制的情况下,两张表的查询规则:

把第一张表的每一条记录和第二张表的每一条记录组合,返回结果是两张表的所有列,一共返回的记录数是第一张表的行数和第二张表的行数的乘积,而组成的表称为笛卡尔集

我们需要使用where对于笛卡尔集进行过滤,需要进行分析

当两张表的deptno编号相等的时候才是我们需要的结果

select * from emp, dept where emp.deptno=dept.deptno;
+-------+--------+----------+------+------------+------+------+--------+--------+----------+---------+
| empno | ename  | job      | mgr  | hiredate   | sal  | comm | deptno | deptno | dname    | loc     |
+-------+--------+----------+------+------------+------+------+--------+--------+----------+---------+
|  7396 | SMITH  | CLERK    | 7902 | 1990-12-17 |  800 | NULL |     20 |     20 | RESEARCH | DALLAS  |
|  7499 | ALLEN  | SALESMAN | 7902 | 1991-02-20 | 1600 |  300 |     30 |     30 | SALES    | CHICAGO |
|  7596 | WARD   | SALESMAN | 7902 | 1993-02-07 | 1250 |  500 |     30 |     30 | SALES    | CHICAGO |
|  7696 | JOOES  | MANAGER  | 7839 | 1980-02-01 | 2975 |  100 |     20 |     20 | RESEARCH | DALLAS  |
|  7796 | MARTIN | SALESMAN | 7698 | 1999-04-17 | 1250 | 1400 |     30 |     30 | SALES    | CHICAGO |
|  7896 | BLAKE  | MANAGER  | 7839 | 1990-12-17 | 2850 | NULL |     30 |     30 | SALES    | CHICAGO |
+-------+--------+----------+------+------------+------+------+--------+--------+----------+---------+

笛卡尔例题#

显示雇员名,雇员工资,所在部门的名字

select ename,sal,dname from emp, dept where emp.deptno=dept.deptno;
+--------+------+----------+
| ename  | sal  | dname    |
+--------+------+----------+
| SMITH  |  800 | RESEARCH |
| ALLEN  | 1600 | SALES    |
| WARD   | 1250 | SALES    |
| JOOES  | 2975 | RESEARCH |
| MARTIN | 1250 | SALES    |
| BLAKE  | 2850 | SALES    |
+--------+------+----------+

而在选取重复列的时候,需要指定是哪一个表的列

select ename,sal,dname,emp.deptno from emp, dept where emp.deptno=dept.deptno;
+--------+------+----------+--------+
| ename  | sal  | dname    | deptno |
+--------+------+----------+--------+
| SMITH  |  800 | RESEARCH |     20 |
| ALLEN  | 1600 | SALES    |     30 |
| WARD   | 1250 | SALES    |     30 |
| JOOES  | 2975 | RESEARCH |     20 |
| MARTIN | 1250 | SALES    |     30 |
| BLAKE  | 2850 | SALES    |     30 |
+--------+------+----------+--------+

显示部门编号为20的部门名称,员工名,工资

select ename,sal,dname,emp.deptno 
from emp,dept 
where emp.deptno = dept.deptno 
and emp.deptno = 20;
+-------+------+----------+--------+
| ename | sal  | dname    | deptno |
+-------+------+----------+--------+
| SMITH |  800 | RESEARCH |     20 |
| JOOES | 2975 | RESEARCH |     20 |
+-------+------+----------+--------+

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

我们先选好需要的表格是emp表和salgrade表,我们这一道题的要点在于过滤条件该如何筛选

select ename,sal,grade from emp, salgrade;
+--------+------+-------+
| ename  | sal  | grade |
+--------+------+-------+
| SMITH  |  800 |     4 |
| SMITH  |  800 |     3 |
| SMITH  |  800 |     2 |
| SMITH  |  800 |     1 |
| ALLEN  | 1600 |     4 |
| ALLEN  | 1600 |     3 |
| ALLEN  | 1600 |     2 |
| ALLEN  | 1600 |     1 |
| WARD   | 1250 |     4 |
| WARD   | 1250 |     3 |
| WARD   | 1250 |     2 |
| WARD   | 1250 |     1 |
| JOOES  | 2975 |     4 |
| JOOES  | 2975 |     3 |
| JOOES  | 2975 |     2 |
| JOOES  | 2975 |     1 |
| MARTIN | 1250 |     4 |
| MARTIN | 1250 |     3 |
| MARTIN | 1250 |     2 |
| MARTIN | 1250 |     1 |
| BLAKE  | 2850 |     4 |
| BLAKE  | 2850 |     3 |
| BLAKE  | 2850 |     2 |
| BLAKE  | 2850 |     1 |
+--------+------+-------+

运用between and语句即可完成,判断sal位于最高工资和最低工资之间即可

select ename,sal,grade from emp, salgrade 
where sal between losal and hisal;
+--------+------+-------+
| ename  | sal  | grade |
+--------+------+-------+
| SMITH  |  800 |     1 |
| ALLEN  | 1600 |     3 |
| WARD   | 1250 |     2 |
| JOOES  | 2975 |     3 |
| MARTIN | 1250 |     2 |
| BLAKE  | 2850 |     3 |
+--------+------+-------+

自连接#

是指的是同一张表的连接查询

显示公司员工和它上级的名字

这个时候我们就需要使用到表的自连接,将员工和上级放在同一行中筛选完成(还需要有一个别名)

select * from emp empee, emp boss;

这样就可以将同一张表连接起来,我们再去写过滤条件

select empee.ename,boss.ename  
from emp empee, emp boss 
where empee.mgr = boss.empno;
+-------+-------+
| ename | ename |
+-------+-------+
| WARD  | BLAKE |
+-------+-------+

子查询#

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

单行子查询:只返回一行数据的子查询语句

多行子查询:返回多行数据的子查询语句(in)

单行子查询例题

如何显示于SMITH同一部门的所有员工

我们首先需要得到SMITH的部门号,再通过部门号筛选

select deptno from emp where ename = 'SMITH';//20
select * from emp where deptno = 20;

我们可以把第一个查询嵌入

select * from emp 
where deptno = (
	select deptno 
    from emp 
    where ename = 'SMITH'
);

即可完成

多行子查询例题

查询和部门20的工作相同的雇员的名字、岗位、部门号,但是不包含20自己的

第一步:查询20号部门有哪一些工作岗位(还需要去重)

select distinct job from emp where deptno = 20;
+---------+
| job     |
+---------+
| CLERK   |
| MANAGER |
+---------+

第二步:运用job的工作字段进行查询筛选

select ename,job,sal,deptno from emp 
where job in('CLERK','MANAGER');

第三步:不包含20号部门

select ename,job,sal,deptno from emp 
where job in('CLERK','MANAGER') and deptno<>20;

综合嵌套一下

select ename,job,sal,deptno from emp 
where job in(
	select distinct job 
	from emp 
	where deptno = 20) 
and deptno<>20;

子查询临时表#

查询ecshop中各个类别中,价格最高的商品

先得到各个类别中价格最高的商品

select cat_id, max(shop_price) from ecsshop
group by cat_id;

我们可以将这个表格和ecsshop表进行连接,在设置过滤条件即可完成要求

select ecshop.cat_id, ecshop.goods_name 
from ecshop,(
select cat_id, max(shop_price) as max_price
from ecshop
group by cat_id)temp
where ecshop.cat_id=temp.cat_id 
and ecshop.shop_price=temp.max_price;

all和any操作符#

all

所有的字段都满足

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

select ename,sal,deptno from emp 
where sal>
all(
    select sal 
    from emp 
    where deptno = 30);

也可以运用max查询最大的薪水,再通过判断条件筛选

select ename,sal,deptno from emp 
where sal>
(select max(sal) 
 from emp 
 where deptno = 30);

any

有一个字段满足条件即可

select ename,sal,deptno from emp 
where sal>
any(
    select sal 
    from emp 
    where deptno = 30);

也可以改为

select ename,sal,deptno from emp 
where sal>
(select min(sal) 
 from emp 
 where deptno = 30);

多列子查询#

多列子查询指的是查询返回多个列数据的子查询语句

例如查询于SMITH部门,岗位完全相同的所有雇员

第一步:查询SMITH所在的部门以及岗位

select deptno,job from emp where ename='SMITH';
+--------+-------+
| deptno | job   |
+--------+-------+
|     20 | CLERK |
+--------+-------+

第二步:将上述的查询结果当作查询使用

select ename, deptno, job from emp 
where deptno=20 
and job='CLERK'
and ename<>'SMITH';

综合嵌套(运用()来进行相应的字段匹配)

select ename, deptno, job from emp 
where (deptno, job) = (
	select deptno,job from emp 
    where ename='SMITH'
)
and ename<>'SMITH';

相关例题#

例题一:

查找每个部门工资高于本部门平均工资人的资料

第一步:查询部门的平均工资

select deptno, avg(sal) from emp group by deptno;

第二步:将查询的结果表和员工表进行拼接,再进行筛选

(这里的emp.*代表选出emp表的所有字段)

select emp.* from emp,(
	select deptno, avg(sal)as avg_sal
	from emp group by deptno
)temp where emp.deptno=temp.deptno 
and emp.sal>temp.avg_sal;

例题二:

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

第一步:查询部门的最高工资

select deptno, max(sal) from emp group by deptno;

第二步:将查询的结果表和员工表进行拼接,再进行筛选

select emp.* from emp,(
	select deptno, max(sal)as avg_sal
	from emp group by deptno
)temp where emp.deptno=temp.deptno 
and emp.sal=temp.avg_sal;

例题三:

查询每个部门的信息(包括:部门名,编号,地址)和人员数量

思路:我们的信息来自于dept表,而人员数量需要构建临时表格

select count(*),deptno from emp group by deptno;
+----------+--------+
| count(*) | deptno |
+----------+--------+
|        2 |     20 |
|        4 |     30 |
+----------+--------+

和部门信息表格拼接

select dept.*, countp 
from dept,(
	select deptno,count(*)as countp 
	from emp 
    group by deptno)temp 
where dept.deptno=temp.deptno;
+--------+----------+---------+--------+
| deptno | dname    | loc     | countp |
+--------+----------+---------+--------+
|     20 | RESEARCH | DALLAS  |      2 |
|     30 | SALES    | CHICAGO |      4 |
+--------+----------+---------+--------+

表复制#

自我复制数据#

有的时候,为了对于某个sql语句进行效率测试,我们需要海量的数据,可以使用此法为表创建海量的数据

例如创建这一张表

create table my_table(	
	id int,
    `name` varchar(32),
	sal double,
	job varchar(32),
	deptno int
	);

我们先将emp表中的内容复制到创建的新表中

insert into my_table(id,`name`,sal,job,deptno)
select empno,ename,sal,job,deptno from emp;

自我复制:每一次都会将表中的内容复制添加到自己的表中

insert into my_table
select * from my_table;

去除表中的重复数据#

可以通过like创建一个新表,格式和旧表一样

create table table2 like table1

思路:

1.创建一张临时表mytemp,该表数据结构和my_table一样

2.把my_temp的记录通过distinct关键字处理复制

3.清除掉原先表my_table的记录

4.将my_temp的记录复制到my_table

5.drop删除临时表my_temp

create table my_temp like my_table;

insert into my_temp
select distinct * from my_table;

delete from my_table;

insert into my_table
select * from my_temp;

drop table my_temp;

或者你直接删除原先的my_table表,再将my_temp表格更改名称为my_table

rename table my_temp to my_table;

合并查询#

union#

在实际使用的时候,有的时候我们需要合并多个select语句查询到的结果,我们会用到union操作

select... union select...;

例如我们查询职业名称为clerk和部门号为20的并集

select * from emp where job='clerk' 
union select * from emp where deptno=20;

union all#

如果你添加了all,则不会去除重复内容

select * from emp where job='clerk' 
union all select * from emp where deptno=20;

外连接#

问题引出#

还记得之前我们的多表查询嘛,我们在合并表的时候会求多表的笛卡尔集

我们有一个要求如下:列出部门名称和这些部门的员工名称和工作,同时要求显示出那些没有员工的部门名

我们如果用笛卡尔积的话查询结果如下

select dname,ename,job 
from emp,dept 
where emp.deptno=dept.deptno;
+----------+--------+----------+
| dname    | ename  | job      |
+----------+--------+----------+
| RESEARCH | SMITH  | CLERK    |
| RESEARCH | JOOES  | MANAGER  |
| SALES    | ALLEN  | SALESMAN |
| SALES    | WARD   | SALESMAN |
| SALES    | MARTIN | SALESMAN |
| SALES    | BLAKE  | MANAGER  |
+----------+--------+----------+

但是事实上我们的部门中还有其他的部门没有显示出来

+--------+------------+----------+
| deptno | dname      | loc      |
+--------+------------+----------+
|     10 | ACCOUNTING | NEW YORK |
|     20 | RESEARCH   | DALLAS   |
|     30 | SALES      | CHICAGO  |
|     40 | OPERATIONS | BOSTON   |
+--------+------------+----------+

这是因为员工表的部门号没有10、40无法匹配到部门表,我们要实现显示出那些没有员工的部门,我们需要外连接,我们创建如下的表作演示

CREATE TABLE stu (
`id` INT,
`name` VARCHAR ( 32 ));

INSERT INTO stu
VALUES
	( 1, 'Jack' ),
	( 2, 'Tom' ),
	( 3, 'Kity' ),
	( 4, 'nono' );
CREATE TABLE exam (
`id` INT,
`grade` INT);

INSERT INTO exam
VALUES
	( 1, 56 ),
	( 2, 76 ),
	( 11, 8 );

用where条件筛选后

select * from stu,exam where stu.id=exam.id;
+----+------+----+-------+
| id | name | id | grade |
+----+------+----+-------+
|  1 | Jack |  1 |    56 |
|  2 | Tom  |  2 |    76 |
+----+------+----+-------+

可见是没有11号id和4号id的行记录的,因为id不是左右两表都有

左外连接#

左侧的表完全显示(如果没有成绩也要显示姓名与id)

select * from stu left join exam on stu.id=exam.id;
+----+------+------+-------+
| id | name | id   | grade |
+----+------+------+-------+
|  1 | Jack |    1 |    56 |
|  2 | Tom  |    2 |    76 |
|  3 | Kity | NULL | NULL  |
|  4 | nono | NULL | NULL  |
+----+------+------+-------+

右外连接#

又侧的表完全显示(如果没有姓名也要显示成绩与id)

select * from stu right join exam on stu.id=exam.id;
+------+------+----+-------+
| id   | name | id | grade |
+------+------+----+-------+
|    1 | Jack |  1 |    56 |
|    2 | Tom  |  2 |    76 |
| NULL | NULL | 11 |     8 |
+------+------+----+-------+

例题#

回到我们的例题:列出部门名称和这些部门的员工名称和工作,同时要求显示出那些没有员工的部门名

左外连接实现:

select dept.deptno,dname,ename,job 
from dept left join emp 
on dept.deptno=emp.deptno; 
+--------+------------+--------+----------+
| deptno | dname      | ename  | job      |
+--------+------------+--------+----------+
|     10 | ACCOUNTING | NULL   | NULL     |
|     20 | RESEARCH   | JOOES  | MANAGER  |
|     20 | RESEARCH   | SMITH  | CLERK    |
|     30 | SALES      | BLAKE  | MANAGER  |
|     30 | SALES      | MARTIN | SALESMAN |
|     30 | SALES      | WARD   | SALESMAN |
|     30 | SALES      | ALLEN  | SALESMAN |
|     40 | OPERATIONS | NULL   | NULL     |
+--------+------------+--------+----------+

右外连接实现:

select dept.deptno,dname,ename,job 
from emp right join dept 
on dept.deptno=emp.deptno; 
+--------+------------+--------+----------+
| deptno | dname      | ename  | job      |
+--------+------------+--------+----------+
|     10 | ACCOUNTING | NULL   | NULL     |
|     20 | RESEARCH   | JOOES  | MANAGER  |
|     20 | RESEARCH   | SMITH  | CLERK    |
|     30 | SALES      | BLAKE  | MANAGER  |
|     30 | SALES      | MARTIN | SALESMAN |
|     30 | SALES      | WARD   | SALESMAN |
|     30 | SALES      | ALLEN  | SALESMAN |
|     40 | OPERATIONS | NULL   | NULL     |
+--------+------------+--------+----------+

MySQL中的约束#

何为约束?

约束是用于确保数据库数据满足特定的商业规则,而再mysql中约束包括not null、unique、primary key、foreign key、check五种

primary key#

primary key:用于唯一的标示表行的数据,当定义主键约束之后,该列不可以重复,以下是一个案例

创建一个主键为id表,向其中插入数据

CREATE TABLE primary_test (
	`id` INT PRIMARY KEY,
	`name` VARCHAR ( 32 ),
	`email` VARCHAR ( 32 ) 
)
insert into primary_test values
(1,'jack','jack@qq.com'),
(2,'marry','marry@gmail.com'),
(3,'lory','lory@163.com');

我们现在加一条语句,id为1(之前加过id为1的记录)

insert into primary_test values(1,'jerry','jerry@qq.com');

报错

> 1062 - Duplicate entry '1' for key 'primary_test.PRIMARY'

主键的细节

1.一张表最多只能有一个主键,但可以是复合主键

CREATE TABLE primary_test2 (
	`id` INT ,
	`name` VARCHAR ( 32 ),
	`email` VARCHAR ( 32 ) ,
	 primary key(`id`,`name`)
) ;

2.主键的指定方式有两种,第一种是直接在字段名 类型后加上primary key,第二种是在表的定义最后加上primary key(列名)

CREATE TABLE primary_test4 (
	`id` INT ,
	`name` VARCHAR ( 32 ),
	`email` VARCHAR ( 32 ) ,
	 primary key(`id`)
) ;

3.使用desc可以查看主键情况

+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   | PRI | NULL    |       |
| name  | varchar(32) | YES  |     | NULL    |       |
| email | varchar(32) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+

4.主键不可以重复且不能为空

not null#

非空约束,当列上定义了not null,那么插入数据的时候必须要提供数据

unique#

唯一约束

1.当定义了唯一约束的时候,该列的值不可以重复

2.一张表可以有多个unique字段

3.当unique没有添加非空约束,则unique字段可以有多个null

foreign key#

外键

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

举个栗子:

例如我们有一个学生表其中有id和class_id,还有一个班级表,有属性id,如果我们要求,每个学生所在的班级号class_id是存在于班级表的,就可以把class_id做成外键约束(班级表为主表,而学生表为从表)

当我们插入一个学生它的班级编号不存在于班级表,则插入失败

当我们学生表有一个学生他的班级编号存在班级表,则删除对应的班级表id会删除失败

班级表(主表)

CREATE TABLE my_class (
	`id` INT primary key,
	`name` VARCHAR ( 32 ),
	`add` VARCHAR ( 32 ) 
) ;

学生表(从表)

CREATE TABLE my_stu (
	`id` INT primary key,
	`name` VARCHAR (32) NOT NULL,
	`class_id` int,
	foreign key(class_id) references my_class(id)
) ;

外键细节

1.外键指向的表字段,要求为primary key或者是unique

2.表的类型是innodb,这样的表才支持外键

3.外键字段的类型要求和主键字段类型一致

4.外键字段的值必须要求在主键字段中出现过,或者为null

5.一旦建立主外键的关系,数据就不可以随意删除了

check#

用于强制行数据必须满足条件,注意在mysql 5.7,这里插入不符合check的数据会成功(只是会提示)8.0是会生效的

假设sal列上定义了check约束,并要求sql列值在1000-2000之间,性别为两个,那么如果插入的记录如果不满足相关条件就会提示报错

CREATE TABLE test (
	`id` INT primary key,
	`name` VARCHAR (32) NOT NULL,
	`sex` VARCHAR(10) CHECK (`sex` IN('man','woman')),
	`sal` double CHECK (`sal`>1000 and `sal`<2000)
) ;

表设计#

现有一个商店的数据库shop_database,记录客户以及其购物情况,有以下的三个表组成

goods商品表:

商品号goods_id 商品名称goods_name 价格price 商品类别category 供货商provider

customer顾客表:

客户号customer_id 姓名name 住址address 邮箱email 性别sex 身份证id

purchase购买表:

订单号order_id 客户号customer_id 商品号goods_id 购买数量nums

要求如下:

1.合理定义主键、外键

2.客户姓名不可以为空值

3.电子邮箱不可以为重复

4.客户的性别约束‘男’|‘女’

5.价格在1.0——9999.99之间

goods

create table goods(
goods_id int primary key,
goods_name varchar(30) not null default '',
price decimal(10,2)not null 
    check(price between 1.0 and 9999.99),
category int not null default '',
provider varchar(30)
);

customer

create table customer(
customer_id int primary key,
name varchar(30) not null,
address varchar(30),
email varchar(30),
sex varchar(10) check(sex IN('男','女')),
id decimal(20,0) unique
);

purchase

create table purchase(
order_id int primary key,
customer_id int not null,
goods_id int not null,
nums int,
FOREIGN key(customer_id) REFERENCES customer(customer_id),
FOREIGN key(goods_id) REFERENCES goods(goods_id)
);