MySQL

文章目录

一、数据库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语句,最终完成数据库中数据的增删改查的操作。

执行
操作
DBMS
SQL
DB

注: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 表名;;
其中要注意:selectfrom 都是关键字,字段名和表名都是标识符。

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 nullis null 为空, is not null 不为空
and并且
or或者
in包含
not取非,主要用在is和in中
like模糊查询,支持“ % ”和“ _ ”匹配
2.2.1 =

emp表中查询sal等于800的员工的empnoename

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的员工的empnoename
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的员工的empnoename

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之间的员工的empnoename
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为空的员工的empnoename

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不为空的员工的empnoename

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表中查询jobMANAGER并且sal大于3000的员工的empnoename

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表中查询jobMANAGERSALESMAN的员工的empnoename

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的员工的empnoename

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的员工的empnoename

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的员工的empnoename

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表中查询enameT结尾的的员工的empnoename

select empno, ename from emp where ename like '%T';

输出结果为:

+-------+-------+
| empno | ename |
+-------+-------+
|  7788 | SCOTT |
+-------+-------+
1 row in set (0.00 sec)

emp表中查询enameK结尾的的员工的empnoename

select empno, ename from emp where ename like 'K%';

输出结果为:

+-------+-------+
| empno | ename |
+-------+-------+
|  7839 | KING  |
+-------+-------+
1 row in set (0.00 sec)

emp表中查询ename的第二个字母为A的员工的empnoename

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的员工的empnoename

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默认的排序方式查询所有员工的enamesal

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降序查询所有员工的enamesal

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表中查询员工的enamesal,并按照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取子串

使用substremp表中找出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

当员工的jobMANAGER时,sal上调10%,当jobSALESMAN时,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中的子查询

找出比最低工资高的员工的姓名和工资
实现思路:

  1. 查询最低工资是多少
select min(sal) from emp;
  1. 找出大于800的
select ename, sal from emp where sal > 800;
  1. 合并
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后的子查询,可以将子查询的查询结果当作一张临时生成的表
找出每个岗位的平均薪资的薪资等级

  1. 找出每个岗位的平均工资
select job, avg(sal) from emp group by job;
  1. 将子查询的查询结果当作一张表
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 表名;

数据删除数据的原理:

  1. 表中的数据删除了,但数据占用的内存空间不会释放
  2. 优点:支持回滚(rollback),数据可以恢复
  3. 缺点:删除效率较低
    示例:
    输入如下代码:
strat transaction;
delete from 表名;

使用select查询时,查询不到数据
但输入如下代码:

rollback;

再次使用select查询,发现数据恢复了。

2.13.10.2 truncate(DDL)

语法格式:

truncate table 表名;

数据删除原理:

  1. 表中的数据被删除,数据占用的内存被释放
  2. 优点:效率高
  3. 缺点:不支持回滚,数据不可恢复

删除大表数据时,使用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 联合约束

nameemail两个字段联合起来具有唯一性,就是只有当nameemail两个字段的数据都相等时,才认为相等。

create table t_vip(id int, name varchar(32), email varchar(32), unique(name, email));

如上,unique关键字没有添加到字段的后面,所以该约束为表级约束
注:not null没有表级约束

2.15.2.2 not nullunique的联合使用
create table t_vip(id int, name varchar(32) not null unique);

如上,name字段不能为空也不能重复。
注:在MySQL中,如果一个字段同时被not nullunique约束的话,该字段自动变成主键字段。在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在查询方面主要有两种方式:

  1. 全局扫描
  2. 根据索引检索

注:在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;
在什么条件下添加索引:

  1. 数据量庞大(相对于硬件而言)
  2. 经常出现在where后的字段,以条件的形式存在,也就是说这个字段总是被扫描
  3. 该字段很少的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
如上表格不满足第一范式的要求:1.没有主键;2.联系方式可拆分为邮箱和联系电话。
学生编号(PK)学生姓名邮箱地址联系电话
1001张三zhangsan@123.com13821021020
1002李四lisi@456.com15121782819
1001王五wangwu@789.com13216272185
做出如上修改,将学生编号设计成主键,将联系方式拆分成邮箱地址和练习电话,使其不可再分。
  • 第二范式
    建立在第一范式的基础上,要求所有非主键字段必须完全依赖主键,不要产生部分依赖。
学生编号和教师编号联合做主键
学生编号教师编号学生姓名教师姓名
1001001张三王老师
1002002李四赵老师
1003001王五王老师
1001002张三赵老师
学生编号和教师编号联合做主键,复合主键,张三依赖==1001==,王老师依赖==001==,产生了部分依赖。部份依赖会导致数据冗余,空间浪费。

将表修改成如下:
学生表:

学生编号(PK)学生姓名
1001张三
1002李四
1003王五
1001张三

教师表:

教师编号(PK)教师姓名
001王老师
002赵老师
001王老师
002赵老师

学生和教师的关系:

id(PK)学生编号(FK)教师编号(FK)
11001001
21002002
31003001
41001002

注:多对多,三张表,关系表两外键

  • 第三范式
    第三范式建立在第二范式的基础之上,要求所有非主键字段必须直接依赖主键字段,不要产生传递依赖。
学生编号(PK)学生姓名班级编号班级名称
1001张三01一年一班
1002李四02一年二班
1003王五03一年三班
1004赵六03一年三班
如上表所示,一年一班依赖==01==,==01==依赖==1001==,产生了传递依赖,产生了数据冗余。

班级表:

班级编号(PK)班级名称
01一年一班
02一年二班
03一年三班

学生表:

学生编号(PK)学生姓名班级编号(FK)
1001张三01
1002李四02
1003王五03
1004赵六03

注:一对多,两张表,多的表加外键(一个班级对应多个学生,多的表就是学生表)

id(PK)用户名密码真实姓名住址邮箱......
1admin123456...............
2Jackqwert...............
3Jamsasdfg...............
如上表:表中的字段过多,可将其拆分为两张表:

登录详细信息表:

id(PK)用户名密码
1admin123456
2Jackqwert
3Jamsasdfg

用户详细信息表:

真实姓名住址邮箱......login_id(fk + unique)
...............1
...............2
...............3

注:一对一,外键唯一


  1. 英文单词DataBase,简称DB。按照一定格式储存数据的一些文件的组合。 ↩︎

  2. 建表属于DDL语句,DDL包括:create drop alter ↩︎

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值