文章目录
一、数据库1
注:1.练习的数据库中有三个表:分别是dept(部门表),emp(员工表)和salgrade(薪资等级表)
2.dept表中内容如下:
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
4 rows in set (0.00 sec)
emp表如下:
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
14 rows in set (0.00 sec)
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)
1.1 数据库管理系统
数据库管理系统:DataBsaeManagement,简称DBMS。数据库管理系统是专门用来管理数据库数据的,数据库管理系统可以对数据库当中的数据进行增删改查。
1.2 SQL
SQL:结构化查询语言,通过编写SQL语句,然后DBMS负责执行SQL语句,最终完成数据库中数据的增删改查的操作。
注:SQL不区分大小写
1.2.1 语句分类
DQL:数据查询语言(凡是带有select关键字的都是查询语句)
DML:数据操作语言(凡是对表中的数据进行增删改查的都是的都是DML)
DDL:数据定义语言(凡是带有create、drop、alter的都是DDL)
DDL主要操作的是表的结构。不是表中的数据。
TCL:事务控制语言(包括事务提交:commit;事务回滚:rollback)
DCL:数据控制语言(授权:grant;撤销授权:revoke)
2 MySQL命令
退出MySQL:exit
查看MySQL中的数据库:show database;(注意以分号结尾)
使用数据库:use test; (使用一个名为test的数据库)
创建数据库:create database mydatabase;
不看表中的数据只看表的结构:desc 表名;
2.1 简单查询
2.1.1 查询一个字段
select 字段名 from 表名;;
其中要注意:select 和 from 都是关键字,字段名和表名都是标识符。
2.1.2 查询多个字段
select 字段名1, 字段名2, …, from 表名;
2.1.3 查询所有字段
select * from 表名;
该方法缺点:1. 效率低;2. 可读性差。
2.1.4 给查询字段起别名
select 字段名 as 别名;
注:只是将显示的查询结果列名显示为别名,原列名不会改变。
select语句是永远不会进行修改操作的!!!
as关键字可以省略,修改为select 字段名 别名;
注:起别名时,别名中间不能带空格,否则报错
如果字段名的数据为数字,可使用“ 字段名*数字 ”来做运算。
select ename, sal*12 from emp;
如上SQL语句,表示从emp表中选出ename字段和sal字段下的数据乘十的结果。
2.2条件查询
条件 | 含义 |
= | 等于 |
<>, != | 不等于 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
between ... and ... | 两个值之间,等同于 >= and <= |
is null, is not null | is null 为空, is not null 不为空 |
and | 并且 |
or | 或者 |
in | 包含 |
not | 取非,主要用在is和in中 |
like | 模糊查询,支持“ % ”和“ _ ”匹配 |
2.2.1 =
从emp表中查询sal等于800的员工的empno和ename:
select empno, ename from emp where sal = 800;
输出结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 7369 | SMITH |
+-------+-------+
1 row in set (0.00 sec)
2.2.2 <>
, !=
从emp表中查询sal不等于800的员工的empno和ename:
1.使用" != "符号:
select empno, ename from emp where sal != 800;
2.使用" <> "符号:
select empno, ename from emp where sal <> 800;
输出结果为:
+-------+--------+
| empno | ename |
+-------+--------+
| 7499 | ALLEN |
| 7521 | WARD |
| 7566 | JONES |
| 7654 | MARTIN |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7788 | SCOTT |
| 7839 | KING |
| 7844 | TURNER |
| 7876 | ADAMS |
| 7900 | JAMES |
| 7902 | FORD |
| 7934 | MILLER |
+-------+--------+
13 rows in set (0.00 sec)
2.2.3 <
, <=
, >
和 >=
从emp表中查询sal小于3000的员工的empno和ename:
select empno, ename from emp where sal < 3000;
输出结果为:
+-------+--------+
| empno | ename |
+-------+--------+
| 7369 | SMITH |
| 7499 | ALLEN |
| 7521 | WARD |
| 7566 | JONES |
| 7654 | MARTIN |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7844 | TURNER |
| 7876 | ADAMS |
| 7900 | JAMES |
| 7934 | MILLER |
+-------+--------+
11 rows in set (0.00 sec)
其余的符号类比 < 符号的操作。
2.2.4 between...and...
从emp表中查询sal在2450和3000之间的员工的empno和ename:
1.使用" >= and <= ":
select empno, ename from emp where sal >= 2450 and sal <= 3000;
2.使用" between and ":
select empno, ename from emp where sal between 2450 and 3000;
输出结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 7566 | JONES |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7788 | SCOTT |
| 7902 | FORD |
+-------+-------+
5 rows in set (0.00 sec)
注:在使用between…and时,较小的数字在前,较大的数字在后
2.2.5 is null
,is not null
从emp表中查询comm为空的员工的empno和ename
select empno, ename from emp where comm is null;
输出结果如下:
+-------+--------+
| empno | ename |
+-------+--------+
| 7369 | SMITH |
| 7566 | JONES |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7788 | SCOTT |
| 7839 | KING |
| 7876 | ADAMS |
| 7900 | JAMES |
| 7902 | FORD |
| 7934 | MILLER |
+-------+--------+
10 rows in set (0.00 sec)
从emp表中查询comm不为空的员工的empno和ename
select empno, ename from emp where comm is not null;
+-------+--------+
| empno | ename |
+-------+--------+
| 7499 | ALLEN |
| 7521 | WARD |
| 7654 | MARTIN |
| 7844 | TURNER |
+-------+--------+
4 rows in set (0.00 sec)
2.2.6 and
从emp表中查询job为MANAGER并且sal大于3000的员工的empno和ename
select empno, ename from emp where job = 'MANAGER' and sal > 2500;
输出结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 7566 | JONES |
| 7698 | BLAKE |
+-------+-------+
2 rows in set (0.00 sec)
2.2.7 or
从emp表中查询job为MANAGER和SALESMAN的员工的empno和ename
select empno, ename from emp where job = 'manager' or job = 'salesman';
输出结果为:
+-------+--------+
| empno | ename |
+-------+--------+
| 7499 | ALLEN |
| 7521 | WARD |
| 7566 | JONES |
| 7654 | MARTIN |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7844 | TURNER |
+-------+--------+
7 rows in set (0.00 sec)
2.2.8 in
注:in不是一个区间,in后面跟的时一个具体的值
从emp表中查询sal为800和3000的员工的empno和ename
select empno, ename from emp where sal in(800, 3000);
输出结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 7369 | SMITH |
| 7788 | SCOTT |
| 7902 | FORD |
+-------+-------+
3 rows in set (0.00 sec)
2.2.9 not
从emp表中查询sal不为800或3000的员工的empno和ename
select empno, ename from emp where sal not in(800, 3000);
输出结果为:
+-------+--------+
| empno | ename |
+-------+--------+
| 7499 | ALLEN |
| 7521 | WARD |
| 7566 | JONES |
| 7654 | MARTIN |
| 7698 | BLAKE |
| 7782 | CLARK |
| 7839 | KING |
| 7844 | TURNER |
| 7876 | ADAMS |
| 7900 | JAMES |
| 7934 | MILLER |
+-------+--------+
11 rows in set (0.00 sec)
注:在使用not in
的时候记得要手动排除NULL。
2.2.10 like
从emp表中查询ename中含有o
的员工的empno和ename
select empno, ename from emp where ename like '%o%';
输出结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 7566 | JONES |
| 7788 | SCOTT |
| 7902 | FORD |
+-------+-------+
3 rows in set (0.00 sec)
从emp表中查询ename以T
结尾的的员工的empno和ename
select empno, ename from emp where ename like '%T';
输出结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 7788 | SCOTT |
+-------+-------+
1 row in set (0.00 sec)
从emp表中查询ename以K
结尾的的员工的empno和ename
select empno, ename from emp where ename like 'K%';
输出结果为:
+-------+-------+
| empno | ename |
+-------+-------+
| 7839 | KING |
+-------+-------+
1 row in set (0.00 sec)
从emp表中查询ename的第二个字母为A
的员工的empno和ename
select empno, ename from emp where ename like '_A%';
输出结果为:
+-------+--------+
| empno | ename |
+-------+--------+
| 7521 | WARD |
| 7654 | MARTIN |
| 7900 | JAMES |
+-------+--------+
3 rows in set (0.00 sec)
从emp表中查询ename的第三个字母为R
的员工的empno和ename
select empno, ename from emp where ename like '__R%';
输出结果为:
+-------+--------+
| empno | ename |
+-------+--------+
| 7521 | WARD |
| 7654 | MARTIN |
| 7844 | TURNER |
| 7902 | FORD |
+-------+--------+
4 rows in set (0.00 sec)
注:1.%匹配任意多个字符,下划线匹配任意一个字符
2.输入字符时不要忘了加单引号!!!
3.and和or同时出现,and的优先级比or高
4.需要改变优先级时使用小括号()
5.用like进行模糊查询,查询名字中有_
的,需要使用转义字符
2.3 排序
2.3.1 默认方式排序
从emp表中以sal默认的排序方式查询所有员工的ename和sal
select ename, sal from emp order by sal;
输出结果为:
+--------+---------+
| ename | sal |
+--------+---------+
| SMITH | 800.00 |
| JAMES | 950.00 |
| ADAMS | 1100.00 |
| WARD | 1250.00 |
| MARTIN | 1250.00 |
| MILLER | 1300.00 |
| TURNER | 1500.00 |
| ALLEN | 1600.00 |
| CLARK | 2450.00 |
| BLAKE | 2850.00 |
| JONES | 2975.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| KING | 5000.00 |
+--------+---------+
14 rows in set (0.00 sec)
如上可见,默认的排序方式是升序。
2.3.2 降序排序
从emp表中以sal以降序查询所有员工的ename和sal
select ename, sal from emp order by sal desc;
输出结果为:
+--------+---------+
| ename | sal |
+--------+---------+
| KING | 5000.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| ALLEN | 1600.00 |
| TURNER | 1500.00 |
| MILLER | 1300.00 |
| WARD | 1250.00 |
| MARTIN | 1250.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| SMITH | 800.00 |
+--------+---------+
14 rows in set (0.00 sec)
改为降序只需要在sal后加一个desc
即可。
2.3.3 指定升序
那该如何指定升序呢?如下代码片所示:
select empno, ename from emp oder by sal asc;
只需要在sal后加一个asc
即可。
2.3.4 多字段排序
从emp表中查询员工的ename和sal,并按照sal升序,若sal相等时,按照ename升序排序
select ename, sal from emp order by sal asc, ename asc;
输出结果为:
+--------+---------+
| ename | sal |
+--------+---------+
| SMITH | 800.00 |
| JAMES | 950.00 |
| ADAMS | 1100.00 |
| MARTIN | 1250.00 |
| WARD | 1250.00 |
| MILLER | 1300.00 |
| TURNER | 1500.00 |
| ALLEN | 1600.00 |
| CLARK | 2450.00 |
| BLAKE | 2850.00 |
| JONES | 2975.00 |
| FORD | 3000.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
+--------+---------+
14 rows in set (0.00 sec)
如上,sal在前,sal起主导,只有sal相等时,才会考虑ename排序。
了解内容:根据指定位置的字段进行排序,如下代码片所示:
select ename, sal from emp order by 2;
输出结果为:
+--------+---------+
| ename | sal |
+--------+---------+
| SMITH | 800.00 |
| JAMES | 950.00 |
| ADAMS | 1100.00 |
| WARD | 1250.00 |
| MARTIN | 1250.00 |
| MILLER | 1300.00 |
| TURNER | 1500.00 |
| ALLEN | 1600.00 |
| CLARK | 2450.00 |
| BLAKE | 2850.00 |
| JONES | 2975.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| KING | 5000.00 |
+--------+---------+
14 rows in set (0.00 sec)
如上,根据第2列字段进行排序,但是这样写程序不够健壮,列的位置很容易发生改变
2.4 单行数据处理
单行处理函数的特点:一个输入对应一个输出
多行处理函数的特点:多个输入对应一个输出
2.4.4 lower
转换小写 和 upper
转换大写
lower
将选择的字段中的内容全部转换成小写
upper
将选择的字段中的内容全部转换成大写
select lower(ename) as ename from emp;
输出结果为:
+--------+
| ename |
+--------+
| smith |
| allen |
| ward |
| jones |
| martin |
| blake |
| clark |
| scott |
| king |
| turner |
| adams |
| james |
| ford |
| miller |
+--------+
14 rows in set (0.00 sec)
2.4.5 substr
取子串
使用substr
从emp表中找出ename的第一个字母是A的员工的ename
select ename from emp where substr(ename, 1, 1) = 'A';
输出结果为:
+-------+
| ename |
+-------+
| ALLEN |
| ADAMS |
+-------+
2 rows in set (0.00 sec)
注:起始下标从1开始,没有0
2.4.6 length
取长度
取出ename字段下数据的长度
select length(ename) enamelength from emp;
输出结果为:
+-------------+
| enamelength |
+-------------+
| 5 |
| 5 |
| 4 |
| 5 |
| 6 |
| 5 |
| 5 |
| 5 |
| 4 |
| 6 |
| 5 |
| 5 |
| 4 |
| 6 |
+-------------+
14 rows in set (0.00 sec)
2.4.7 trim
去空格
输入如下SQL语句:
select ename from emp where ename = ' king ';
输出结果为:
Empty set (0.00 sec)
实际上是要查找ename为king的数据,但会显示 Empty set (0.00 sec)
trim
会防止以上情况出现,自动去除空格
select ename from emp where ename = trim(' king ');
输出结果为:
+-------+
| ename |
+-------+
| KING |
+-------+
1 row in set (0.00 sec)
2.4.8 round()
四舍五入
注:select后面可以跟某个表的字段名,也可以跟字面量 / 字面值(数据)
当跟字面值时,所有筛选的数据都是该字面值如下所示:
SQL语句:
select 1200 from emp;
结果为:
+------+
| 1200 |
+------+
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
+------+
14 rows in set (0.00 sec)
四舍五入,保留一位小数:
select round(111.266, 1) from emp;
输出结果为:
+-------------------+
| round(111.266, 1) |
+-------------------+
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
| 111.3 |
+-------------------+
14 rows in set (0.00 sec)
四舍五入保留到十位:
select round(1166, -1) from emp;
输出结果为:
+-----------------+
| round(1166, -1) |
+-----------------+
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
| 1170 |
+-----------------+
14 rows in set (0.00 sec)
2.4.9 rand()
生成随机数
select rand() from emp;
输出结果为:
+---------------------+
| rand() |
+---------------------+
| 0.1434921893696228 |
| 0.5800480149500519 |
| 0.4697635373750362 |
| 0.6086736727586702 |
| 0.6340777824018875 |
| 0.3443679244670718 |
| 0.8196063058633416 |
| 0.06492778664913754 |
| 0.8658200463893079 |
| 0.13431690273277172 |
| 0.07412440522958004 |
| 0.96767134868323 |
| 0.6159835584610548 |
| 0.17690377698922882 |
+---------------------+
14 rows in set (0.00 sec)
输出0~100的整数
select round(rand()*100, 0) from emp;
输出结果为:
+----------------------+
| round(rand()*100, 0) |
+----------------------+
| 4 |
| 65 |
| 15 |
| 80 |
| 54 |
| 30 |
| 88 |
| 50 |
| 86 |
| 80 |
| 41 |
| 66 |
| 9 |
| 45 |
+----------------------+
14 rows in set (0.00 sec)
2.4.10 Ifnull
将null转换成具体值
Null只要参与运算,最终结果一定是Null,为避免此现象,需要使用ifnull函数
Ifnull(数据,被当作哪个值)
如果数据为NUll时,把这个数据当作哪个值
当comm为null时,将comm当作0
select ename, (sal + ifnull(comm, 0))*10 as yearsal from emp;
输出结果为:
+--------+----------+
| ename | yearsal |
+--------+----------+
| SMITH | 8000.00 |
| ALLEN | 19000.00 |
| WARD | 17500.00 |
| JONES | 29750.00 |
| MARTIN | 26500.00 |
| BLAKE | 28500.00 |
| CLARK | 24500.00 |
| SCOTT | 30000.00 |
| KING | 50000.00 |
| TURNER | 15000.00 |
| ADAMS | 11000.00 |
| JAMES | 9500.00 |
| FORD | 30000.00 |
| MILLER | 13000.00 |
+--------+----------+
14 rows in set (0.00 sec)
2.4.11 case...when...then...when...then...else...and
当员工的job为MANAGER时,sal上调10%,当job为SALESMAN时,sal上调50%,其他正常
select ename, job,
sal as oldsal,
(case job when 'MANAGER' then sal * 1.1 when 'SALESMAN' then sal * 1.5 else sal end) as newsal
from emp;
输出结果为:
+--------+-----------+---------+---------+
| ename | job | oldsal | newsal |
+--------+-----------+---------+---------+
| SMITH | CLERK | 800.00 | 800.00 |
| ALLEN | SALESMAN | 1600.00 | 2400.00 |
| WARD | SALESMAN | 1250.00 | 1875.00 |
| JONES | MANAGER | 2975.00 | 3272.50 |
| MARTIN | SALESMAN | 1250.00 | 1875.00 |
| BLAKE | MANAGER | 2850.00 | 3135.00 |
| CLARK | MANAGER | 2450.00 | 2695.00 |
| SCOTT | ANALYST | 3000.00 | 3000.00 |
| KING | PRESIDENT | 5000.00 | 5000.00 |
| TURNER | SALESMAN | 1500.00 | 2250.00 |
| ADAMS | CLERK | 1100.00 | 1100.00 |
| JAMES | CLERK | 950.00 | 950.00 |
| FORD | ANALYST | 3000.00 | 3000.00 |
| MILLER | CLERK | 1300.00 | 1300.00 |
+--------+-----------+---------+---------+
14 rows in set (0.00 sec)
2.5 分组函数(多行处理函数)
多行处理函数的特点:多行输入对应一个输出
注:分组函数在使用时必须先进行分组,然后才能使用,如果没有对数据进行分组,整张表默认为一组
2.5.1 max
最大值
找出emp表中的最高sal
select max(sal) from emp;
输出结果为:
+----------+
| max(sal) |
+----------+
| 5000.00 |
+----------+
1 row in set (0.00 sec)
2.5.2 min
最小值
找出emp表中的最低sal
select min(sal) from emp;
输出结果为:
+----------+
| min(sal) |
+----------+
| 800.00 |
+----------+
1 row in set (0.00 sec)
2.5.3 sum
求和
计算emp表中sal的和
select sum(sal) from emp;
输出结果为:
+----------+
| sum(sal) |
+----------+
| 29025.00 |
+----------+
1 row in set (0.00 sec)
2.5.4 avg
求平均
求emp表中sal的平均值
select avg(sal) from emp;
输出结果为:
+-------------+
| avg(sal) |
+-------------+
| 2073.214286 |
+-------------+
1 row in set (0.00 sec)
2.5.5 count
计数
计算emp表中的员工数量
select count(ename) from emp;
输出结果为:
+--------------+
| count(ename) |
+--------------+
| 14 |
+--------------+
1 row in set (0.00 sec)
分组函数的注意事项:
1.分组函数自动忽略NULL,不需要提前对“ NULL ”进行处理
2.分组函数中count(具体字段)
表示统计该字段下所有不为“ NULL ”的元素总数,count(*)
表示统计表中的总行数(只要有一行数据,则count++)
3.分组函数不能直接使用在where子句中
2.6 分组查询
2.6.1关键字的执行顺序
select
...
from
...
where
...
group by
...
order by
...
执行顺序:
1.from
2.where
3.group by
4.select
5.order by
2.6.2 例题
2.6.2.1 找出每个工作岗位的工资和
思路:按job分组,然后对sal求和。
select job, sum(sal) from emp group by job;
输出结果为:
+-----------+----------+
| job | sum(sal) |
+-----------+----------+
| CLERK | 4150.00 |
| SALESMAN | 5600.00 |
| MANAGER | 8275.00 |
| ANALYST | 6000.00 |
| PRESIDENT | 5000.00 |
+-----------+----------+
5 rows in set (0.00 sec)
注:在一条select语句中,如果有group by语句,select后只能跟参加分组的字段和分组函数,其余的一律不跟
2.6.2.2 找出每个部门不同工作岗位的最高薪资
思路:将depno(部门)和job(工作岗位)两个字段看成一个字段来分组
select deptno, job, max(sal) from emp group by deptno, job;
结果为:
+--------+-----------+----------+
| deptno | job | max(sal) |
+--------+-----------+----------+
| 20 | CLERK | 1100.00 |
| 30 | SALESMAN | 1600.00 |
| 20 | MANAGER | 2975.00 |
| 30 | MANAGER | 2850.00 |
| 10 | MANAGER | 2450.00 |
| 20 | ANALYST | 3000.00 |
| 10 | PRESIDENT | 5000.00 |
| 30 | CLERK | 950.00 |
| 10 | CLERK | 1300.00 |
+--------+-----------+----------+
9 rows in set (0.00 sec)
2.6.2.3 找出每个部门的最高薪资,要求显示最高薪资大于3000的部门编号
思路:
第一步:找出部门最高薪资
select deptno, max(sal) from emp group by deptno;
结果为:
+--------+----------+
| deptno | max(sal) |
+--------+----------+
| 20 | 3000.00 |
| 30 | 2850.00 |
| 10 | 5000.00 |
+--------+----------+
3 rows in set (0.00 sec)
第二步:要求显示最高薪资大于3000的
select deptno, max(sal) from emp group by deptno having max(sal) > 3000;
注:使用having可以对分完组之后的数据进一步过滤,having不能单独使用,having不能代替where,having必须和group by联合使用
结果为:
+--------+----------+
| deptno | max(sal) |
+--------+----------+
| 10 | 5000.00 |
+--------+----------+
1 row in set (0.00 sec)
但是上述SQL语句实际上是将所有的薪资进行分组并找出最大薪资,然后进行筛选,将小于3000的舍去,这样执行效率较低。
优化:先将大于3000的都找出来,再进行分组。
select deptno, max(sal) from emp where sal > 3000 group by deptno;
注:where和having,优先选择where。
2.7 连接查询
注:当两张表进行连接查询,没有任何条件限制的时候,最终查询结果条数是两张表条数的乘积,这种现象被称为笛卡尔积现象
语法:
select
...
from
a
join
b
on
a和b的连接条件
where
筛选条件
2.7.1 distinct
去重
把查询结果去除重复记录
注:1.原表数据不会被修改,只是查询结果去重
2.distinct只能出现在所有字段的最前面
3.distinct出现在两个字段之前,表示两个字段联合起来去重
select distinct job from emp;
结果:
+-----------+
| job |
+-----------+
| CLERK |
| SALESMAN |
| MANAGER |
| ANALYST |
| PRESIDENT |
+-----------+
5 rows in set (0.00 sec)
2.7.2 内连接之等值查询
查询每个员工所在部门名称,显示员工名和部门名
select e.ename, d.dname from emp e join dept d on e.deptno = d.deptno;
结果为:
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| JONES | RESEARCH |
| MARTIN | SALES |
| BLAKE | SALES |
| CLARK | ACCOUNTING |
| SCOTT | RESEARCH |
| KING | ACCOUNTING |
| TURNER | SALES |
| ADAMS | RESEARCH |
| JAMES | SALES |
| FORD | RESEARCH |
| MILLER | ACCOUNTING |
+--------+------------+
14 rows in set (0.00 sec)
2.7.3 内连接之非等值连接
条件不是一个等量关系称为非等值连接
找出每个员工的薪资等级,要求显示员工名,薪资,薪资等级
select e.ename, e.sal, s.grade from emp e join salgrade s on e.sal between s.losal and s.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)
2.7.4 内连接之自连接
技巧,在连接时,将自身这张表看成两张表
查询员工的上级领导,要求显示员工名和对应的领导名
select a.ename '员工名', b.ename '领导名' from emp a join emp b on a.mgr = b.empno;
结果:
+--------+--------+
| 员工名 | 领导名 |
+--------+--------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+--------+
13 rows in set (0.00 sec)
2.7.5 外连接之右外连接
select e.ename, d.dname from emp e right outer join dept d on e.deptno = d.deptno;
right
表示将join
关键字右边的这张表看成主表,主要是为了将这张表的数据全部查询出来,捎带着关联查询左边的表
结果:
+--------+------------+
| ename | dname |
+--------+------------+
| MILLER | ACCOUNTING |
| KING | ACCOUNTING |
| CLARK | ACCOUNTING |
| FORD | RESEARCH |
| ADAMS | RESEARCH |
| SCOTT | RESEARCH |
| JONES | RESEARCH |
| SMITH | RESEARCH |
| JAMES | SALES |
| TURNER | SALES |
| BLAKE | SALES |
| MARTIN | SALES |
| WARD | SALES |
| ALLEN | SALES |
| NULL | OPERATIONS |
+--------+------------+
15 rows in set (0.00 sec)
2.7.6 外连接之左外连接
类比右查询
select e.ename, d.dname from emp e left outer join dept d on e.deptno = d.deptno;
结果:
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| JONES | RESEARCH |
| MARTIN | SALES |
| BLAKE | SALES |
| CLARK | ACCOUNTING |
| SCOTT | RESEARCH |
| KING | ACCOUNTING |
| TURNER | SALES |
| ADAMS | RESEARCH |
| JAMES | SALES |
| FORD | RESEARCH |
| MILLER | ACCOUNTING |
+--------+------------+
14 rows in set (0.00 sec)
注:1.外连接查询的outer
可以省略,但是带上可读性更强
2.外连接的表有主次之分,内连接没有
2.7.7 多张表连接
select
...
from
a
join
b
on
a和b的连接条件
join
c
on
a和c的连接条件
right join
d
on
a和d的连接条件
注:1.内连接和外连接可以混合使用,都可以出现
2.多表连接时,如果要使用左
外连接,每条连接前都要加上left
找出每个员工的部门名称以及工资等级,要求显示员工名、部门名、薪资、薪资等级
select e.ename, e.sal, d.dname, s.grade from emp e join dept d on e.deptno = d.deptno join salgrade s on e.sal between s.losal and s.hisal;
结果:
+--------+---------+------------+-------+
| ename | sal | dname | grade |
+--------+---------+------------+-------+
| SMITH | 800.00 | RESEARCH | 1 |
| ALLEN | 1600.00 | SALES | 3 |
| WARD | 1250.00 | SALES | 2 |
| JONES | 2975.00 | RESEARCH | 4 |
| MARTIN | 1250.00 | SALES | 2 |
| BLAKE | 2850.00 | SALES | 4 |
| CLARK | 2450.00 | ACCOUNTING | 4 |
| SCOTT | 3000.00 | RESEARCH | 4 |
| KING | 5000.00 | ACCOUNTING | 5 |
| TURNER | 1500.00 | SALES | 3 |
| ADAMS | 1100.00 | RESEARCH | 1 |
| JAMES | 950.00 | SALES | 1 |
| FORD | 3000.00 | RESEARCH | 4 |
| MILLER | 1300.00 | ACCOUNTING | 2 |
+--------+---------+------------+-------+
14 rows in set (0.00 sec)
2.8 子查询
子查询时select语句中嵌套select语句,被嵌套的select语句称为子查询。
2.8.1 子查询可出现的位置
select
...(select)...
from
....(select)...
where
....(select)...
2.8.2 where中的子查询
找出比最低工资高的员工的姓名和工资
实现思路:
- 查询最低工资是多少
select min(sal) from emp;
- 找出大于800的
select ename, sal from emp where sal > 800;
- 合并
select ename, sal from emp where sal > (select min(sal) from emp);
结果:
+--------+---------+
| ename | sal |
+--------+---------+
| ALLEN | 1600.00 |
| WARD | 1250.00 |
| JONES | 2975.00 |
| MARTIN | 1250.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
| TURNER | 1500.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| FORD | 3000.00 |
| MILLER | 1300.00 |
+--------+---------+
13 rows in set (0.00 sec)
2.8.3 from子句中的子查询
注:from后的子查询,可以将子查询的查询结果当作一张临时生成的表
找出每个岗位的平均薪资的薪资等级
- 找出每个岗位的平均工资
select job, avg(sal) from emp group by job;
- 将子查询的查询结果当作一张表
select t.*, s.grade from (select job, avg(sal) as avgsal from emp group by job) t join salgrade s on t.avgsal between s.losal and s.hisal;
结果:
+-----------+-------------+-------+
| job | avgsal | grade |
+-----------+-------------+-------+
| CLERK | 1037.500000 | 1 |
| SALESMAN | 1400.000000 | 2 |
| MANAGER | 2758.333333 | 4 |
| ANALYST | 3000.000000 | 4 |
| PRESIDENT | 5000.000000 | 5 |
+-----------+-------------+-------+
5 rows in set (0.00 sec)
2.8.4 select后面出现的子查询
找出每个员工的部门名称,要求显示员工名,部门名
select e.ename, e.deptno, (select d.dname from dept d where e.deptno = d.deptno) as dname from emp e;
结果:
+--------+--------+------------+
| ename | deptno | dname |
+--------+--------+------------+
| SMITH | 20 | RESEARCH |
| ALLEN | 30 | SALES |
| WARD | 30 | SALES |
| JONES | 20 | RESEARCH |
| MARTIN | 30 | SALES |
| BLAKE | 30 | SALES |
| CLARK | 10 | ACCOUNTING |
| SCOTT | 20 | RESEARCH |
| KING | 10 | ACCOUNTING |
| TURNER | 30 | SALES |
| ADAMS | 20 | RESEARCH |
| JAMES | 30 | SALES |
| FORD | 20 | RESEARCH |
| MILLER | 10 | ACCOUNTING |
+--------+--------+------------+
14 rows in set (0.00 sec)
注:对于select后面的子查询来说,这个子查询只能一次返回一条记录,多于一条就报错。
2.9 union
合并查询结果集
查询工作岗位是MANAGER和SALESMAN的员工
- 第一种方式:
select ename, job from emp where job = 'MANAGER' or job = 'SALESMAN';
第二种方式:
select ename. job from emp where job in ('MANAGER', 'SALESMAN');
- 第三种方式:
select ename, job from emp where job = 'MANAGER' union select ename, job from emp where job = 'SALESMAN';
结果:
+--------+----------+
| ename | job |
+--------+----------+
| JONES | MANAGER |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| MARTIN | SALESMAN |
| TURNER | SALESMAN |
+--------+----------+
7 rows in set (0.00 sec)
对于表连接来说,每连接一次新表,则匹配次数满足笛卡尔积,但是union
可以减少匹配次数。
注:1.union在合并集合时要求两个结果的集列数相同。
2.结果集合并时,要求列于列的数据类型相同
2.10 limit
将结果集的一部分取出来,通常使用在分页查询当中。
用法:
limit startindex, length
startindex是起始下标,length是长度。起始下标从 0 开始
缺省用法:
limit 5;
表示取前5。
按薪资降序,取出排名在前5名的员工:
select ename, sal from emp order by sal desc limit 5;
结果:
+-------+---------+
| ename | sal |
+-------+---------+
| KING | 5000.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
+-------+---------+
5 rows in set (0.00 sec)
注:limit在order by之后执行。
取出工资排名在5~9
select ename, sal from emp order by sal desc limit 4, 5;
结果:
+--------+---------+
| ename | sal |
+--------+---------+
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| ALLEN | 1600.00 |
| TURNER | 1500.00 |
| MILLER | 1300.00 |
+--------+---------+
5 rows in set (0.00 sec)
每页显示pageSize条记录:
第pageNo页:limit (pageNo - 1) * pageSize, pageSize
2.11 DQL语句执行顺序
select
...
from
...
where
...
group by
...
having
...
order by
...
limit
...
执行顺序:
1.from
2.where
3.group by
4.having
5.select
6.order by
7.limit
2.12 常见数据类型
varchar
(最长255)
可变长度的字符串
比较智能,节省空间
会根据实际的数据长度动态分配空间
优点:节省空间
缺点:要动态分配空间,速度慢char
(最长255)
定字符串
不管实际的数据长度是多少,分配固定长度的空间去存储数据
使用不恰当会导致空间浪费
优点:不需要动态分配空间,速度快
缺点:使用不当可能会导致空间的浪费int
(最长11)
数字中的整数型bigint
数字中的长整型float
单精度浮点型double
双精度浮点型date
段日型datetime
长日期型clob
字符大对象
最多可以存储4G的字符串blob
二进制大对象
专门用来存储图片、声音、视频等流媒体数据
往BLOB类型的字段插入数据时,需使用IO流
2.13 建表2
建表的语法格式:
create table 表名(字段名1 数据类型, 字段名2, 数据类型, 字段名3, 数据类型);
注:
表名:建议以t_或者tbl_开始,可读性强,见名知意
字段名:见名知意
表名和字段名都属于标识符
创建一个学生表:包括学号、姓名、年龄、性别、邮箱地址
create table t_student(no int, name varchar(32), sex char(1), age int(3), email varchar(255));
当显示如下数据时,就表示建表成功:
Query OK, 0 rows affected, 1 warning (0.03 sec)
2.13.1 删除表
drop table 表名;
如上的删除方式,虽然可以达到删表目的,但是如果表不存在的话,就会报错,所以优化成如下方式:
drop table if exists 表名;
2.13.2 插入数据insert
(DML)
语法格式:
insert into 表名(字段名1, 字段名2, 字段名3) values (值1, 值2, 值3);
注:字段名和值数量要对应,数据类型要对应
向建好的t_student表中添加数据:
insert into t_student(no, name, sex, age, email) values(1, 'Jack', 'M', 20, 'Jack@123.com');
insert into t_student(sex, name, email, no ,age) values('W', 'Sun', 'Sun@456.com', 2, '22');
insert into t_student(no) values (3);
insert into t_student(name) values ('Peter');
打印出这张表:
+------+-------+------+------+--------------+
| no | name | sex | age | email |
+------+-------+------+------+--------------+
| 1 | Jack | M | 20 | Jack@123.com |
| 2 | Sun | W | 22 | Sun@456.com |
| 3 | NULL | NULL | NULL | NULL |
| NULL | Peter | NULL | NULL | NULL |
+------+-------+------+------+--------------+
4 rows in set (0.00 sec)
注:insert语句但凡是执行成功了,那么必然会多一条记录,没有给其他字段指定名称的话,默认值就是NULL
insert
语句中的字段名可以省略,但都省略不写的话,相当于都写上了,所以值也要都写上,并且顺序必须与默认一致。如下所示:
insert into t_student values (2, 'John', 'M', 20, 'John@789.com');
注:insert可以一次插入多条记录,如下所示:
insert into t_student(no, name, sex, age, email) values(1, 'Jack', 'M', 20, 'Jack@123.com'), (2, 'Sun', 'W', 22, 'Sun@456.com'), (3, 'John', 'M', 21, 'John@789.com');
2.13.3 str_to_date
关键字
创建一个t_user表,如下所示:
create table t_user(id int, name varchar(32), birth date);
插入数据:
insert into t_user(id, name, birth) values (1, 'admin', '01-01-2000');
上述插入数据报错,因为birth需要的是date
类型的数据,而插入时提供的类型为varchar
。
可以使用str_to_date
关键字来将字符串类型转变成date类型。
语法格式:
str_to_date('字符串类型', '日期格式');
MySQL的日期格式:
%Y 年
%m 月
%d 日
%h 时
%i 分
%s 秒
将上述报错代码片改为如下:
insert into t_user(id, name, birth) values (1, 'admin', str_to_date('01-01-2000', '%d-%m-%Y'));
注:如果提供的字符串的格式是%Y-%m-%d
,会自动转换成date类型的数据
2.13.4 date_format
关键字
指定日期类型的数据的格式
语法格式:
date_format('日期类型的数据', '指定日期格式');
2.13.5 datetime
数据类型
datetime
包括年月日时分秒信息
长日期的默认格式:
%Y-%m-%d %h:%i:%s
创建一个包含datetime
数据类型的表:
create table t_user(id int, name varchar(32), birth date, create_time datetime);
插入数据:
insert into t_user(id, name, birth, create_time) values (1, 'Kun', '2000-01-01', '2022-07-01 16:04:29');
查询表中数据:
+------+------+------------+---------------------+
| id | name | birth | create_time |
+------+------+------------+---------------------+
| 1 | Kun | 2000-01-01 | 2022-07-01 16:04:29 |
+------+------+------------+---------------------+
1 row in set (0.00 sec)
2.13.6 now()
获取当前系统时间
2.13.7 update
关键字(DML)
更新表中的数据
语法格式:
update 表名 set 字段名1=值1, 字段名2=值2, 字段名3=值3 where 条件;
注:没有where后的条件限制会导致所有数据全部更新
2.13.8 delete
关键字(DML)
删除表中的数据
语法格式:
delete from 表名 where 条件;
注:没有where后的条件限制会导致所有数据全部删除
2.13.9 快速建表
create table emp2 as select * from emp;
将一个查询结果当作一张表新建,可以完成表的快速复制。
注:还可以将查询结果插入一张表当中,如下代码片所示:
insert into 表名 查询结果;
2.13.10 删除数据
2.13.10.1 delete
(DML)
语法格式:
delete from 表名;
数据删除数据的原理:
- 表中的数据删除了,但数据占用的内存空间不会释放
- 优点:支持回滚(
rollback
),数据可以恢复 - 缺点:删除效率较低
示例:
输入如下代码:
strat transaction;
delete from 表名;
使用select查询时,查询不到数据
但输入如下代码:
rollback;
再次使用select查询,发现数据恢复了。
2.13.10.2 truncate
(DDL)
语法格式:
truncate table 表名;
数据删除原理:
- 表中的数据被删除,数据占用的内存被释放
- 优点:效率高
- 缺点:不支持回滚,数据不可恢复
删除大表数据时,使用delete
进行删除,需要执行很长时间,但使用truncate
删除时,速度很快
2.14 对表结构的增删改(非重点)
添加一个字段,删除一个字段属于表结构的增删改。需要使用到alter
关键字
2.15 约束
- 约束对应的英文单词:constraint
- 在创建表时,给表中的字段加上一些约束,来保证表中数据的完整性,有效性
2.15.1 非空约束:not null
not null
约束的字段的数据不能为空。
create table t_vip(id int, name varchar(32) not null);
在需要约束的字段后加上not null
关键字(这种约束为列级约束),就表示该字段不能为空,如上所示,name字段不能为空
insert into t_vip (id) values (1);
当执行如上代码时,因为没有给name字段赋值,所以会报错。
2.15.2 唯一性约束:unique
unique
约束的字段的数据不能重复,但是可以为空,都为空的两个数据不算重复
create table t_vip (id int, name varchar(32) unique, eamil varchar(32));
如上所示,name不能重复。
insert into t_vip(id, name) values (1, 'Jack', 'Jack@QQ.com');
insert into t_vip(id, name) values (2, 'Jack', 'Jack@sina.com');
插入第一行数据时没有问题,当插入第二行数据时,就会报错,因为名字重复了
2.15.2.1 联合约束
name和email两个字段联合起来具有唯一性,就是只有当name和email两个字段的数据都相等时,才认为相等。
create table t_vip(id int, name varchar(32), email varchar(32), unique(name, email));
如上,unique
关键字没有添加到字段的后面,所以该约束为表级约束。
注:not null没有表级约束
2.15.2.2 not null
和unique
的联合使用
create table t_vip(id int, name varchar(32) not null unique);
如上,name字段不能为空也不能重复。
注:在MySQL中,如果一个字段同时被not null
和unique
约束的话,该字段自动变成主键字段。在oracle中不同!
2.15.3 主键约束:primary key
- 主键字段:被主键约束约束的字段
- 主键值:主键字段中的每一个值
主键值是每一行记录的唯一标识,相当于身份证号。
注:如果表没有主键,则该表无效
主键值的特征:主键值不能为空,并且不能重复。
添加主键约束如下所示:
create table t_vip(id int primary key, name varchar(32));
也可用表级约束来添加:
create table t_vip(id int, name varchar(32), primary key(id));
一个字段做主键叫做单一主键,多个字段联合起来做主键叫做复合主键。
复合主键的写法:
create table t_vip(id int, name varchar(32), email varchar(32), primary key(id, name));
注:1.在实际开发中,不建议使用复合主键,建议使用单一主键。
2.一张表主键约束只能添加一个
3.主键值建议使用int,bigint,char等,不建议使用varchar来做主键,主键一般都是数字,一般都是定长的。
2.15.3.1 自然主键和业务主键
自然主键:主键是一个自然数和业务没关系
业务主键:主键值和业务紧密联系,例如把银行卡号作为主键值
注:最好使用自然主键,因为业务主键与业务挂钩,当业务发生变动时,会影响到主键
自动维护一个主键值(不用自己取主键值),使用auto_increment
关键字
create table t_vip(id int primary key auto_increment, name varchar(32));
insert into t_vip(name) values ('Jack');
insert into t_vip(name) values ('Jack');
insert into t_vip(name) values ('Jack');
insert into t_vip(name) values ('Jack');
insert into t_vip(name) values ('Jack');
select * from t_vip;
将id字段设置成主键,并使用auto_increment
。
输出结果为:
+----+------+
| id | name |
+----+------+
| 1 | Jack |
| 2 | Jack |
| 3 | Jack |
| 4 | Jack |
| 5 | Jack |
+----+------+
5 rows in set (0.00 sec)
2.15.4 外键约束:foreign key
在创建表时,有些部分过于冗余,会占用不必要的内存,如下所示
+----+------+---------+-------------------------------------+
| no | name | classno | classname |
+----+------+---------+-------------------------------------+
| 1 | Jack | 100 | 江苏省苏州市相城区陆慕高级中学高三9班 |
| 2 | Sun | 100 | 江苏省苏州市相城区陆慕高级中学高三9班 |
| 3 | Lucy | 101 | 江苏省苏州市相城区陆慕高级中学高三7班 |
| 4 | John | 101 | 江苏省苏州市相城区陆慕高级中学高三7班 |
| 5 | Helen| 101 | 江苏省苏州市相城区陆慕高级中学高三7班 |
+----+------+---------+-------------------------------------+
classname字段下的数太过冗余,这时据需要使用外键约束,如下使用两张表:
班级表:
+---------+-------------------------------------+
| classno | classname |
+---------+-------------------------------------+
| 100 | 江苏省苏州市相城区陆慕高级中学高三9班 |
| 101 | 江苏省苏州市相城区陆慕高级中学高三7班 |
+---------+-------------------------------------+
学生表:
+----+------+---------+
| no | name | cno |
+----+------+---------+
| 1 | Jack | 100 |
| 2 | Sun | 100 |
| 3 | Lucy | 101 |
| 4 | John | 101 |
| 5 | Helen| 101 |
+----+------+---------+
当学生表的cno字段没有任何约束的时候,会产生无效的数据(例如,cno的值为102,但是相关的班级表中却没有102对应的数据,这个102就是无效数据),为了保证数据的有效,就应该给cno字段添加外键约束,来引用班级表的classsno字段,因此cno字段就是外键字段,cno字段中的每一个值就是外键值,学生表就是子表,班级表就是父表。
注:1.在删除表时,应先删除子表,再删除父表
2.在创建表时,应先创建父表,再创建子表
3.在删除数据时,应先删除子表中的数据,再删除父表中的数据
4.在往表中插入数据时,应先插入父表中的数据,再插入子表中的数据
添加外键约束:
create table t_class(classno int primary key, classname varchar(32));
create table t_student(no int primary key auto_increment, name varchar(32), cno int, foreign key(cno) references t_class(classno));
在子表中添加如下代码:
foreign key(子表被外键约束的字段名) references 父表名(父表引用的字段名);
注:1.子表引用父表的字段不一定是主键,但一定要有unique
约束,外键值可以为null
2.引用和被引用字段的类型要相同
2.16 sql脚本文件
xxx.sql被称为sql脚本文件,sql脚本文件中编写了大量sql语句,我们执行sql脚本文件时该文件中所有的sql语句都会执行,如下所示:
source sql脚本文件的文件路径;
输入如上代码,就会执行sql脚本文件中所有的sql语句。
2.17 存储引擎
存储引擎是一个表存储组织数据的方式,不同的存储引擎,表存储数据的方式不同。
执行如下语句,可以查看建表的源代码:
show create table 表名;
查看数据库中的建表语句:
CREATE TABLE `t_student` (
`no` int NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`cno` int DEFAULT NULL,
PRIMARY KEY (`no`),
KEY `cno` (`cno`),
CONSTRAINT `t_student_ibfk_1` FOREIGN KEY (`cno`) REFERENCES `t_class` (`classno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
查看建表语句发现,可以在建表时使用engine
关键字来指定表的存储引擎,同时也可以使用charset
关键字来指定文字的编码方式。
建表时指定存储引擎和编码方式:
create table t_user(id int primary key auto_increment, name varchar(32)) engine=InnoDB default charset=gbk;
注:MySQL默认的存储引擎是InnoDB
查看MySQL中有哪些存储引擎:
show engines \G
查询结果:
*************************** 1. row ***************************
Engine: MEMORY
Support: YES
Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 2. row ***************************
Engine: MRG_MYISAM
Support: YES
Comment: Collection of identical MyISAM tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 3. row ***************************
Engine: CSV
Support: YES
Comment: CSV storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 4. row ***************************
Engine: FEDERATED
Support: NO
Comment: Federated MySQL storage engine
Transactions: NULL
XA: NULL
Savepoints: NULL
*************************** 5. row ***************************
Engine: PERFORMANCE_SCHEMA
Support: YES
Comment: Performance Schema
Transactions: NO
XA: NO
Savepoints: NO
*************************** 6. row ***************************
Engine: MyISAM
Support: YES
Comment: MyISAM storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 7. row ***************************
Engine: InnoDB
Support: DEFAULT
Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
XA: YES
Savepoints: YES
*************************** 8. row ***************************
Engine: BLACKHOLE
Support: YES
Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
XA: NO
Savepoints: NO
*************************** 9. row ***************************
Engine: ARCHIVE
Support: YES
Comment: Archive storage engine
Transactions: NO
XA: NO
Savepoints: NO
9 rows in set (0.00 sec)
2.17.1 MyISAM存储引擎
使用三个文件表示每个表:
- 格式文件 - 存储表结构的定义(以.frm结尾的文件)
- 数据文件 - 存储表行的内容(以.MYD结尾的文件)
- 索引文件 - 存储表上的索引(以.MYI结尾的文件)
对于一张表来说,只要是主键,或者加有unique
约束的字段上会自动创建索引。
MyISAM存储引擎的特点:
可被转换为压缩,只读表来节省空间。
2.17.2 InnoDB存储引擎
特征:
- 每个InnoDB表在数据库目录中以.frm格式文件表示
- InnoDB表空间tablespace被用于存储表的数据和索引
- 提供一组用来记录事务的性活动的日志文件
- 支持事务处理:COMMIT、SAVEPOINT、ROLLBACK
- 在MySQL服务器崩溃后提供自动恢复
- 多版本和行级锁定
- 支持外键及应用的完整性,包括级联删除和更新
总结:数据安全,效率不高,不是很节省内存空间
2.17.3 MEMORY存储引擎
使用MEMORY存储引擎的表,其数据存储在内存当中,且行的长的固定这两个特点使得该存储引擎特别快。
特征:
- 在数据库目录内,每个表均以.frm格式的文件表示
- 表数据及索引被存储在内存当中
- 表级锁机制
- 不能包含 TEXT 或 BLOB字段
优点:查询效率是最高的,不需要和硬盘交互
缺点:不安全,关机之后数据消失
2.18 事务
一个事务是一个完整的事务逻辑,是一个最小的工作单元,不可再分。
举例:
从A账户向B账户中转账100,
A账户余额扣除100(update语句)
B账户余额增加100(update语句)
两条update语句要么同时成功,要么同时失败,这样才能保证数据的无误。
注:只有DML语句(insert、delete、update)才有事务操作
部分存储引擎提供一组用来记录事务性活动的日志文件。
在事务执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。在事务的执行过程中,我们可以提交事务,也可以回滚事务。
2.18.1 提交事务:commit
提交事务:
清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。
提交事务标志着事务的结束,并且是一种全部成功的结束
MySQL默认自动提交事务(每执行一条DML语句,就提交一次)
关闭自动提交:
start transaction;
执行上述语句,表示开启事务,关闭自动提交机制,需要输入commit
关键字来手动提交。如下所示:
start transaction;
insert into t_user(name) values ('zhangsan');
insert into t_user(name) values ('lisi');
insert into t_user(name) values ('wangwu');
insert into t_user(name) values ('zhaoliu');
commit;
select * from t_user;
结果:
+----+----------+
| id | name |
+----+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | wangwu |
| 4 | zhaoliu |
+----+----------+
4 rows in set (0.00 sec)
2.18.2 回滚事务:rollback
回滚事务:
将之前所有的DML语句全部撤销,并且清空事务性活动的日志文件
回滚事务标志着事务的结束,并且是一种全部失败的结束
演示:
start transaction;
insert into t_user(name) values ('zhangsan');
insert into t_user(name) values ('lisi');
insert into t_user(name) values ('wangwu');
insert into t_user(name) values ('zhaoliu');
select * from t_user;
查询结果:
+----+----------+
| id | name |
+----+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | wangwu |
| 4 | zhaoliu |
+----+----------+
4 rows in set (0.00 sec)
但是上面的代码块没有使用commit
关键字进行提交,所以支持使用rollback
回滚:
rollback;
select * from t_user;
结果为:
Empty set (0.00 sec)
可见回滚成功了。
注:回滚只能回滚到上一次提交数据点!!!
2.18.3 事务的4个特性
- A:原子性
事务是一个最小的工作单元,不可再分 - C:一致性
所有事物要求,在同一事务中,所有操作必须同时成功,或者同时失败,以保证数据的一致性 - I:隔离性
A事务和B事务之间具有一定的隔离,A事务在操作一张表时,B事务也在操作这张表会出现不同的情况。 - D:持久性
事务最终结束的一个保障,事务提交就相当于将没有保存到银盘上的事务保存到硬盘上。
2.18.4 事务的隔离级别
2.18.4.1 读未提交:read uncommitted
事务A可以读到事务B未提交的数据。
该隔离级别存在的问题:
脏读现象(Dirty Read)
2.18.4.2 读已提交:read committed
事务A只能读取到事务B提交之后的数据,该隔离级别解决了脏读现象。
该隔离级别存在的问题:
不可重复读取数据(前后读取的数据不一致)
2.18.4.3 可重复读:repeatable read
事务A开启之后,每一次读取的数据都是事务A开启之前的数据,即使在事务B中进行了修改并且提交了,只要事务A不结束并且在事务A种没有进行修改数据,读到的数据永远都是事务A开启之前的数据。解决了不可重复读取数据的问题。
该隔离级别存在的问题:
会出现幻影读
2.18.4.4 序列化:serializable
最高隔离级别,效率最低,但是解决了所有问题。这种隔离级别表示事务排队,不能并发
查看隔离级别:
select @@transaction_isolation;
设置隔离级别:
set global transaction isolation level 隔离级别;
2.19索引
索引是在数据库表的字段上添加的,是为了提高查询效率而存在的一种机制,一张表的一个字段可以添加一个索引,多个字段联合也可以添加索引,索引相当于目录,是为了缩小扫描范围而存在的一种机制
select ename from emp where enmae = 'king';
如上SQL语句,在执行时会去ename字段上扫描,因为查询条件是:ename = ‘king’。
如果ename字段上没有添加索引,MySQL会进行全扫描,会将ename字段上的每一个值都比对一遍,效率较低。
MySQL在查询方面主要有两种方式:
- 全局扫描
- 根据索引检索
注:在MySQL数据库当中,索引是需要排序的并且这个排序和TreeSet数据结构相同,是一个自平衡二叉树,在MySQL当中索引是一个B-Tree数据结构,遵循左小右大的原则存放,采用中序遍历方式遍历数据
索引的要点:
- 在任何数据库当中主键都会自动添加索引对象,在MySQL当中,一个字段如果有
unique
约束的话,也会自动创建索引对象 - 在任何数据库当中,任何一张表的任何一条记录都在硬盘存储上都有一个硬盘的物理存储编号
- 在MySQL当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在,例如在MyISAM存储引擎中,索引存储在一个.MYI文件中。但都是以B-Tree的形式存在。
select * from emp where empno = 7561;
执行原理:
在执行上述代码片时,通过empno的索引对象定位到7561(缩小下扫描范围,快速定位),通过7561得出物理编号(假设为0x1111),此时,马上SQL语句转换:select * from emp where 物理编号 = 0x1111;
在什么条件下添加索引:
- 数据量庞大(相对于硬件而言)
- 经常出现在where后的字段,以条件的形式存在,也就是说这个字段总是被扫描
- 该字段很少的DML(insert、delete、update)操作(因为DML之后,索引需要重新排序)
注:建议不要随意添加索引,因为索引也是需要维护的,太多的话反而降低系统的性能,建议通过主键查询,通过unique
约束的字段进行查询。
2.19.1 索引的创建与删除
创建索引:
create index 索引名 on 表名(添加索引的字段名);
删除索引:
drop index 索引名 on 表名;
查看一个SQL语句是否使用了索引进行检索:
explain SQL语句;
2.19.2 索引失效
- 第一种情况:
select * from emp where ename like '%T';
ename上即使添加了索引也不会走索引,因为模糊查询以%
开头了,尽量避免模糊查询的时候以%
开头。
- 第二种情况:
select * from emp where ename = 'king' or job = 'MANAGER';
使用or的时候会失效,如果使用or那么要求or两边的字段都要有索引。如果其中一个字段没有索引,那么另一个字段的索引也会失效。
- 第三种情况
create index emp_job_ename_index on emp(job, sal);
select * from emp where job = 'MANAGER';
select * from emp where sal = 800;
使用复合索引的时候,没有使用左侧的列进行查找,索引失败。如上,第一条SQL查询语句使用左侧列job进行查找,在查找时使用了索引,而第二条SQL查询语句使用右侧列sal进行查找,则不会使用索引。
- 第四种情况
select * from emp where sal+1 = 800;
在where当中索引列参加了运算,索引失效。
- 第五种情况
select * from emp where lower(ename) = 'SMITH';
在where当中索引列使用了函数。
2.19.3 索引的分类
- 单一索引:一个字段上添加索引
- 复合索引:两个字段或者更多字段上添加索引
- 主键索引:主键上添加索引
- 唯一性索引:具有
unique
约束的字段上添加索引
注:唯一性比较弱的字段上添加索引用处不大!!!
2.20. 视图
view:站在不同的角度去看待同一份数据
创建视图对象:
create view 视图名 as DQL语句;
drop view 视图名;
注:只有DQL语句才能以view的形式创建
2.20.1 视图的用法
我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作。
例如:
create view emp2_view as select * from emp2;
insert into emp2_view(empno, ename) values (1, 'Jack');
select * from emp2;
向emp2的视图插入一条数据,再查询emp2表的数据,结果如下:
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100.00 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 |
| 1 | Jack | NULL | NULL | NULL | NULL | NULL | NULL |
+-------+--------+-----------+------+------------+---------+---------+--------+
15 rows in set (0.00 sec)
可见多出了一条数据。
2.20.2 视图的作用
假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同位置上反复使用,每次编写这条语句都很复杂,耗时。此时就可以将这条复杂的SQL语句以视图对象的形式创建出来,在需要编写这条SQL语句的地方使用视图对象,可以大大简化开发,并且利于后期维护。
注:使用视图的时候可以像使用表一样,可以对视图进行增删改查的操作,视图不是存储在内存当中,是存储在硬盘上的。
2.21 DBA常用命令
- 数据导出
mysqldump 数据库名>D:\导出的文件名 -u用户名 -p用户密码
注:要在退出MySQL的状态下输入如上代码。
- 数据导入
source 需要导入文件的文件路径
注:需要先登录到MySQL数据库服务器。
2.22 数据库开发三范式
- 第一范式
最核心,最重要的范式,所有表的设计都需要满足。
必须有主键,并且每一个字段都是原子性,不可再分。
学生编号 | 学生姓名 | 联系方式 |
1001 | 张三 | zhangsan@123.com, 13821021020 |
1002 | 李四 | lisi@456.com, 15121782819 |
1001 | 王五 | wangwu@789.com, 13216272185 |
学生编号(PK) | 学生姓名 | 邮箱地址 | 联系电话 |
1001 | 张三 | zhangsan@123.com | 13821021020 |
1002 | 李四 | lisi@456.com | 15121782819 |
1001 | 王五 | wangwu@789.com | 13216272185 |
- 第二范式
建立在第一范式的基础上,要求所有非主键字段必须完全依赖主键,不要产生部分依赖。
学生编号和教师编号联合做主键 | |||
学生编号 | 教师编号 | 学生姓名 | 教师姓名 |
1001 | 001 | 张三 | 王老师 |
1002 | 002 | 李四 | 赵老师 |
1003 | 001 | 王五 | 王老师 |
1001 | 002 | 张三 | 赵老师 |
将表修改成如下:
学生表:
学生编号(PK) | 学生姓名 |
1001 | 张三 |
1002 | 李四 |
1003 | 王五 |
1001 | 张三 |
教师表:
教师编号(PK) | 教师姓名 |
001 | 王老师 |
002 | 赵老师 |
001 | 王老师 |
002 | 赵老师 |
学生和教师的关系:
id(PK) | 学生编号(FK) | 教师编号(FK) |
1 | 1001 | 001 |
2 | 1002 | 002 |
3 | 1003 | 001 |
4 | 1001 | 002 |
注:多对多,三张表,关系表两外键
- 第三范式
第三范式建立在第二范式的基础之上,要求所有非主键字段必须直接依赖主键字段,不要产生传递依赖。
学生编号(PK) | 学生姓名 | 班级编号 | 班级名称 |
1001 | 张三 | 01 | 一年一班 |
1002 | 李四 | 02 | 一年二班 |
1003 | 王五 | 03 | 一年三班 |
1004 | 赵六 | 03 | 一年三班 |
班级表:
班级编号(PK) | 班级名称 |
01 | 一年一班 |
02 | 一年二班 |
03 | 一年三班 |
学生表:
学生编号(PK) | 学生姓名 | 班级编号(FK) |
1001 | 张三 | 01 |
1002 | 李四 | 02 |
1003 | 王五 | 03 |
1004 | 赵六 | 03 |
注:一对多,两张表,多的表加外键(一个班级对应多个学生,多的表就是学生表)
id(PK) | 用户名 | 密码 | 真实姓名 | 住址 | 邮箱 | ... | ... |
1 | admin | 123456 | ... | ... | ... | ... | ... |
2 | Jack | qwert | ... | ... | ... | ... | ... |
3 | Jams | asdfg | ... | ... | ... | ... | ... |
登录详细信息表:
id(PK) | 用户名 | 密码 |
1 | admin | 123456 |
2 | Jack | qwert |
3 | Jams | asdfg |
用户详细信息表:
真实姓名 | 住址 | 邮箱 | ... | ... | login_id(fk + unique) |
... | ... | ... | ... | ... | 1 |
... | ... | ... | ... | ... | 2 |
... | ... | ... | ... | ... | 3 |
注:一对一,外键唯一