目录
between … and …. 两个值之间, 等同于 >= and <=:
in 包含,相当于多个 or (not in 不在这个范围中):
substr 取子串(substr( 被截取的字符串, 起始下标,截取的长度)):
case..when..then..when..then..else..end:
思考:子表中的外键引用的父表中的某个字段,被引用的这个字段必须是主键吗?
在mysql当中,怎么查看一个SQL语句是否使用了索引进行检索?
数据库:
DataBase,简称DB。是按照一定格式存储数据的一些文件的组合。
顾名思义:存储数据的仓库,实际上就是一堆文件。这些文件中存储了具有特定格式的数据。
数据库管理系统:
DataBaseManagement,简称DBMS。
数据库管理系统是专门用来管理数据库中数据的,数据库管理系统可以对数据库当中的数据进行增删改查。
常见的数据库管理系统:MySQL、Oracle、MS SglServer、DB2、sybase等....
SOL:
是一个结构化查询语言。
程序员通过编写SOL语句,然后DBMS负责执行SOL语句,最终来完成数据库中数据的增删改查操作。
SQL是一种通用的标准语言,不只是可以在MySQL中使用,还可以在Oracle等其他数据库管理系统中使用。
三者之间的关系:DBMS --- 执行 ---> SOL --- 操作 ---> DB 。
配置环境变量:
如果我们想从DOS窗口来启动MySQL,我们可以把 C:\Program Files\MySQL\MySQL Server 8.0\bin 配置到环境变量 path 中,这样我们可以直接在DOS窗口启动该路径下的 mysql.exe 。
然后我们可以在DOS窗口中输入:
mysql -u用户名 -p密码
这样就可以直接登录MySQL了。
但是这种方法有一个弊端,就是会让密码明文输入。
我们可以使用下述代码:
mysql -u用户名 -p
然后我们就会进入输入密码界面,在密码界面我们输入的密码是不会明文显示的。
SQL脚本文件:
xxxx.sql这种文件被称为sql脚本文件,sql脚本文件中编写了大量的sql语句,我们执行sql脚本文件的时候,该文件中所有的sql语句会全部执行,批量的执行SQL语句,可以使用sql脚本文件。
在mysql当中怎么执行sql脚本呢?
mysql> source D:\course\03-MySQL\document\vip.sql
在实际的工作中,第一天到了公司,项目经理会给你一个xxx.sql文件,执行这个脚本文件,电脑上就有项目的的数据库数据了!
表:
数据库当中最基本的单元是表:table。
数据库当中是以表格的形式表示数据的。因为表比较直观。
任何一张表都有行和列:
行(row):被称为数据/记录。
列(column):被称为字段。(姓名字段、性别字段、年龄字段)
姓名 性别 年龄(列:字段)
---------------------------
张三 男 20 ------->行(记录)
李四 女 21 ------->行(记录)
王五 男 22 ------->行(记录)
每一个字段都有:字段名、数据类型、约束等属性。字段名可以理解,是一个普通的名字,见名知意就行。
数据类型:字符串,数字,日期等。
约束:约束也有很多,其中一个叫做唯一性约束,这种约束添加之后,该字段中的数据不能重复。
关于SQL语句的分类:
SQL语句有很多,最好进行分门别类,这样更容易记忆。
分为:
DQL:
数据查询语言(凡是带有select关键字的都是查询语句)。
DML:
数据操作语言(凡是对表当中的数据进行增删改的都是DML)。
insert delete update
insert 增
delete 删
update 改
这个主要是操作表中的数据data。
DDL:
数据定义语言。
凡是带有create、drop、alter的都是DDL。
DDL主要操作的是表的结构。不是表中的数据。(如新增数据库、新增表格)
create:新建,等同于增
drop:删除
alter:修改
这个增删改和DML不同,这个主要是对表结构进行操作。
TCL:
事务控制语言。
包括:
事务提交:commit;
事务回滚:rollback;
DCL:
是数据控制语言。
例如:授权grant、撤销权限revoke....
MySQL的常用命令:
注意:对于SQL语句来说,是通用的,所有的SQL语句以英文“;”结尾。另外SQL语句不区分大小写。
退出mysql :exit;
查看mysql中有哪些数据库 : show databases;
mysql> show databases;
+-----------------------------+
| Database |
+-----------------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+-----------------------------+
4 rows in set (0.05 sec)
mysql默认自带了4个数据库。
选择使用某个数据库:use xxx;
mysql> use test;
Database changed
表示正在使用一个名字叫做test的数据库。
创建数据库:create database xxx;
mysql> create database hyf;
Query OK, 1 row affected (0.04 sec)
mysql> show databases ;
+-----------------------------+
| Database |
+-----------------------------+
| hyf |
| information_schema |
| mysql |
| performance_schema |
| sys |
+-----------------------------+
5 rows in set (0.04 sec)
查看某个数据库下有哪些表:show tables; (需先进入某一个数据库)
一个数据库中可以有多张表,不同的表存储不同的信息。
查看mysql数据库的版本号:
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.5.36 |
+-----------+
查看当前使用的是哪个数据库:
mysql> select database();
+-------------+
| database() |
+-------------+
| hyf |
+-------------+
导入表:source sql后缀文件路径 (不用加分号)
注意:
路径中不要有中文!!!!
导入的是表,须先进入一个数据库!!!
查看表中所有内容:select * from 表名;
查看表的结构:desc 表名; (desc是describe的缩写,可 describe 表名)
mysql> desc dept;
+--------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+-------------+------+-----+---------+-------+
| DEPTNO | int(2) | NO | PRI | NULL | | 该行为部门编号的结构(数据存储的类型等)
| DNAME | varchar(14) | YES | | NULL | | 该行为部门名字的结构
| LOC | varchar(13) | YES | | NULL | | 该行为地理位置的结构
+--------+-------------+------+-----+---------+-------+
注意:
mysql> show
-> databases
-> ;
+--------------------+
| Database |
+--------------------+
| information_schema |
| bjpowernode |
| mysql |
| performance_schema |
| test |
+--------------------+
注意:mysql是不见“;”不执行,“;”表示结束!
可回车换行继续输入代码。
可以通过 \c 来结束未完成的SQL语句。(或Ctrl+c)
注意,Mysql 默认情况下大小写是不敏感的:
MySQL 在 windows 下是不区分大小写的,将script 文件导入 MySQL 后表名也会自动转化为小写,结果再想要将数据库导出放到linux服务器中使用时就出错了。因为在 linux下表名区分大小写而找不到表,查了很多都是说在 linux下更改 MySQL 的设置使其也不区分大小写,但是有没有办法反过来让 windows 下大小写敏感呢。其实方法是一样的,相应的更改 windows 中 MySQL 的设置就行了。
具体操作:
在 MySQL 的配置文件 my.ini 中增加一行: lower_case_table_names = 0
其中 0:区分大小写,1:不区分大小写
MySQL 在 Linux下数据库名、表名、列名、别名大小写规则是这样的:
- 数据库名与表名是严格区分大小写的;
- 表的别名是严格区分大小写的;
- 列名与列的别名在所有的情况下均是忽略大小写的;
- 变量名也是严格区分大小写的; MySQL 在 Windows 下都不区分大小写
MySQL的注释与命名规范:
MySQL用 井号# 作为注释行的开头。
注意:数据库中的命名规范与Java不同:
所有的标识符都是全部小写,单词和单词之间使用下划线进行衔接。
查询(select):
简单查询:
查询一个字段?
select 字段名 from 表名;
注意:
select和from都是关键字。
字段名和表名都是标识符。
查询多个字段可以使用英文逗号隔开。
如查询部门编号和部门名:
select deptno,dname from dept;
+--------+------------+
| deptno | dname |
+--------+------------+
| 10 | ACCOUNTING |
| 20 | RESEARCH |
| 30 | SALES |
| 40 | OPERATIONS |
+--------+------------+
查询所有字段:
第一种方式:可以把每个字段都写上
select a,b,c,d,e,f... from tablename;
第二种方式:可以使用*
select * from tablename;
星号查询的缺点:
1、效率低。(程序会先将*号转换为所有字段的名字)
2、可读性差。
在实际开发中不建议。
给查询的列起别名:
使用as关键字起别名。
语法格式:
字段 as 别名
当设置别名后,原字段将替换为别名进行输出展示。
mysql> select deptno,dname as deptname from dept;
+--------+------------+
| deptno | deptname |
+--------+------------+
| 10 | ACCOUNTING |
| 20 | RESEARCH |
| 30 | SALES |
| 40 | OPERATIONS |
+--------+------------+
注意:只是将显示的查询结果中的dname显示为deptname,原表该字段名还是叫:dname。
记住:select语句是永远都不会进行修改操作的。(因为只负责查询)
as关键字可以省略吗?
可以的,用空格间隔字段名与别名。
mysql> select deptno,dname deptname from dept;
假设起别名的时候,别名里面有空格,怎么办?
mysql> select deptno,dname dept name from dept; //程序报错!!!
解决方法:
mysql> select deptno,dname 'dept name' from dept; //加单引号
mysql> select deptno,dname "dept name" from dept; //加双引号
注意:在所有的数据库当中,字符串统一使用单引号括起来,单引号是标准,双引号在oracle数据库中用不了,但是在mysql中可以使用。为了语句的通用,尽量使用单引号。
提醒:
select中查询什么这一列就会显示什么,如:
mysql> select avg(sal),deptno from emp group by deptno order by deptno asc;
+-------------+--------+
| avg(sal) | deptno |
+-------------+--------+ # 按部门的不同分组,查询不同部门的sal的平均值和部门编码
| 2916.666667 | 10 | # 按部门编码升序排序
| 2175.000000 | 20 |
| 1566.666667 | 30 |
+-------------+--------+
3 rows in set (0.00 sec)
以上代码,因为 select avg(sal),deptno ,所以这两行打印的都是分组后的平均值和部门编码。
所以以下题目有解如下:
题目来源:查找GPA最高值_牛客题霸_牛客网 (nowcoder.com)
列参与数学运算:
假如我们想计算员工年薪?sal * 12
mysql> select ename,sal from emp;
+--------+---------+
| ename | sal |
+--------+---------+
| SMITH | 800.00 |
| 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 |
+--------+---------+
mysql> select ename,sal*12 from emp;
+--------+----------+
| ename | sal*12 |
+--------+----------+
| SMITH | 9600.00 |
| ALLEN | 19200.00 |
| WARD | 15000.00 |
| JONES | 35700.00 |
| MARTIN | 15000.00 |
| BLAKE | 34200.00 |
| CLARK | 29400.00 |
| SCOTT | 36000.00 |
| KING | 60000.00 |
| TURNER | 18000.00 |
| ADAMS | 13200.00 |
| JAMES | 11400.00 |
| FORD | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
结论:字段可以使用数学表达式!
我们也可以使用别名修改 sal*12 的显示:
mysql> select ename,sal*12 as yearsal from emp; //起别名
+--------+----------+
| ename | yearsal |
+--------+----------+
| SMITH | 9600.00 |
| ALLEN | 19200.00 |
| WARD | 15000.00 |
| JONES | 35700.00 |
| MARTIN | 15000.00 |
| BLAKE | 34200.00 |
| CLARK | 29400.00 |
| SCOTT | 36000.00 |
| KING | 60000.00 |
| TURNER | 18000.00 |
| ADAMS | 13200.00 |
| JAMES | 11400.00 |
| FORD | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
如果别名是中文,需要用单引号括起来:
mysql> select ename,sal*12 as '年薪' from emp;
+--------+----------+
| ename | 年薪 |
+--------+----------+
| SMITH | 9600.00 |
| ALLEN | 19200.00 |
| WARD | 15000.00 |
| JONES | 35700.00 |
| MARTIN | 15000.00 |
| BLAKE | 34200.00 |
| CLARK | 29400.00 |
| SCOTT | 36000.00 |
| KING | 60000.00 |
| TURNER | 18000.00 |
| ADAMS | 13200.00 |
| JAMES | 11400.00 |
| FORD | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
条件查询:
什么是条件查询?
不是将表中所有数据都查出来,而是查询符合条件的数据。
语法格式:
select
字段1,字段2,字段3....
from
表名
where
条件;
注意:条件查询需要用到 where 语句,where 必须放到 from 语句表的后面。
= 等于:
查询薪资等于800的员工姓名和编号?
select empno,ename from emp where sal = 800;
查询指定员工的编号和薪资?
select empno,sal from emp where ename = 'SMITH'; //字符串使用单引号
<>或!= 不等于:
查询薪资不等于800的员工姓名和编号?
select empno,ename from emp where sal != 800;
select empno,ename from emp where sal <> 800; // 小于号和大于号组成的不等号
查询小于 800 和 大于800 的,也就是查询不等于800的。
< 小于:
查询薪资小于2000的员工姓名和编号?
mysql> select empno,ename,sal from emp where sal < 2000;
+-------+--------+---------+
| empno | ename | sal |
+-------+--------+---------+
| 7369 | SMITH | 800.00 |
| 7499 | ALLEN | 1600.00 |
| 7521 | WARD | 1250.00 |
| 7654 | MARTIN | 1250.00 |
| 7844 | TURNER | 1500.00 |
| 7876 | ADAMS | 1100.00 |
| 7900 | JAMES | 950.00 |
| 7934 | MILLER | 1300.00 |
+-------+--------+---------+
<= 小于等于:
查询薪资小于等于3000的员工姓名和编号?
select empno,ename,sal from emp where sal <= 3000;
> 大于:
查询薪资大于3000的员工姓名和编号?
select empno,ename,sal from emp where sal > 3000;
>= 大于等于:
查询薪资大于等于3000的员工姓名和编号?
select empno,ename,sal from emp where sal >= 3000;
between … and …. 两个值之间, 等同于 >= and <=:
查询薪资在2450和3000之间的员工信息?包括2450和3000
第一种方式:>= and <= (and是并且的意思。)
select empno,ename,sal from emp where sal >= 2450 and sal <= 3000;
+-------+-------+---------+
| empno | ename | sal |
+-------+-------+---------+
| 7566 | JONES | 2975.00 |
| 7698 | BLAKE | 2850.00 |
| 7782 | CLARK | 2450.00 |
| 7788 | SCOTT | 3000.00 |
| 7902 | FORD | 3000.00 |
+-------+-------+---------+
第二种方式:between … and …
select
empno,ename,sal
from
emp
where
sal between 2450 and 3000;
注意:
1、使用between and的时候,必须遵循左小右大。
2、between and是闭区间,包括两端的值。
is null(is not null 不为空):
查询哪些员工的津贴/补助为null?
mysql> select empno,ename,sal,comm from emp where comm = null;
Empty set (0.00 sec)
mysql> select empno,ename,sal,comm from emp where comm is null;
+-------+--------+---------+------+
| empno | ename | sal | comm |
+-------+--------+---------+------+
| 7369 | SMITH | 800.00 | NULL |
| 7566 | JONES | 2975.00 | NULL |
| 7698 | BLAKE | 2850.00 | NULL |
| 7782 | CLARK | 2450.00 | NULL |
| 7788 | SCOTT | 3000.00 | NULL |
| 7839 | KING | 5000.00 | NULL |
| 7876 | ADAMS | 1100.00 | NULL |
| 7900 | JAMES | 950.00 | NULL |
| 7902 | FORD | 3000.00 | NULL |
| 7934 | MILLER | 1300.00 | NULL |
+-------+--------+---------+------+
10 rows in set (0.00 sec)
注意:在数据库当中null不能使用等号进行衡量。需要使用is null。因为数据库中的null代表什么也没有,它不是一个值,所以不能使用等号衡量。
查询哪些员工的津贴/补助不为null?
select empno,ename,sal,comm from emp where comm is not null;
+-------+--------+---------+---------+
| empno | ename | sal | comm |
+-------+--------+---------+---------+
| 7499 | ALLEN | 1600.00 | 300.00 |
| 7521 | WARD | 1250.00 | 500.00 |
| 7654 | MARTIN | 1250.00 | 1400.00 |
| 7844 | TURNER | 1500.00 | 0.00 |
+-------+--------+---------+---------+
and 并且:
查询工作岗位是MANAGER并且工资大于2500的员工信息?
select
empno,ename,job,sal
from
emp
where
job = 'MANAGER' and sal > 2500;
+-------+-------+---------+---------+
| empno | ename | job | sal |
+-------+-------+---------+---------+
| 7566 | JONES | MANAGER | 2975.00 |
| 7698 | BLAKE | MANAGER | 2850.00 |
+-------+-------+---------+---------+
or 或者:
查询工作岗位是 MANAGER 和 SALESMAN 的员工?
select empno,ename,job from emp where job = 'MANAGER';
select empno,ename,job from emp where job = 'SALESMAN';
select
empno,ename,job
from
emp
where
job = 'MANAGER' or job = 'SALESMAN';
+-------+--------+----------+
| empno | ename | job |
+-------+--------+----------+
| 7499 | ALLEN | SALESMAN |
| 7521 | WARD | SALESMAN |
| 7566 | JONES | MANAGER |
| 7654 | MARTIN | SALESMAN |
| 7698 | BLAKE | MANAGER |
| 7782 | CLARK | MANAGER |
| 7844 | TURNER | SALESMAN |
+-------+--------+----------+
and和or同时出现的话,有优先级问题吗?
查询工资大于2500,并且部门编号为10或20部门的员工?
select
*
from
emp
where
sal > 2500 and deptno = 10 or deptno = 20;
分析以上语句的问题?
and优先级比or高。
以上语句会先执行and,然后执行or。
以上这个语句表示什么含义?
找出工资大于2500并且部门编号为10的员工,或者20部门所有员工找出来。
正确方法:
select
*
from
emp
where
sal > 2500 and (deptno = 10 or deptno = 20);
and和or同时出现,and优先级较高。如果想让or先执行,需要加“小括号”。
以后在开发中,如果不确定优先级,就加小括号就行了。
in 包含,相当于多个 or (not in 不在这个范围中):
查询工作岗位是MANAGER和SALESMAN的员工?
select empno,ename,job from emp where job = 'MANAGER' or job = 'SALESMAN';
select empno,ename,job from emp where job in('MANAGER', 'SALESMAN');
+-------+--------+----------+
| empno | ename | job |
+-------+--------+----------+
| 7499 | ALLEN | SALESMAN |
| 7521 | WARD | SALESMAN |
| 7566 | JONES | MANAGER |
| 7654 | MARTIN | SALESMAN |
| 7698 | BLAKE | MANAGER |
| 7782 | CLARK | MANAGER |
| 7844 | TURNER | SALESMAN |
+-------+--------+----------+
where job = 'MANAGER' or job = 'SALESMAN';
等价于
where job in('MANAGER', 'SALESMAN');
注意:in不是一个区间。in后面跟的是具体的值。in相对于多个or。
如:select ename,sal from emp where sal in(800, 5000); //这个不是表示800到5000都找出来。
not in 表示不在这几个值当中的数据:
select ename,sal from emp where sal not in(800, 5000, 3000);
+--------+---------+
| ename | sal |
+--------+---------+
| ALLEN | 1600.00 |
| WARD | 1250.00 |
| JONES | 2975.00 |
| MARTIN | 1250.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| TURNER | 1500.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| MILLER | 1300.00 |
+--------+---------+
in 子查询:
in的基本语法是:
where value in (value1,value2,value3,…)
但除此之外,in后面的括号内还可以跟查询语句,如:
where (gpa) in (select min(gpa) from user_profile)
这被称为in的子查询。
in的子查询还可以查询多个语句,但多个语句必须一一对应,不能多,不能少,顺序不能换。
where (university,gpa) in (select university,min(gpa) from user_profile)
not 可以取非,主要用在 is 或 in 中
is null
is not null
in
not in
like
称为模糊查询,支持%或下划线匹配
% 表示 任意多个字符。
_ 表示 任意一个字符。
(%是一个特殊的符号,_ 也是一个特殊符号)
找出名字中含有O的?
mysql> select ename from emp where ename like '%O%';
找出名字以T结尾的?
mysql> select ename from emp where ename like '%T';
找出名字以K开始的?
mysql> select ename from emp where ename like 'K%';
找出第二个字每是A的?
mysql> select ename from emp where ename like '_A%';
找出第三个字母是R的?
mysql> select ename from emp where ename like '__R%';
假如现在有一个名为 t_student 的学生表:
name
---------
zhangsan
lisi
wangwu
zhaoliu
jack_son
找出名字中有“_”的?
mysql> select name from t_student where name like '%_%';
// 这样不行,因为下划线是一个特殊字符。
正确方法(使用转义字符):
mysql> select name from t_student where name like '%\_%'; // \转义字符。
+----------+
| name |
+----------+
| jack_son |
+----------+
排序:
默认升序:
查询所有员工薪资,排序?
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 |
| FORD | 3000.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
+--------+---------+
表示查询 emp 表的 ename 和 sal 字段,并按 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 |
| MARTIN | 1250.00 |
| WARD | 1250.00 |
| ADAMS | 1100.00 |
| JAMES | 950.00 |
| SMITH | 800.00 |
+--------+---------+
指定升序:
select
ename,sal
from
emp
order by
sal asc;
+--------+---------+
| 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 |
| FORD | 3000.00 |
| SCOTT | 3000.00 |
| KING | 5000.00 |
+--------+---------+
按多个字段排序:
查询员工名字和薪资,要求按照薪资升序,如果薪资一样的话,再按照名字升序排列。
select
ename,sal
from
emp
order by
sal asc, ename asc; // sal在前,起主导,只有sal相等的时候,才会考虑启用ename排序。
+--------+---------+
| 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 |
+--------+---------+
注意:当多个字段同时参与排序时,如果想让所有字段都降序,必须在每个字段后面都添加一个 desc ,否则像下面这种情况,就会变成先按gpa升序排序,再按age降序排序。
select device_id,gpa,age
from user_profile
order by gpa,age desc;
根据字段的位置也可以排序:
按照查询结果的第2列sal排序。
select ename,sal from emp order by 2; // 2表示第二列。第二列是sal
不建议在开发中这样写,因为不健壮,列的顺序很容易发生改变,列顺序修改之后,2就废了。
案例:
找出工资在1250到3000之间的员工信息,要求按照薪资降序排列。
select
ename,sal
from
emp
where
sal between 1250 and 3000
order by
sal desc;
+--------+---------+
| ename | sal |
+--------+---------+
| FORD | 3000.00 |
| SCOTT | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
| CLARK | 2450.00 |
| ALLEN | 1600.00 |
| TURNER | 1500.00 |
| MILLER | 1300.00 |
| MARTIN | 1250.00 |
| WARD | 1250.00 |
+--------+---------+
关键字的书写顺序:
select
...
from
...
where
...
order by
...
关键字的执行顺序(必须掌握):
第一步:from
第二步:where
第三步:select
第四步:order by(排序总是在最后执行!)
数据处理函数:
数据处理函数又被称为单行处理函数
单行处理函数的特点:一个输入对应一个输出。
和单行处理函数相对的是:多行处理函数。(又称分组函数)
(多行处理函数特点:多个输入,对应1个输出!如求薪资一列的和。)
常见的单行处理函数:
Lower () 转小写输出:
mysql> select lower(ename) from emp;
+--------------+
| lower(ename) |
+--------------+
| smith |
| allen |
| ward |
| jones |
| martin |
| blake |
| clark |
| scott |
| king |
| turner |
| adams |
| james |
| ford |
| miller |
+--------------+
14 rows in set (0.04 sec)
改别名:
mysql> 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.04 sec)
upper() 转大写输出:
mysql> select upper(name) as name from t_student;
+----------+
| name |
+----------+
| ZHANGSAN |
| LISI |
| WANGWU |
| JACK_SON |
+----------+
substr 取子串(substr( 被截取的字符串, 起始下标,截取的长度)):
语法格式:
方式一:
substr(字段, 开始位置, 要取字符长度)
方式二:
substr(字段, 开始位置)
表示从开始位置一直取到最后一个字符。
注意:
起始下标从1开始,没有0。
找出员工名字第一个字母是A的员工信息?
第一种方式:模糊查询
select ename from emp where ename like 'A%';
第二种方式:substr函数
select
ename
from
emp
where
substr(ename,1,1) = 'A';
concat() 函数进行字符串的拼接:
select concat(empno,ename) from emp;
+---------------------+
| concat(empno,ename) |
+---------------------+
| 7369SMITH |
| 7499ALLEN |
| 7521WARD |
| 7566JONES |
| 7654MARTIN |
| 7698BLAKE |
| 7782CLARK |
| 7788SCOTT |
| 7839KING |
| 7844TURNER |
| 7876ADAMS |
| 7900JAMES |
| 7902FORD |
| 7934MILLER |
+---------------------+
length() 取长度:
select length(ename) as enamelength from emp;
+-------------+
| enamelength |
+-------------+
| 5 |
| 5 |
| 4 |
| 5 |
| 6 |
| 5 |
| 5 |
| 5 |
| 4 |
| 6 |
| 5 |
| 5 |
| 4 |
| 6 |
+-------------+
首字母大写?
先把首字母取出来并转为大写:
select upper(substr(name,1,1)) from t_student;
再把后面的字符取出来:
select substr(name,2,length(name) - 1) from t_student;
使用 concat() 函数拼接字符串:
select concat(upper(substr(name,1,1)),substr(name,2,length(name) - 1)) as result from t_student;
+----------+
| result |
+----------+
| Zhangsan |
| Lisi |
| Wangwu |
| Jack_son |
+----------+
trim() 去空格:
查询其他代码传过来的可能带空格的用于查询的字符:
mysql> select * from emp where ename = ' KING';
Empty set (0.00 sec)
mysql> select * from emp where ename = trim(' KING');
+-------+-------+-----------+------+------------+---------+------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+-------+-----------+------+------------+---------+------+--------+
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 |
+-------+-------+-----------+------+------------+---------+------+--------+
case..when..then..when..then..else..end:
当员工的工作岗位是MANAGER的时候,工资上调10%,当工作岗位是SALESMAN的时候,工资上调50%,其它正常。(不修改数据库,只是将查询结果显示为工资上调)
该语句本身相对于一个列,列中的数据由其中的数学运算得出。
case函数分为简单函数和搜索函数两种。
case 是 when 比较的内容的来源所在列。
then 后面是一列中不同行的单元格所要显示的数据/内容。
when...then...表示 “当...时单元格显示为...” 。
else 后面是当没有when匹配这个值时单元格所要显示的内容。
as是给这一列的表头起别名,如果不起别名,表头将是这一整块case语句(case...end)。
简单函数:
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 |
+--------+-----------+---------+---------+
简单 CASE 语句仅允许将表达式的值与一组不同的值进行匹配。 为了执行更复杂的匹配,如范围,则可以使用可搜索 CASE 语句。 可搜索 CASE 语句等同于 IF 语句,但是它的构造更加可读。
搜索函数:
在 when 中进行判断,返回第一个符合 when 中条件的值。
(case
when age >= 18 then '成年'
when age < 18 then '未成年'
else '保密' end) as '是否成年'
case子句后直接跟when,when后可以跟范围。
if判断:
IF表达式:
if(expr1,expr2,expr3)
如果 expr1 是ture,则 if()的返回值为expr2,否则返回值则为 expr3。
if() 的返回值可以是数字值,也可以是字符串值,具体情况视其所在语境而定。
示例:
select *,if(sva=1,"男","女") as ssva from taname where sva != ""
作为表达式的 if 也可以用CASE when来实现:
select CASE sva WHEN 1 THEN '男' ELSE '女' END as ssva from taname where sva != ''
IF…ELSE…语句:
if search_condition then
statement_list
[elseif search_condition then]
statement_list ...
[else
statement_list]
end if;
当IF中条件search_condition成立时,执行THEN后的statement_list语句,否则判断ELSEIF中的条件,成立则执行其后的statement_list语句,否则继续判断其他分支。当所有分支的条件均不成立时,执行ELSE分支。search_condition是一个条件表达式,可以由“=、<、<=、>、>=、!=”等条件运算符组成,并且可以使用AND、OR、NOT对多个表达式进行组合。
中括号内的内容不是必须的。
IFNULL语句:
在所有数据库当中,只要有NULL参与的数学运算,最终结果就是NULL。为了避免这个现象,需要使用ifnull函数。
ifnull(expr1,expr2)
假如expr1不为null,则返回expr1,否则返回expr2。
示例:
mysql> select ename, sal + comm as salcomm from emp; // 薪资sal + 补助 comm
+--------+---------+
| ename | salcomm |
+--------+---------+
| SMITH | NULL | // 有些人只有工资没有补助,运算结果就会为NULL
| ALLEN | 1900.00 |
| WARD | 1750.00 |
| JONES | NULL |
| MARTIN | 2650.00 |
| BLAKE | NULL |
| CLARK | NULL |
| SCOTT | NULL |
| KING | NULL |
| TURNER | 1500.00 |
| ADAMS | NULL |
| JAMES | NULL |
| FORD | NULL |
| MILLER | NULL |
+--------+---------+
select ename, (sal + ifnull(comm, 0)) * 12 as yearsal from emp;
+--------+----------+
| ename | yearsal |
+--------+----------+
| SMITH | 9600.00 |
| ALLEN | 22800.00 |
| WARD | 21000.00 |
| JONES | 35700.00 |
| MARTIN | 31800.00 |
| BLAKE | 34200.00 |
| CLARK | 29400.00 |
| SCOTT | 36000.00 |
| KING | 60000.00 |
| TURNER | 18000.00 |
| ADAMS | 13200.00 |
| JAMES | 11400.00 |
| FORD | 36000.00 |
| MILLER | 15600.00 |
+--------+----------+
以上,均可以在语句后面给该语句起别名。
round() 四舍五入:
在此之前我们先介绍一下select的一个特性:
我们都知道,select 字段 from 表名; 是在一个表中查找该字段的数据。
但当 字段处 填上 字符 或 数字,那么会打印出一系列的重复数据。数据的个数就是该表的行数,数据的表头默认是该字符/数字。
重复的字符:
mysql> select 'abc' from emp;
+-----+
| abc |
+-----+
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
+-----+
14 rows in set (0.00 sec)
mysql> select 'abc' as bieming from emp;
+---------+
| bieming |
+---------+
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
| abc |
+---------+
14 rows in set (0.00 sec)
注意:
mysql> select abc from emp;
ERROR 1054 (42S22): Unknown column 'abc' in 'field list'
这样肯定报错,因为会把abc当做一个字段的名字,去emp表中找abc字段去了。找不到,报错。
重复的数字:
select 1000 as num from emp; // 1000 也是被当做一个字面量/字面值。
+------+
| num |
+------+
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
| 1000 |
+------+
我们可以用这个特性来查看各个函数的运行结果。
下面我们将用该特性来直观地查看 round() 函数对数据的变化。
round() 的语法格式:
round(数据, 要保留的小数位数)
“ 要保留的小数位数 ”处,0表示四舍五入保留整数,1表示四舍五入保留一位小数,2表示四舍五入保留两位小数,-1表示保留到十位。
select round(1236.567, -2) as result from emp;
+--------+
| result |
+--------+
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
| 1200 |
+--------+
rand() 生成0到1随机数:
100以内的随机数:
mysql> select round(rand()*100,0) from emp; // 取随机数,取整
+---------------------+
| round(rand()*100,0) |
+---------------------+
| 76 |
| 29 |
| 15 |
| 88 |
| 95 |
| 9 |
| 63 |
| 89 |
| 54 |
| 3 |
| 54 |
| 61 |
| 42 |
| 28 |
+---------------------+
分组函数(多行处理函数):
多行处理函数的特点:输入多行,最终输出一行。
5个分组函数:
count() 计数
sum() 求和
avg() 平均值
max() 最大值
min() 最小值
注意:
分组函数在使用的时候必须先进行分组,然后才能用。
如果你没有对数据进行分组,整张表默认为一组。
找出最高工资:
mysql> select max(sal) from emp;
+----------+
| max(sal) |
+----------+
| 5000.00 |
+----------+
找出最低工资:
mysql> select min(sal) from emp;
+----------+
| min(sal) |
+----------+
| 800.00 |
+----------+
计算工资和:
mysql> select sum(sal) from emp;
+----------+
| sum(sal) |
+----------+
| 29025.00 |
+----------+
计算平均工资:
mysql> select avg(sal) from emp;
+-------------+
| avg(sal) |
+-------------+
| 2073.214286 |
+-------------+
计算员工数量:
mysql> select count(ename) from emp;
+--------------+
| count(ename) |
+--------------+
| 14 |
+--------------+
分组函数在使用的注意事项:
第一点:分组函数自动忽略NULL,你不需要提前对NULL进行处理。
mysql> select sum(comm) from emp; // 补助
+-----------+
| sum(comm) |
+-----------+
| 2200.00 |
+-----------+
不需要sum(ifnull(comm)),这样就会降低代码的运行效率了。
第二点:分组函数中count(*)和count(具体字段)有什么区别?
mysql> select count(*) from emp;
+----------+
| count(*) |
+----------+
| 14 |
+----------+
mysql> select count(comm) from emp;
+-------------+
| count(comm) |
+-------------+
| 4 |
+-------------+
count(具体字段):表示统计分组后该字段下所有不为NULL的元素的总数。
count(*):统计分组后一个组的总行数。(包含某一字段下为NULL的元素)
注意:count函数中,重复的数据会多次计算,如果要去重,就得搭配 distinct 使用,即count(distinct xxxxxx)。
第三点:分组函数不能够直接使用在where子句中。
找出比最低工资高的员工信息。
select ename,sal from emp where sal > min(sal);
说完分组查询(group by)之后就明白了了。
第四点:所有的分组函数可以组合起来一起用。
select sum(sal),min(sal),max(sal),avg(sal),count(*) from emp;
+----------+----------+----------+-------------+----------+
| sum(sal) | min(sal) | max(sal) | avg(sal) | count(*) |
+----------+----------+----------+-------------+----------+
| 29025.00 | 800.00 | 5000.00 | 2073.214286 | 14 |
+----------+----------+----------+-------------+----------+
第五点:分组函数不能嵌套使用。(五个)
如 count(avg(sal)) 、sum(count(sal)) 都是不可以的。
但数据处理函数可以!
分组查询(重要):
为什么要分组查询:
在实际的应用中,可能有这样的需求,需要先进行分组,然后对每一组的数据进行操作,如计算每个部门的工资和、计算每个工作岗位的平均薪资、找出每个工作岗位的最高薪资等。这个时候我们需要使用分组查询,怎么进行分组查询呢?
语法格式:
select // 查询
...
from // 来源
...
where // 过滤
...
group by // 分组
...
order by // 排序
...
以上关键字的顺序不能颠倒,需要记忆。
以上代码的执行顺序是什么?
1. from
2. where
3. group by
4. select
5. order by
先from拿到表,再where进行过滤,接着group by进行分组,然后select查询,最后order by排序输出。
提醒:
以下题有题解如下:
因为 where 在 select 之前执行,所以等到 select 执行的时候,gender 已经被 where 筛选为 'male' 了,所以 select 中应直接 count(gender) 即可。
不可以 count(gender='male') ,因为分组函数里不能加条件,如 select count('clerk'=job) from emp; 统计的永远是emp表的行数。
不然还有 where 干嘛。
mysql> select * from 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)
mysql> select count('clerk'=job) from emp ;
+--------------------+
| count('clerk'=job) |
+--------------------+
| 14 |
+--------------------+
1 row in set (0.00 sec)
为什么分组函数不能直接使用在where后面?
select ename,sal from emp where sal > min(sal); //报错。
mysql> select count(ename) from emp; // 可以
因为分组函数在使用的时候必须先分组之后才能使用。where 的执行顺序大于 group by ,where 执行时 group by 还没执行,整张表还没被看成一组(不手动分组时会把整张表看成一组),所以分组函数不能使用。
因此分组函数不能够直接使用在where子句中。
而分组函数可以使用在select的子句中,因为select的执行顺序在group by之后,此时整张表已经被看作了一组,可以使用分组函数。
找出每个工作岗位的工资和?
实现思路:按照工作岗位分组,然后对工资求和。
select
job,sum(sal)
from
emp
group by
job;
+-----------+----------+
| job | sum(sal) |
+-----------+----------+
| ANALYST | 6000.00 |
| CLERK | 4150.00 |
| MANAGER | 8275.00 |
| PRESIDENT | 5000.00 |
| SALESMAN | 5600.00 |
+-----------+----------+
以上这个语句的执行顺序?
先从emp表中查询数据。
根据job字段进行分组。
然后对每一组(不同职位)的数据进行求和 sum(sal) 。
分组后查询的应该是与分组有关系的字段:
如,按工作岗位的总工资进行分组,那么查询的应该是 工作岗位job 和 该工作岗位的总薪资sum(sal) ,而不应该查询员工的姓名(因为输出后对不上号,一个工作岗位对应多个人,但只会输出一个人)(即使语法没有错误可以输出,但没有意义)
select ename,job,sum(sal) from emp group by job;
+-------+-----------+----------+
| ename | job | sum(sal) |
+-------+-----------+----------+
| SCOTT | ANALYST | 6000.00 |
| SMITH | CLERK | 4150.00 |
| JONES | MANAGER | 8275.00 |
| KING | PRESIDENT | 5000.00 |
| ALLEN | SALESMAN | 5600.00 |
+-------+-----------+----------+
以上语句在mysql中可以执行,但是毫无意义。
以上语句在oracle中执行报错。oracle的语法比mysql的语法严格。(mysql的语法相对来说松散一些!)
也就是说,在一条select语句的字句当中,如果有group by语句的话,select的字句只能有:参加分组的字段,以及分组函数,其它的一律不能跟。( 如按照部门编号分组求最高薪资,那么只能查询部门编号还有我们求最高薪资的函数max(sal) )
分组+排序(group by + order by):
找出每个工作岗位的工资和:
select
job,sum(sal)
from
emp
group by
job
order by
sum(sal);
+-----------+----------+
| job | sum(sal) |
+-----------+----------+
| CLERK | 4150.00 |
| PRESIDENT | 5000.00 |
| SALESMAN | 5600.00 |
| ANALYST | 6000.00 |
| MANAGER | 8275.00 |
+-----------+----------+
5 rows in set (0.00 sec)
注意:我们 order by 排序时,不能 order by sal ,因为我们不是按照工资排序,而是按照工资总和排序。
双分组:
找出“每个部门,不同工作岗位”的最高薪资?(不同部门,即使同一个工作岗位也视作不同)
技巧:把参与分组的字段联合起来看成一个字段,即 | MANAGER | 20 | 和 | MANAGER | 30 | 是不同的。
select
deptno, job, max(sal)
from
emp
group by
deptno, job;
+--------+-----------+----------+
| deptno | job | max(sal) |
+--------+-----------+----------+
| 10 | CLERK | 1300.00 |
| 10 | MANAGER | 2450.00 |
| 10 | PRESIDENT | 5000.00 |
| 20 | ANALYST | 3000.00 |
| 20 | CLERK | 1100.00 |
| 20 | MANAGER | 2975.00 |
| 30 | CLERK | 950.00 |
| 30 | MANAGER | 2850.00 |
| 30 | SALESMAN | 1600.00 |
+--------+-----------+----------+
having字句过滤分组函数结果:
假如我们想找出每个部门最高薪资,要求显示最高薪资大于3000的?
第一步:找出每个部门最高薪资:
按照部门编号分组,求每一组最大值。
select deptno,max(sal) from emp group by deptno;
+--------+----------+
| deptno | max(sal) |
+--------+----------+
| 10 | 5000.00 |
| 20 | 3000.00 |
| 30 | 2850.00 |
+--------+----------+
第二步:要求显示最高薪资大于3000:
错误方法:
select
deptno,max(sal)
from
emp
where
max(sal) > 3000;
group by
deptno
分组函数不能使用在 where 的字句中。
为了达到筛选的效果,我们可以使用having字句:
select
deptno,max(sal)
from
emp
group by
deptno
having
max(sal) > 3000;
+--------+----------+
| deptno | max(sal) |
+--------+----------+
| 10 | 5000.00 |
+--------+----------+
使用having可以对分完组之后的数据进一步过滤。
注意事项:
having不能单独使用,having不能代替where,having 必须和 group by (分组函数)联合使用。
思考一个问题:以上的sql语句执行效率是不是低?
假如我们想找出每个部门最高薪资,要求显示最高薪资大于3000的?
比较低,因为我们想找出每个部门最高薪资,所有我们对所有人员都施行了分组函数 max() ,但有一些部门在取得max后因为所取得的最高薪资小于3000,因此不显示了,但分组函数 max() 在它们身上花费了时间,很不值当。
所以我们可以先有 where 快速筛选出大于 3000 的人,再对这些人进行部门的分组,再求出每个部门的最大薪资。(3000以下的都不参与分组,都不对其施行分组函数)
select
deptno,max(sal)
from
emp
where
sal > 3000
group by
deptno;
+--------+----------+
| deptno | max(sal) |
+--------+----------+
| 10 | 5000.00 |
+--------+----------+
优化策略:
where和having,优先选择where,where实在完成不了了,再选择having。
必须使用having的情况(where 无法解决问题的):
找出每个部门平均薪资,要求显示平均薪资高于2500的。
第一步:找出每个部门平均薪资:
select deptno,avg(sal) from emp group by deptno;
+--------+-------------+
| deptno | avg(sal) |
+--------+-------------+
| 10 | 2916.666667 |
| 20 | 2175.000000 |
| 30 | 1566.666667 |
+--------+-------------+
第二步:要求显示平均薪资高于2500的:
select
deptno,avg(sal)
from
emp
group by
deptno
having
avg(sal) > 2500;
+--------+-------------+
| deptno | avg(sal) |
+--------+-------------+
| 10 | 2916.666667 |
+--------+-------------+
上述情况中,平均薪资一开始不存在,只有在调用分组函数后才存在,而分组函数不能写在 where 字句中(不能where avg(sal) >2500),所以这时候就必须使用 having 了。
单表查询总结:
关键字顺序:
select
...
from
...
where
...
group by
...
having
...
order by
...
以上关键字只能按照这个顺序来,不能颠倒。
代码执行顺序:
1. from
2. where
3. group by
4. having
5. select
6. order by
从某张表中查询数据,先经过where条件筛选出有价值的数据。再对这些有价值的数据进行分组。分组之后可以使用having继续筛选。select查询出来。最后排序输出!
除了 where 其他都可以使用分组函数。
综合案例:
找出每个岗位的平均薪资,要求显示平均薪资大于1500的,除MANAGER岗位之外,要求按照平均薪资降序排。
select
job, avg(sal) as avgsal
from
emp
where
job <> 'MANAGER'
group by
job
having
avg(sal) > 1500
order by
avgsal desc; // 因为先执行 select 再执行 order by ,所以 order by 中可以直接使用别名 avgsal
+-----------+-------------+
| job | avgsal |
+-----------+-------------+
| PRESIDENT | 5000.000000 |
| ANALYST | 3000.000000 |
+-----------+-------------+
查询结果去除重复记录【distinct】:
注意:select查询并不会改变原表的数据,只是查询结果去重。
去重需要使用一个关键字:distinct
mysql> select distinct job from emp;
+-----------+
| job |
+-----------+
| CLERK |
| SALESMAN |
| MANAGER |
| ANALYST |
| PRESIDENT |
+-----------+
注意:distinct只能出现在所有字段的最前方。
以下代码是错误的。
mysql> select ename,distinct job from emp;
联合去重:
注意:distinct出现在多个字段之前,表示多个字段联合起来去重。
(如:distinct job,deptno,相对于把 job 数据中的字符串与 deptno 数据中的字符串拼接起来,对这个拼接后的字符串进行查重)(如原表中有两个 | SALESMAN | 30 | ,经过联合去重后,只会出现一个 | SALESMAN | 30 | ,但会有 | MANAGER | 20 | | MANAGER | 30 | 或 | SALESMAN | 30 | | MANAGER | 30 | )
与双分组方法一致!!!
原表:
mysql> select job,deptno from emp;
+-----------+--------+
| job | deptno |
+-----------+--------+
| CLERK | 20 |
| SALESMAN | 30 |
| SALESMAN | 30 |
| MANAGER | 20 |
| SALESMAN | 30 |
| MANAGER | 30 |
| MANAGER | 10 |
| ANALYST | 20 |
| PRESIDENT | 10 |
| SALESMAN | 30 |
| CLERK | 20 |
| CLERK | 30 |
| ANALYST | 20 |
| CLERK | 10 |
+-----------+--------+
去重后:
mysql> select distinct job,deptno from emp;
+-----------+--------+
| job | deptno |
+-----------+--------+
| CLERK | 20 |
| SALESMAN | 30 |
| MANAGER | 20 |
| MANAGER | 30 |
| MANAGER | 10 |
| ANALYST | 20 |
| PRESIDENT | 10 |
| CLERK | 30 |
| CLERK | 10 |
+-----------+--------+
案例:统计一下工作岗位的数量?
select count(distinct job) from emp;
+---------------------+
| count(distinct job) |
+---------------------+
| 5 |
+---------------------+
连接查询:
什么是连接查询?
从一张表中单独查询,称为单表查询。
emp表和dept表联合起来查询数据,从emp表中取员工名字,从dept表中取部门名字。
这种跨表查询,多张表联合起来查询数据,被称为连接查询。
连接查询的分类?
根据语法的年代分类:
SQL92:1992年的时候出现的语法
SQL99:1999年的时候出现的语法
我们重点学习SQL99。(这个过程中简单演示一个SQL92的例子)
根据表连接的方式分类:
内连接:
等值连接
非等值连接
自连接
外连接:
左外连接(左连接)
右外连接(右连接)
全连接(不讲,几乎不用)
当两张表进行连接查询时,没有任何条件的限制会发生什么现象?
案例:查询每个员工所在部门名称?
表一(有员工名和部门编号):
mysql> select ename,deptno from emp;
+--------+--------+
| ename | deptno |
+--------+--------+
| SMITH | 20 |
| ALLEN | 30 |
| WARD | 30 |
| JONES | 20 |
| MARTIN | 30 |
| BLAKE | 30 |
| CLARK | 10 |
| SCOTT | 20 |
| KING | 10 |
| TURNER | 30 |
| ADAMS | 20 |
| JAMES | 30 |
| FORD | 20 |
| MILLER | 10 |
+--------+--------+
表二(有部门编号和部门名称以及工作地点):
mysql> select * from dept;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
当两张表连接没有任何条件限制时:
select ename,dname from emp, dept;
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | ACCOUNTING |
| SMITH | RESEARCH |
| SMITH | SALES |
| SMITH | OPERATIONS |
| ALLEN | ACCOUNTING |
| ALLEN | RESEARCH |
| ALLEN | SALES |
| ALLEN | OPERATIONS |
...
56 rows in set (0.00 sec)
当两张表进行连接查询,没有任何条件限制的时候,最终查询结果条数,是两张表条数的乘积,这种现象被称为:笛卡尔积现象。(表一所查询字段下的所有数据都分别匹配表二所查询字段下的所有数据)
怎么避免笛卡尔积现象?
连接时加条件,满足这个条件的记录被筛选出来:
select
ename,dname
from
emp, dept
where
emp.deptno = dept.deptno;
思考:最终查询的结果条数是14条,但是匹配的过程中,匹配的次数减少了吗?
匹配次数还是56次,只不过进行了四选一。次数没有减少。
怎么进一步提升效率:
因为在上述代码中,MySQL会去 emp 中查找 ename ,也会去 dept 中 查找 ename (虽然查不到有该字段)。同理,MySQL会去 dept 中查找 dname ,也会去 emp 中 查找 dname (虽然查不到有该字段)。
那么我们可以为其指定查询的表:
select
emp.ename,dept.dname
from
emp, dept
where
emp.deptno = dept.deptno; // 条件
为了方便,我们还可以为表的名称起一个别名:
注意,只有在 select 语句内的定义的别名才会输出到表头上,其他地方定义的不会,如 from 语句中。
select
e.ename,d.dname
from
emp e, dept d // from 先执行,所以在 from 语句内起别名
where
e.deptno = d.deptno; //SQL92语法。
+--------+------------+
| ename | dname | // 此处的别名仅在查询中使用,并不会带到输出语句中
+--------+------------+
| CLARK | ACCOUNTING |
| KING | ACCOUNTING |
| MILLER | ACCOUNTING |
| SMITH | RESEARCH |
| JONES | RESEARCH |
| SCOTT | RESEARCH |
| ADAMS | RESEARCH |
| FORD | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| MARTIN | SALES |
| BLAKE | SALES |
| TURNER | SALES |
| JAMES | SALES |
+--------+------------+
匹配次数还是56次,只不过进行了四选一。次数没有减少。
注意:通过笛卡尔积现象得出,表的连接次数越多效率越低,尽量避免表的连接次数。
内连接之等值连接:
表的连接条件是等量关系,所以被称为等值连接。
案例:查询每个员工所在部门名称,显示员工名和部门名?
SQL92语法:
select
e.ename,d.dname
from
emp e, dept d
where
e.deptno = d.deptno;
sql92的缺点:结构不清晰,表的连接条件,和后期进一步筛选的条件,都放到了where后面。
SQL99语法:
select
e.ename,d.dname
from
emp e // sql99也可以设置别名
join // 表连接
dept d // sql99也可以设置别名
on // 表的连接条件
e.deptno = d.deptno;
where // 筛选条件
... // where 可有可没有
from 和 join 中填写用于内连接的两个表名。
其实 join 前面还有一个inner,inter 可以省略(带着inner可读性更好!!!一眼就能看出来是内连接)
select
e.ename,d.dname
from
emp e
inner join
dept d
on
e.deptno = d.deptno; // 条件是等量关系,所以被称为等值连接。
where
... // where 可有可没有
sql99优点:表连接的条件是独立的,连接之后,如果还需要进一步筛选,再往后继续添加where。
内连接之非等值连接:
表的连接条件不是一个等量关系,称为非等值连接。
案例:找出每个员工的薪资等级,要求显示员工名、薪资、薪资等级?
薪资等级表:
mysql> select * from salgrade;
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+
查询如下:
select
e.ename, e.sal, s.grade
from
emp e
join
salgrade s
on
e.sal between s.losal and s.hisal; // 条件不是一个等量关系,称为非等值连接。
其中,s.losal 和 s.hisal 虽然没有查询(输出)但可以使用。
因为也是内连接,所以 join 前面也有一个inner,inter 可以省略(带着inner可读性更好!!!一眼就能看出来是内连接)
select
e.ename, e.sal, s.grade
from
emp e
inner 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 |
+--------+---------+-------+
内连接之自连接:
一张表与自身形成连接(也可以看成两张一样的表),这种连接方式被称为自连接。
案例:查询员工的上级领导,要求显示员工名和对应的领导名?
原表:
mysql> select empno,ename,mgr from emp;
+-------+--------+------+
| empno | ename | mgr |
+-------+--------+------+
| 7369 | SMITH | 7902 |
| 7499 | ALLEN | 7698 |
| 7521 | WARD | 7698 |
| 7566 | JONES | 7839 |
| 7654 | MARTIN | 7698 |
| 7698 | BLAKE | 7839 |
| 7782 | CLARK | 7839 |
| 7788 | SCOTT | 7566 |
| 7839 | KING | NULL |
| 7844 | TURNER | 7698 |
| 7876 | ADAMS | 7788 |
| 7900 | JAMES | 7698 |
| 7902 | FORD | 7566 |
| 7934 | MILLER | 7782 |
+-------+--------+------+
查询方法:给同一张表起两个不一样的别名。
查询如下:
select
a.ename as '员工名', b.ename as '领导名'
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条记录,没有 KING,因为 KING 没有上级领导。
外连接(左外连接/左连接、右外连接/右连接):
员工表:
mysql> select * from emp; e
+-------+--------+-----------+------+------------+---------+---------+--------+
| 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 |
+-------+--------+-----------+------+------------+---------+---------+--------+
部门表:
mysql> select * from dept; d
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
我们可以看到,部门表中有一个部门编号为 40 的 OPERATIONS 部门,但员工表中没有人是这个部门的,如果我们想对两张表进行连接后,除了能显示这两张表匹配上的数据之外,还想显示部门表的未匹配上的所有数据,这时候就要用到我们的外连接了。
外连接(右外连接):
select
e.ename,d.dname
from
emp e
right join
dept d
on
e.deptno = d.deptno;
+--------+------------+
| 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.04 sec)
同内连接一样,外连接也省略了员工关键字:outer。可以带着,带着可读性强。
select
e.ename,d.dname
from
emp e
right outer join
dept d
on
e.deptno = d.deptno;
right代表什么:表示将join关键字右边的这张表看成主表,主要是为了将这张表的数据全部查询出来,捎带着关联查询左边的表。
在外连接当中,两张表连接,产生了主次关系(内连接两张表是平等的)。
外连接(左外连接):
select
e.ename,d.dname
from
dept d
left join
emp e
on
e.deptno = d.deptno;
// outer省略了,可带着,带着可读性强。
select
e.ename,d.dname
from
dept d
left outer join
emp e
on
e.deptno = d.deptno;
带有right的是右外连接,又叫做右连接。
带有left的是左外连接,又叫做左连接。
任何一个右连接都有左连接的写法。
任何一个左连接都有右连接的写法。
(左右两表交换位置就可以从左连接变成右连接)
两着是可以互换的。
思考:外连接的查询结果条数一定是 >= 内连接的查询结果条数?
正确。
主要区分内外连接主要还是看有无 right 或 left 。inter 和 outer 主要是为了可读性好。
案例:查询每个员工的上级领导,要求显示所有员工的名字和领导名(包括KING)?
select
a.ename as '员工名', b.ename as '领导名'
from
emp a
left join
emp b
on
a.mgr = b.empno;
+--------+--------+
| 员工名 | 领导名 |
+--------+--------+
| SMITH | FORD |
| ALLEN | BLAKE |
| WARD | BLAKE |
| JONES | KING |
| MARTIN | BLAKE |
| BLAKE | KING |
| CLARK | KING |
| SCOTT | JONES |
| KING | NULL |
| TURNER | BLAKE |
| ADAMS | SCOTT |
| JAMES | BLAKE |
| FORD | JONES |
| MILLER | CLARK |
+--------+--------+
三张表,四张表怎么连接?
语法格式:
select
...
from
a
join // 内连接
b
on
a和b的连接条件
join // 内连接
c
on
a和c的连接条件
right join // 外连接
d
on
a和d的连接条件
一条SQL中内连接和外连接可以混合出现。
可以理解成 a 和 b 进行连接,再 a 和 c 进行连接......
案例:找出每个员工的部门名称以及工资等级,要求显示员工名、部门名、薪资、薪资等级?
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 |
+--------+---------+------------+-------+
案例:找出每个员工的部门名称以及工资等级,还有上级领导,要求显示员工名、领导名、部门名、薪资、薪资等级?
select
e.ename,e.sal,d.dname,s.grade,l.ename
from
emp e
join
dept d
on
e.deptno = d.deptno
join
salgrade s
on
e.sal between s.losal and s.hisal
left join // 如果不左连接,那就缺少“无领导”的员工的信息了(KING)
emp l
on
e.mgr = l.empno;
+--------+---------+------------+-------+-------+
| ename | sal | dname | grade | ename |
+--------+---------+------------+-------+-------+
| SMITH | 800.00 | RESEARCH | 1 | FORD |
| ALLEN | 1600.00 | SALES | 3 | BLAKE |
| WARD | 1250.00 | SALES | 2 | BLAKE |
| JONES | 2975.00 | RESEARCH | 4 | KING |
| MARTIN | 1250.00 | SALES | 2 | BLAKE |
| BLAKE | 2850.00 | SALES | 4 | KING |
| CLARK | 2450.00 | ACCOUNTING | 4 | KING |
| SCOTT | 3000.00 | RESEARCH | 4 | JONES |
| KING | 5000.00 | ACCOUNTING | 5 | NULL |
| TURNER | 1500.00 | SALES | 3 | BLAKE |
| ADAMS | 1100.00 | RESEARCH | 1 | SCOTT |
| JAMES | 950.00 | SALES | 1 | BLAKE |
| FORD | 3000.00 | RESEARCH | 4 | JONES |
| MILLER | 1300.00 | ACCOUNTING | 2 | CLARK |
+--------+---------+------------+-------+-------+
全连接:
全连接就是两张表都是主表,了解即可。
子查询?
什么是子查询?
select 语句块中嵌套select语句块,被嵌套的select语句称为子查询。
子查询都可以出现在哪里呢?
select 、from、where后面均可。
select
..(select).
from
..(select).
where
..(select).
where子句中的子查询:
案例:找出比最低工资高的员工姓名和工资?
错误示范:
select
ename,sal
from
emp
where
sal > min(sal);
ERROR 1111 (HY000): Invalid use of group function
where子句中不能直接使用分组函数。
实现思路:
第一步:查询最低工资是多少
select min(sal) from emp;
+----------+
| min(sal) |
+----------+
| 800.00 |
+----------+
第二步:找出>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 |
+--------+---------+
from子句中的子查询:
注意:from后面的子查询,可以将子查询的查询结果当做一张临时表。(技巧)
案例:找出每个岗位的平均工资的薪资等级。
第一步:找出每个岗位的平均工资(按照岗位分组求平均值)
select job,avg(sal) from emp group by job;
+-----------+-------------+
| job | avgsal |
+-----------+-------------+
| ANALYST | 3000.000000 |
| CLERK | 1037.500000 |
| MANAGER | 2758.333333 |
| PRESIDENT | 5000.000000 |
| SALESMAN | 1400.000000 |
+-----------+-------------+ #临时t表
第二步:把以上的查询结果就当做一张真实存在的表 t 。
mysql> select * from salgrade;
+-------+-------+-------+
| GRADE | LOSAL | HISAL |
+-------+-------+-------+
| 1 | 700 | 1200 |
| 2 | 1201 | 1400 |
| 3 | 1401 | 2000 |
| 4 | 2001 | 3000 |
| 5 | 3001 | 9999 |
+-------+-------+-------+ # 临时表s
第三步:t表和s表进行表连接,条件:t表avg(sal) between s.losal and s.hisal;
错误示范:
select
t.*, s.grade
from
(select job,avg(sal) from emp group by job) t
join
salgrade s
on
t.avg(sal) between s.losal and s.hisal;
原因:在on字句中,t.avg(sal) 语句 MySQL 会把 avg(sal) 看作一个函数而不是一个数,又因为这处不可进行函数调用,所以错误。
记法:在哪张表中的分组函数,就是对哪张表进行操作。
改正方法:起别名!
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 |
| ANALYST | 3000.000000 | 4 |
| MANAGER | 2758.333333 | 4 |
| PRESIDENT | 5000.000000 | 5 |
+-----------+-------------+-------+
select后面出现的子查询:
案例:找出每个员工的部门名称,要求显示员工名,部门名?
原始数据:
mysql> select * from dept;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
4 rows in set (0.00 sec)
mysql> select * from emp;
+-------+--------+-----------+------+------------+---------+---------+--------+
| EMPNO | ENAME | JOB | MGR | HIREDATE | SAL | COMM | DEPTNO |
+-------+--------+-----------+------+------------+---------+---------+--------+
| 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)
查询:
select
e.ename,(select d.dname from dept d where e.deptno = d.deptno) as dname
from
emp e;
+--------+------------+
| 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)
有点内连接那味了。
使用前提:
select内连接时,每一个外部查询的结果只能对应一个内部查询的结果!如上面的 ename 只对应 e.deptno = d.deptno 的那个 dname 结果。
但下面的案例就不行:
select
e.ename,(select dname from dept) as dname
from
emp e;
ERROR 1242 (21000): Subquery returns more than 1 row
该案例中,一个 ename 对应四个 dname (一个 dept 表中含有四个 dname),不行!
union合并查询结果集:
union可以连接两次select查询的结果。
案例:查询工作岗位是MANAGER和SALESMAN的员工?
select ename,job from emp where job in('MANAGER','SALESMAN');
+--------+----------+
| ename | job |
+--------+----------+
| ALLEN | SALESMAN |
| WARD | SALESMAN |
| JONES | MANAGER |
| MARTIN | SALESMAN |
| BLAKE | MANAGER |
| CLARK | MANAGER |
| TURNER | 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 |
+--------+----------+
union的效率要高一些。因为对于表连接来说,每连接一次新表,则匹配的次数满足笛卡尔积,匹配次数会成倍的翻。但是union可以减少匹配的次数,直接把两次查询的结果拼接起来。
union使用的注意事项:
(一)、union在进行结果集合并的时候,所有查询语句中列的个数和列的顺序必须相同。(因为union会让两个查询结果上下拼接)
select ename,job from emp where job = 'MANAGER'
union
select ename from emp where job = 'SALESMAN';
# 报错,上面的列数为2,下面的列数为1
(二)、结果集合并时列和列的数据类型也要一致。(MYSQL其实是允许的,但oracle语法严格 ,不允许)(所有查询语句中对应列的数据类型必须兼容)
select ename,job from emp where job = 'MANAGER'
union
select ename,sal from emp where job = 'SALESMAN';
# 在oracle中会报错
(三)、order by语句要放在最后一个查询语句的后边。
(四)、union all可让拼接结果不去重。
(五)、使用nuion时,前一个查询语句不用以分号结束,分号写在后一个查询语句后面,只需写一个。
limit部分输出:
limit作用:
将查询结果集的一部分取出来。通常使用在分页查询当中。
limit怎么用呢?
语法格式:
limit startIndex, length
startIndex是起始下标,length是长度。
简写形式:
limit length
这是默认起始下标从0开始。
示例:按照薪资降序,取出排名在前5名的员工?
select
ename,sal
from
emp
order by
sal desc
limit 5; # 取前5
select
ename,sal
from
emp
order by
sal desc
limit 0,5; # 法二
+-------+---------+
| ename | sal |
+-------+---------+
| KING | 5000.00 |
| SCOTT | 3000.00 |
| FORD | 3000.00 |
| JONES | 2975.00 |
| BLAKE | 2850.00 |
+-------+---------+
注意:mysql当中limit语句在order by之后,且在order by之后执行!!!!!!
分页:
每页显示3条记录
第1页:limit 0,3 [0 1 2]
第2页:limit 3,3 [3 4 5]
第3页:limit 6,3 [6 7 8]
第4页:limit 9,3 [9 10 11]
每页显示pageSize条记录,第pageNo页:
limit (pageNo - 1) * pageSize , pageSize
配合Java实现分页:
public static void main(String[] args){
// 用户提交过来一个页码,以及每页显示的记录条数
int pageNo = 5; //第5页
int pageSize = 10; //每页显示10条
int startIndex = (pageNo - 1) * pageSize;
String sql = "select ...limit " + startIndex + ", " + pageSize;
}
关于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
表的创建(DDL语句):
建表属于DDL语句,DDL包括:create drop alter。
建表的语法格式:
格式一:
create table 表名(字段名1 数据类型, 字段名2 数据类型, 字段名3 数据类型);
格式二:
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型
);
表名:建议以t_ 或者 tbl_开始,可读性强。见名知意。
字段名:见名知意。
表名和字段名都属于标识符。
关于mysql中的数据类型?
MySQL有很多数据类型,我们只需要掌握一些常见的数据类型即可。
常用的数据类型:
varchar():
可变长度的字符串。比较智能,节省空间。会根据实际的数据长度动态分配空间。
括号内最大为255。
优点:节省空间。
缺点:需要动态分配空间,速度慢。
char():
固定长度的字符串,不管实际的数据长度是多少,都分配固定长度的空间去存储数据。使用不恰当的时候,可能会导致空间的浪费。
括号内最大为255。
优点:不需要动态分配空间,速度快。
缺点:使用不当可能会导致空间的浪费。
varchar和char我们应该怎么选择?
固定长度的字符串选择char,不同对象长度不同的字符串选择varchar。
int(最长11):
数字中的整数型。等同于java的int。
bigint:
数字中的长整型。等同于java中的long。
float:
单精度浮点型数据。
double:
双精度浮点型数据。
date:
短日期类型。
datetime:
长日期类型。
clob:
字符大对象,最多可以存储4G的字符串。比如:存储一篇文章,存储一个说明。超过255个字符的都要采用CLOB字符大对象来存储。
blob:
二进制大对象,专门用来存储图片、声音、视频等流媒体数据。往BLOB类型的字段上插入数据的时候,例如插入一个图片、视频等,需要使用IO流。
default指定默认值:
在建表时,在数据类型后加default可指定该字段的默认值。
create table t_student(
no int,
name varchar(32),
sex char(1) default 'm',
age int(3),
email varchar(255)
);
desc t_student;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| no | int(11) | YES | | NULL | |
| name | varchar(32) | YES | | NULL | |
| sex | char(1) | YES | | m | | # 默认值为m
| age | int(3) | YES | | NULL | |
| email | varchar(255) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
insert into t_student(no) values(1);
mysql> select * from t_student;
+------+------+------+------+-------+
| no | name | sex | age | email |
+------+------+------+------+-------+
| 1 | NULL | m | NULL | NULL | # 新增数据不指定sex默认值为m
+------+------+------+------+-------+
插入数据insert (DML语句):
语法格式:
insert into 表名(字段名1,字段名2,字段名3...) values(值1,值2,值3);
注意:字段名和值要一一对应。数量要对应,数据类型要对应。
insert into t_student(no,name,sex,age,email) values(1,'zhangsan','m',20,'zhangsan@123.com');
insert into t_student(email,name,sex,age,no) values('lisi@123.com','lisi','f',20,2);
# 以上代码均可。顺序可调换,但只要一一对应即可。
insert into t_student(no) values(3);
+------+----------+------+------+------------------+
| no | name | sex | age | email |
+------+----------+------+------+------------------+
| 1 | zhangsan | m | 20 | zhangsan@123.com |
| 2 | lisi | f | 20 | lisi@123.com |
| 3 | NULL | NULL | NULL | NULL |
+------+----------+------+------+------------------+
insert into t_student(name) values('wangwu');
+------+----------+------+------+------------------+
| no | name | sex | age | email |
+------+----------+------+------+------------------+
| 1 | zhangsan | m | 20 | zhangsan@123.com |
| 2 | lisi | f | 20 | lisi@123.com |
| 3 | NULL | NULL | NULL | NULL |
| NULL | wangwu | NULL | NULL | NULL |
+------+----------+------+------+------------------+
注意:insert语句但凡是执行成功了,那么必然会多一条记录。没有给其它字段指定值的话,默认值是NULL。后续想给上一次未指定的值赋值时不能使用insert语句(会新增一行而不是改变上一行),应该用修改语句。
insert语句中的“字段名”可以省略吗?
可以!
错误方式:
insert into t_student values(2);
注意:前面的字段名省略的话,等于都写上了!所以值也要都写上!
正确方法:
insert into t_student values(2, 'lisi', 'f', 20, 'lisi@123.com');
+------+------+------+------+--------------+
| no | name | sex | age | email |
+------+------+------+------+--------------+
| 1 | NULL | m | NULL | NULL |
| 2 | lisi | f | 20 | lisi@123.com |
+------+------+------+------+--------------+
insert插入日期:
数字格式化显示:
语法格式:
format(数字, '格式')
'格式' 规定以 $ 符合开头,并且用数字 9 组成的格式化数字来表示想要变成的格式。
select ename,format(sal, '$999,999') as sal from emp;
+--------+-------+
| ename | sal |
+--------+-------+
| SMITH | 800 |
| ALLEN | 1,600 |
| WARD | 1,250 |
| JONES | 2,975 |
| MARTIN | 1,250 |
| BLAKE | 2,850 |
| CLARK | 2,450 |
| SCOTT | 3,000 |
| KING | 5,000 |
| TURNER | 1,500 |
| ADAMS | 1,100 |
| JAMES | 950 |
| FORD | 3,000 |
| MILLER | 1,300 |
+--------+-------+
str_to_date数据处理函数:
将字符串varchar类型转换成date类型。
str_to_date的语法格式:
str_to_date('字符串日期', '日期格式')
mysql的日期格式:
%Y 年
%m 月
%d 日
%h 时
%i 分
%s 秒
现有表:
mysql> desc t_user;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(32) | YES | | NULL | |
| birth | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
插入数据
insert into t_user(id,name,birth) values(1, 'zhangsan', '01-10-1990'); # 1990年10月1日
报错:原因是类型不匹配。数据库birth是date类型,这里给了一个字符串varchar。
怎么办?可以使用str_to_date函数进行类型转换。
解决方式:
insert into t_user(id,name,birth) values(1, 'zhangsan', str_to_date('01-10-1990','%d-%m-%Y'));
str_to_date函数可以把字符串varchar转换成日期date类型数据,通常使用在插入insert方面,因为插入的时候需要一个日期类型的数据,需要通过该函数将字符串转换成date。
但其实,如果你提供的日期字符串是 %Y-%m-%d 这个格式,str_to_date函数就不需要了!!!
insert into t_user(id,name,birth) values(2, 'lisi', '1990-10-01');
date_format数据处理函数:
这个函数可以将日期类型转换成特定格式的字符串。
语法格式:
date_format(日期类型数据, '日期格式')
mysql> select id,name,birth from t_user;
+------+----------+------------+
| id | name | birth |
+------+----------+------------+
| 1 | zhangsan | 1990-10-01 |
| 2 | lisi | 1990-10-01 |
+------+----------+------------+
以上的SQL语句实际上是进行了默认的日期格式化,自动将数据库中的date类型转换成varchar类型,并且采用的格式是mysql默认的日期格式:'%Y-%m-%d'。
设置输出格式:
select id,name,date_format(birth, '%m/%d/%Y') as birth from t_user;
+------+----------+------------+
| id | name | birth |
+------+----------+------------+
| 1 | zhangsan | 10/01/1990 |
| 2 | lisi | 10/01/1990 |
+------+----------+------------+
这个函数通常使用在查询日期方面。设置展示的日期格式。
注意,MySQL的日期格式与Java中的日期格式有所不同:
Java中的日期格式:yyyy-MM-dd HH:mm:ss SSS
date和datetime两个类型的区别?
date是短日期:只包括年月日信息。
datetime是长日期:包括年月日时分秒信息。
mysql短日期默认格式:%Y-%m-%d
mysql长日期默认格式:%Y-%m-%d %h:%i:%s
录入符合日期默认格式的 varchar 字符串可以不使用 str_to_date 函数。
insert into t_user(id,name,birth,create_time) values(1,'zhangsan','1990-10-01','2020-03-18 15:49:50');
在mysql当中获取系统当前时间(now() 函数):
使用now() 函数可获取当前带有日月年时分秒信息的datetime类型的日期数据,如果赋值给date类型则省略时分秒信息。
insert into t_user(id,name,birth,create_time) values(2,'lisi','1991-10-01',now());
修改数据update(DML语句):
语法格式:
update 表名 set 字段名1=值1,字段名2=值2,字段名3=值3... where 条件;
注意:没有条件限制会导致所有数据全部更新。
可以一次只改部分数据。
修改id为2的数据:
update t_user set name = 'jack', birth = '2000-10-11' where id = 2;
+------+----------+------------+---------------------+
| id | name | birth | create_time |
+------+----------+------------+---------------------+
| 1 | zhangsan | 1990-10-01 | 2020-03-18 15:49:50 |
| 2 | jack | 2000-10-11 | 2020-03-18 15:51:23 |
+------+----------+------------+---------------------+
更新所有数据:
update t_user set name = 'abc';
删除数据 delete(DML语句):
语法格式:
delete from 表名 where 条件;
注意:没有条件,整张表的数据会全部删除!
删除id为2的数据:
delete from t_user where id = 2;
删除所有数据:
delete from t_user;
insert语句一次插入多条数据:
语法格式:
insert into t_user(字段名1,字段名2) values(),(),(),();
示例:
insert into t_user(id,name,birth,create_time) values
(1,'zs','1980-10-11',now()),
(2,'lisi','1981-10-11',now()),
(3,'wangwu','1982-10-11',now());
mysql> select * from t_user;
+------+--------+------------+---------------------+
| id | name | birth | create_time |
+------+--------+------------+---------------------+
| 1 | zs | 1980-10-11 | 2020-03-19 09:37:01 |
| 2 | lisi | 1981-10-11 | 2020-03-19 09:37:01 |
| 3 | wangwu | 1982-10-11 | 2020-03-19 09:37:01 |
+------+--------+------------+---------------------+
快速创建表(将一个查询结果当做一张表新建):
mysql> create table emp2 as select * from emp;
原理:
将一个查询结果当做一张表新建,可以完成表的快速复制!!!!
可只复制原表的部分内容:
create table mytable as select empno,ename from emp where job = 'MANAGER';
快速插入表(将一个查询结果当做一块数据插入):
原理:
将一个查询结果当做一张表新建,可以完成表的快速复制!!!!
前提:插入内容要与被插入的表的结构要一致。
create table dept_bak as select * from dept;
mysql> select * from dept_bak;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
insert into dept_bak select * from dept; //很少用!
mysql> select * from dept_bak;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
快速删除表中的数据(DDL操作)?
delete语句删除数据的原理?(delete属于DML语句!!!)
表中的数据被删除了,但是这个数据在硬盘上的真实存储空间不会被释放!!!
这种删除缺点是:删除效率比较低。
这种删除优点是:支持回滚,后悔了可以再恢复数据!!!
truncate语句删除数据的原理?
这种删除效率比较高,表被一次截断,物理删除。
这种删除缺点:不支持回滚。
这种删除优点:快速。
delete和truncate都是删除表中的数据,表还在!
语法格式:
truncate table 表名;
当我们有一张大表,里面存有上亿条记录:
删除的时候,使用delete,也许需要执行1个小时才能删除完!效率较低。
可以选择使用truncate删除表中的数据。只需要不到1秒钟的时间就删除结束。效率较高。
但是使用truncate之前,必须仔细询问客户是否真的要删除,并警告删除之后不可恢复!
删除表操作?
语法格式:
drop table 表名;
这不是删除表中的数据,这是把表删除。
对表结构的修改(alter):
1、增加列:
alter table 表名 add 列名 类型(长度) 约束 after 在哪个字段后添加;
即使没有约束也可以加 after 关键字。
2、修改现有列类型、长度和约束:
alter table 表名 modify 列名 类型(长度) 约束 after 修改后在哪一列后面;
3、修改现有列名称:
alter table 表名 change 旧列名 新列名 类型(长度) 约束 fter 修改后在哪一列后面;
注意:modify只能修改属性 不能修改列名。
MySQL语法松散,modify和change都可以修改列的属性
4、删除现有列:
alter table 表名 drop 列名;
5、调整字段顺序:
使用 after 关键字。
6、重命名表:
alter table 旧表名 rename 新表名;
7、修改约束:
mysql 对有些约束的修改时不支持的,所以我们可以先删除,再添加
约束:
什么是约束?
在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性、有效性。
约束包括哪些?
非空约束:not null
唯一性约束: unique
主键约束: primary key (简称PK)
外键约束:foreign key(简称FK)
检查约束:check(mysql不支持,oracle支持)
非空约束(not null):
非空约束not null约束的字段不能为NULL。
create table t_vip(
id int,
name varchar(255) not null
);
insert into t_vip(id) values(3);
ERROR 1364 (HY000): Field 'name' doesn't have a default value
not null只有列级约束,没有表级约束!(不能两个字段联合起来同时非空)
唯一性约束(unique):
唯一性约束unique表示该字段不能重复,但是可以为NULL(多个为NULL也可)。
create table t_vip(
id int,
name varchar(255) unique,
email varchar(255)
);
insert into t_vip(id,name,email) values(3,'wangwu','wangwu@123.com');
insert into t_vip(id,name,email) values(4,'wangwu','wangwu@sina.com');
ERROR 1062 (23000): Duplicate entry 'wangwu' for key 'name'
新需求:name和email两个字段联合起来具有唯一性,怎么弄?
create table t_vip(
id int,
name varchar(255) unique, # 约束直接添加到列后面的,叫做列级约束。
email varchar(255) unique
);
这张表这样创建是不符合我以上“新需求”的。
这样创建表示:name具有唯一性,email具有唯一性。各自唯一。
怎么创建这样的表,才能符合新需求呢?
create table t_vip(
id int,
name varchar(255),
email varchar(255),
unique(name,email) # 约束没有添加在列的后面,这种约束被称为表级约束。
);
name和email两个字段联合起来唯一!!!
insert into t_vip(id,name,email) values(3,'zhangsan','zhangsan@sina.com');
ERROR 1062 (23000): Duplicate entry 'zhangsan-zhangsan@sina.com' for key 'name'
什么时候使用表级约束呢?
需要给多个字段联合起来添加某一个约束的时候,需要使用表级约束。
两两约束可以联合使用吗?
可以的。
create table t_vip(
id int,
name varchar(255) not null unique
);
mysql> desc t_vip;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(255) | NO | PRI | NULL | |
+-------+--------------+------+-----+---------+-------+
insert into t_vip(id,name) values(1,'zhangsan');
insert into t_vip(id,name) values(2,'zhangsan'); # 错误:name不能重复
insert into t_vip(id) values(2); # 错误:name不能为NULL。
另外,在mysql当中,如果一个字段同时被not null和unique约束的话,该字段自动变成主键字段。(注意:oracle中不一样!)
主键约束(primary key,简称PK):
主键约束的相关术语?
主键约束:就是一种约束。
主键字段:该字段上添加了主键约束,这样的字段叫做:主键字段
主键值:主键字段中的每一个值都叫做:主键值。
什么是主键?有啥用?
主键是每一行记录的唯一标识,是每一行记录的身份证号,任何一张表都应该有主键,没有主键,表无效。
主键的特征:
not null + unique(主键值不能是NULL,同时也不能重复!)
怎么给一张表添加主键约束:
使用关键字 primary key。
使用列级约束添加主键:
create table t_vip(
id int primary key, //列级约束
name varchar(255)
);
insert into t_vip(id,name) values(1,'zhangsan');
insert into t_vip(id,name) values(2,'lisi');
//错误:不能重复
insert into t_vip(id,name) values(2,'wangwu');
ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'
//错误:不能为NULL
insert into t_vip(name) values('zhaoliu');
ERROR 1364 (HY000): Field 'id' doesn't have a default value
一个字段做主键,叫做:单一主键。
多个字段做主键,叫做:复合主键。
也可以使用表级约束添加主键:
create table t_vip(
id int,
name varchar(255),
email varchar(255),
primary key(id,name)
);
insert into t_vip(id,name,email) values(1,'zhangsan','zhangsan@123.com');
insert into t_vip(id,name,email) values(1,'lisi','lisi@123.com');
# 错误:不能重复
insert into t_vip(id,name,email) values(1,'lisi','lisi@123.com');
ERROR 1062 (23000): Duplicate entry '1-lisi' for key 'PRIMARY'
表级约束也可以仅约束一个字段。
两者本质上没什么区别,而如果你的约束需要同时对多列进行约束那么就只能采用表级约束,因为表级约束面向的是表(当然就包括所有列),而列级约束只能针对该列进行约束。
在实际开发中,复合主键比较复杂,不建议使用,建议使用单一主键!
一个表中主键约束能加两个吗?
drop table if exists t_vip;
create table t_vip(
id int primary key,
name varchar(255) primary key
);
ERROR 1068 (42000): Multiple primary key defined
一张表,主键约束只能添加1个。(复合主键也只是一个主键)
主键值的类型选择:
主键值建议使用:int、bigint、char等类型。
不建议使用varchar来做主键。主键值一般都是数字,且一般都是定长的!
主键除了:单一主键和复合主键之外,还可以这样进行分类:
自然主键:主键值是一个自然数,和业务没关系。
业务主键:主键值和业务紧密关联,例如拿银行卡账号做主键值。这就是业务主键!
在实际开发中使用业务主键多,还是使用自然主键多一些?
自然主键使用比较多,因为主键只要做到不重复就行,不需要有意义。业务主键不好,因为主键一旦和业务挂钩,那么当业务发生变动的时候,可能会影响到主键值,所以业务主键不建议使用。尽量使用自然主键。
auto_increment关键字:
drop table if exists t_vip;
create table t_vip(
id int primary key auto_increment, # auto_increment表示自增,从1开始,以1递增!
name varchar(255)
);
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
insert into t_vip(name) values('zhangsan');
select * from t_vip;
+----+----------+
| id | name |
+----+----------+
| 1 | zhangsan |
| 2 | zhangsan |
| 3 | zhangsan |
+----+----------+
auto_increment的初始值(offset)是1, 步长(increment)是1(步长指每次的递增量)。即每次递增1。
如何修改auto_increment的初始值和步长:
set @@auto_increment_increment=2; # 设置步长为2
set @@auto_increment_offset=2; # 设置初始值为3
外键约束(foreign key,简称FK):
外键约束涉及到的相关术语:
外键约束:一种约束(foreign key)
外键字段:该字段上添加了外键约束
外键值:外键字段当中的每一个值。
什么是外键约束:
参照完整性要求关系中不允许引用不存在的实体。在 MySQL 中设置参照完整性的方式是使用 外键约束 。所谓外键就是用于在两个表中的数据之间建立和加强链接的一列或多列的组合,可控制可在外键表中存储的数据。例如,有学生表和分数表两个表,表内容如下。
student 学生表:
stu_no | name | phone |
2021001 | 张三 | 123456789 |
2021002 | 李四 | 987654321 |
score 分数表:
score_no | stu_no | gross_score |
1 | 2021001 | 750 |
2 | 2021002 | 685 |
在分数表中有 stu_no 列存储的是学生表中的学生编号,当我们把这个字段设置为分数表的外键字段后,插入一条不存在的学生的分数将会报错。例如插入一条stu_no 编号为 2021003 的学生的分数,这时 MySQL 将会报错。这是因为在学生表中不存在编号为 2021003 的学生,如果想插入成功则必须现在学生表中创建编号为 2021003 这个学生后再向分数表插入这个学生的分数。
在 MySQL 中只有 InnoDB 存储引擎支持外键约束。外键约束的语法如下:
[CONSTRAINT [symbol]] FOREIGRN KEY [index_name] (col_name,...) REFERENCES tbl_name(col_name,...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
中括号内为可省略的。
在语法中 reference_option 指的是外键约束条件,外键约束条件包括如下五种选项:
RESTRICT:拒绝对父表的删除或更新操作;
CASCADE:从父表删除或更新时自动删除或更新子表中匹配的行;
SET NULL:从父表删除或更新时自动设置子表对应的外键列为NULL;
NO ACTION: 不采取任何动作;
SET DEFAULT:使用默认约束;
前述的学生表的分数表可以这样创建:
# 创建学生表
create table student(
stu_no varchar(30) not null primary key,
name varchar(30),
phone varchar(30)
) engine=InnoDB
# 创建分数表,设置 stu_no 为外键
create table score(
score_no int not null auto_increment primary key,
stu_no varchar(30),
gross_score int,
foreign key(stu_no) references student(stu_no)
# foreign key(stu_no) 表示为本表的stu_no添加外键约束。
# references student(stu_no) 表示外键约束指向student表的stu_no字段。
) engine=InnoDB
注意:
student 是父表
score 是子表
删除表的顺序?
先删子,再删父。
创建表的顺序?
先创建父,再创建子。
删除数据的顺序?
先删子,再删父。
插入数据的顺序?
先插入父,再插入子。
思考:子表中的外键引用的父表中的某个字段,被引用的这个字段必须是主键吗?
不一定是主键,但至少具有unique约束。
测试:外键可以为NULL吗?
外键值可以为NULL。
存储引擎:
什么是存储引擎,有什么用呢?
存储引擎是MySQL中特有的一个术语,其它数据库中没有。(Oracle中有,但是不叫这个名字)实际上存储引擎是一个表存储/组织数据的方式。不同的存储引擎,表存储数据的方式不同。
查看当初建这张表时指定了什么存储引擎:
show create table t_student;
CREATE TABLE `t_student` (
`no` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`cno` int(11) DEFAULT NULL,
PRIMARY KEY (`no`),
KEY `cno` (`cno`),
CONSTRAINT `t_student_ibfk_1` FOREIGN KEY (`cno`) REFERENCES `t_class` (`classno`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8
怎么给表添加/指定“存储引擎”:
可以在建表的时候给表指定存储引擎。
在建表的时候可以在最后小括号的")"的右边使用:
ENGINE来指定存储引擎。
CHARSET来指定这张表的字符编码方式。( DEFAULT CHARSET表示为默认字符集)
create table t_product(
id int primary key,
name varchar(255)
)engine=InnoDB default charset=gbk;
结论:
mysql默认的存储引擎是:InnoDB
mysql默认的字符编码方式是:utf8
怎么查看mysql版本支持哪些存储引擎呢?
查看当前MySQL版本:
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.5.36 |
+-----------+
查看该版本下MySQL支持哪些存储引擎:
show engines \G
*************************** 1. row ***************************
Engine: FEDERATED
Support: NO
Comment: Federated MySQL storage engine
Transactions: NULL
XA: NULL
Savepoints: NULL
*************************** 2. row ***************************
Engine: MRG_MYISAM
Support: YES
Comment: Collection of identical MyISAM tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 3. row ***************************
Engine: MyISAM
Support: YES
Comment: MyISAM storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 4. row ***************************
Engine: BLACKHOLE
Support: YES
Comment: /dev/null storage engine (anything you write to it disappears
Transactions: NO
XA: NO
Savepoints: NO
*************************** 5. row ***************************
Engine: CSV
Support: YES
Comment: CSV storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 6. row ***************************
Engine: MEMORY
Support: YES
Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 7. row ***************************
Engine: ARCHIVE
Support: YES
Comment: Archive storage engine
Transactions: NO
XA: NO
Savepoints: NO
*************************** 8. row ***************************
Engine: InnoDB
Support: DEFAULT
Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
XA: YES
Savepoints: YES
*************************** 9. row ***************************
Engine: PERFORMANCE_SCHEMA
Support: YES
Comment: Performance Schema
Transactions: NO
XA: NO
Savepoints: NO
mysql支持九大存储引擎,当前5.5.36支持8个。版本不同支持情况不同。
mysql常用的存储引擎的区别:
MyISAM存储引擎:
它管理的表具有以下特征:使用三个文件表示每个表:
格式文件 — 存储表结构的定义(mytable.frm)
数据文件 — 存储表行的内容(mytable.MYD)
索引文件 — 存储表上索引(mytable.MYI):索引是一本书的目录,缩小扫描范围,提高查询效率的一种机制。
可被转换为压缩、只读表来节省空间
提醒:
对于一张表来说,只要是主键,或者加有unique约束的字段上会自动创建索引。
MyISAM存储引擎的优点:
可被转换为压缩、只读表来节省空间。
MyISAM存储引擎的缺点:
MyISAM不支持事务机制,安全性低。
InnoDB存储引擎:
这是mysql默认的存储引擎,同时也是一个重量级的存储引擎。InnoDB支持事务,支持数据库崩溃后自动恢复机制。
它管理的表具有下列主要特征:
– 每个 InnoDB 表在数据库目录中以.frm 格式文件表示
– InnoDB 表空间 tablespace 被用于存储表的内容(表空间是一个逻辑名称。表空间存储数据+索引。)
– 提供一组用来记录事务性活动的日志文件
– 用 COMMIT(提交)、SAVEPOINT 及ROLLBACK(回滚)支持事务处理
– 提供全 ACID 兼容
– 在 MySQL 服务器崩溃后提供自动恢复
– 多版本(MVCC)和行级锁定
– 支持外键及引用的完整性,包括级联删除和更新
InnoDB存储引擎的优点:
支持事务,以保证数据的安全。
InnoDB存储引擎的缺点:
效率不是很高,并且也不能压缩,不能转换为只读,不能很好的节省存储空间。
MEMORY存储引擎:
使用 MEMORY 存储引擎的表,其数据不存储在硬盘中,而是存储在内存中,且行的长度固定,这两个特点使得 MEMORY 存储引擎非常快。
MEMORY 存储引擎管理的表具有下列特征:
– 在数据库目录内,每个表均以.frm 格式的文件表示。
– 表数据及索引被存储在内存中。(目的就是快,查询快!)
– 表级锁机制。
– 不能包含 TEXT 或 BLOB 字段。
MEMORY 存储引擎以前被称为HEAP 引擎。
MEMORY引擎的优点:
查询效率是最高的。不需要和硬盘交互。
MEMORY引擎的缺点:
不安全,关机之后数据消失。因为数据和索引都是在内存当中。
事务:
什么是事务?
一个事务其实就是一个完整的业务逻辑,是一个最小的工作单元。不可再分。
什么是一个完整的业务逻辑?
假设转账,从A账户向B账户中转账10000.
将A账户的钱减去10000(update语句)
将B账户的钱加上10000(update语句)
这就是一个完整的业务逻辑。
以上的操作是一个最小的工作单元,要么同时成功,要么同时失败,不可再分。这两个update语句要求必须同时成功或者同时失败,这样才能保证钱是正确的。
只有DML语句才会有事务这一说,其它语句和事务无关!!!
insert
delete
update
只有以上的三个语句和事务有关系,其它都没有关系。因为只有以上的三个语句是数据库表中数据进行增、删、改的。只要你的操作一旦涉及到数据的增、删、改,那么就一定要考虑安全问题。数据安全第一位!!!
假设所有的业务,只要一条DML语句就能完成,还有必要存在事务机制吗?
正是因为做某件事的时候,需要多条DML语句共同联合起来才能完成,所以需要事务的存在。如果任何一件复杂的事儿都能一条DML语句搞定,那么事务则没有存在的价值了。
到底什么是事务呢?
说到底,说到本质上,一个事务其实就是多条DML语句同时成功,或者同时失败!
事务:就是批量的DML语句同时成功,或者同时失败!
事务是怎么做到多条DML语句同时成功和同时失败的呢?
InnoDB存储引擎:提供一组用来记录事务性活动的日志文件
事务开启了:
insert
insert
insert
delete
update
update
update
事务结束了!
在事务的执行过程中,每一条DML的操作都会记录到“事务性活动的日志文件”中。在事务的执行过程中,我们可以提交事务,也可以回滚事务。
提交事务:
清空事务性活动的日志文件,将数据全部彻底持久化到数据库表中。
提交事务标志着,事务的结束。并且是一种全部成功的结束。
回滚事务:
将之前所有的DML操作全部撤销,并且清空事务性活动的日志文件
回滚事务标志着,事务的结束。并且是一种全部失败的结束。
怎么提交事务,怎么回滚事务?
提交事务:commit; 语句
回滚事务:rollback; 语句(回滚永远都是只能回滚到上一次的提交点!)
测试一下,在mysql当中默认的事务行为是怎样的?
mysql默认情况下是支持自动提交事务的,即每执行一条DML语句,则提交一次!
这种自动提交实际上是不符合我们的开发习惯,因为一个业务通常是需要多条DML语句共同执行才能完成的,为了保证数据的安全,必须要求同时成功之后再提交,所以不能执行一条就提交一条。
怎么将mysql的自动提交机制关闭掉呢?
执行命令start transaction;表示开启事务,MySQL自动关闭自动提交事务机制。等待提交事务语句commit或回滚事务语句rollback再对该事务进行进一步的操作。
回滚事务演示:
mysql> use bjpowernode;
Database changed
mysql> select * from dept_bak;
Empty set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into dept_bak values(10,'abc', 'tj');
Query OK, 1 row affected (0.00 sec)
mysql> insert into dept_bak values(10,'abc', 'tj');
Query OK, 1 row affected (0.00 sec)
mysql> select * from dept_bak;
+--------+-------+------+
| DEPTNO | DNAME | LOC |
+--------+-------+------+
| 10 | abc | tj |
| 10 | abc | tj |
+--------+-------+------+
2 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from dept_bak;
Empty set (0.00 sec)
提交事务演示:
mysql> use bjpowernode;
Database changed
mysql> select * from dept_bak;
+--------+-------+------+
| DEPTNO | DNAME | LOC |
+--------+-------+------+
| 10 | abc | bj |
+--------+-------+------+
1 row in set (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into dept_bak values(20,'abc
Query OK, 1 row affected (0.00 sec)
mysql> insert into dept_bak values(20,'abc
Query OK, 1 row affected (0.00 sec)
mysql> insert into dept_bak values(20,'abc
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from dept_bak;
+--------+-------+------+
| DEPTNO | DNAME | LOC |
+--------+-------+------+
| 10 | abc | bj |
| 20 | abc | tj |
| 20 | abc | tj |
| 20 | abc | tj |
+--------+-------+------+
4 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from dept_bak;
+--------+-------+------+
| DEPTNO | DNAME | LOC |
+--------+-------+------+
| 10 | abc | bj |
| 20 | abc | tj |
| 20 | abc | tj |
| 20 | abc | tj |
+--------+-------+------+
4 rows in set (0.00 sec)
事务的4个特性:
A:原子性
说明事务是最小的工作单元。不可再分。
C:一致性
所有事务要求,在同一个事务当中,所有操作必须同时成功,或者同时失败,以保证数据的一致性。
I:隔离性
A事务和B事务之间具有一定的隔离,隔离也有不同的隔离等级。
D:持久性
事务最终结束的一个保障。事务提交,就相当于将没有保存到硬盘上的数据保存到硬盘上!
事务的隔离性:
事务和事务之间有4个隔离级别:
读未提交:read uncommitted(最低的隔离级别)《A事务没有提交B事务就可以读到》
即事务A可以读取到事务B未提交的数据。
这种隔离级别存在的问题:
脏读现象!(Dirty Read),我们称读到了脏数据。
这种隔离级别一般都是理论上的,大多数的数据库隔离级别都是二档起步!
读已提交:read committed《A事务提交之后B事务才能读到》
即事务A只能读取到事务B提交之后的数据。这种隔离级别解决了脏读的现象。
这种隔离级别存在问题:
不可重复读取数据。
什么是不可重复读取数据呢?
在事务开启之后,第一次读到的数据是3条,当前事务还没有结束,可能第二次再读取的时候,读到的数据是4条,前后读取的结果不一致,称为不可重复读取。
这种隔离级别是比较真实的数据,每一次读到的数据是绝对的真实。
oracle数据库默认的隔离级别是:read committed
可重复读:repeatable read《永远读取的都是刚开启事务时的数据,即便另一个事务又提交了新的数据》
即事务A开启之后,不管是多久,每一次在事务A中读取到的数据都是一致的。即使事务B将数据已经修改,并且提交了,事务A读取到的数据还是没有发生改变,这就是可重复读。
可重复读解决了什么问题?
解决了不可重复读取数据。
可重复读存在的问题是什么?
可以会出现幻读,每一次读取到的数据都是幻象,不够真实!
mysql中默认的事务隔离级别就是这个!
序列化/串行化:serializable(最高的隔离级别)
这是最高隔离级别,效率最低。但解决了所有的问题。这种隔离级别表示事务排队,不能并发!即一个事务进行时另一个事务必须排队等待该事务结束才能进行,每一次读取到的数据都是最真实的,并且效率是最低的。
验证各种隔离级别:
查看隔离级别:
SELECT @@tx_isolation
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
这是mysql默认的隔离级别。
修改隔离级别:
set global transaction isolation level 所修改为的隔离级别;
被测试的表为t_user:
验证:read uncommited
mysql> set global transaction isolation level read uncommitted; (修改事务级别为读未提交)
事务A 事务B
--------------------------------------------------------------------------------
use bjpowernode;
use bjpowernode;
start transaction;
select * from t_user;(查到0条数据)
start transaction;
insert into t_user values('zhangsan');
select * from t_user;(查到zhangsan)
rollback;
select * from t_user;(查到0条数据)
验证:read commited
mysql> set global transaction isolation level read committed; (修改事务级别读以提交)
事务A 事务B
--------------------------------------------------------------------------------
use bjpowernode;
use bjpowernode;
start transaction;
start transaction;
select * from t_user;(查到0条数据)
insert into t_user values('zhangsan');
select * from t_user;(查到0条数据)
commit;
select * from t_user;(查到1条数据)
验证:repeatable read
mysql> set global transaction isolation level repeatable read; (修改事务级别为可重复读)
事务A 事务B
--------------------------------------------------------------------------------
use bjpowernode;
use bjpowernode;
start transaction;
start transaction;
select * from t_user;(查到1条数据)
insert into t_user values('lisi');
insert into t_user values('wangwu');
commit;
select * from t_user;(还是只能查到1条数据)
验证:serializable
mysql> set global transaction isolation level serializable; (修改事务级别为序列号)
事务A 事务B
--------------------------------------------------------------------------------
use bjpowernode;
use bjpowernode;
start transaction;
start transaction;
select * from t_user;
insert into t_user values('abc');
select * from t_user;(光标暂停,等待事务A结束再进行查询)
索引(index)
什么是索引?
索引是在数据库表的字段上添加的,是为了提高查询效率存在的一种机制。
一张表的一个字段可以添加一个索引,当然,多个字段联合起来也可以添加索引。
索引相当于一本书的目录,是为了缩小扫描范围而存在的一种机制。
在mysql数据库当中索引也是需要排序的,并且这个所以的排序和TreeSet数据结构相同。TreeSet(TreeMap)底层是一个自平衡的二叉树!在mysql当中索引是一个B-Tree数据结构。遵循左小又大原则存放。采用中序遍历方式遍历取数据。
索引的实现原理:
假设有一张用户表:t_user
id(PK) name 每一行记录在硬盘上都有物理存储编号
----------------------------------------------------------------------------------
100 zhangsan 0x1111
120 lisi 0x2222
99 wangwu 0x8888
88 zhaoliu 0x9999
101 jack 0x6666
55 lucy 0x5555
130 tom 0x7777
(一):在任何数据库当中主键上都会自动添加索引对象,id字段上自动有索引,因为id是PK。另外在mysql当中,一个字段上如果有unique约束的话,也会自动创建索引对象。
(二):在任何数据库当中,任何一张表的任何一条记录在硬盘存储上都有一个硬盘的物理存储编号。
(三):在mysql当中,索引是一个单独的对象,不同的存储引擎以不同的形式存在。在MyISAM存储引擎中,索引存储在一个.MYI文件中。在InnoDB存储引擎中索引存储在一个逻辑名称叫做tablespace的当中。在MEMORY存储引擎当中索引被存储在内存当中。不管索引存储在哪里,索引在mysql当中都是一个树的形式存在。(自平衡二叉树:B-Tree)
什么条件下,我们会考虑手动给字段添加索引呢?
条件1:数据量庞大(到底有多么庞大算庞大,这个需要测试,因为每一个硬件环境不同)
条件2:该字段经常出现在where的后面,以条件的形式存在,也就是说这个字段总是被扫描。
条件3:该字段很少的DML(insert delete update)操作。(因为DML之后,索引需要重新排序。)
建议不要随意添加索引,因为索引也是需要维护的,太多的话反而会降低系统的性能。建议通过主键查询,建议通过unique约束的字段进行查询,效率是比较高的。
索引怎么创建?怎么删除?语法是什么?
创建索引:
mysql> create index emp_ename_index on emp(ename);
给emp表的ename字段添加索引,起名:emp_ename_index
复合索引:
create index emp_job_sal_index on emp(job,sal);
两个字段,或者更多的字段联合起来添加一个索引,叫做复合索引。
删除索引:
mysql> drop index emp_ename_index on emp;
将emp表上的emp_ename_index索引对象删除。
在mysql当中,怎么查看一个SQL语句是否使用了索引进行检索?
使用explain关键字:
mysql> explain select * from emp where ename = 'KING';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
扫描了14条记录:说明没有使用索引。type=ALL
mysql> create index emp_ename_index on emp(ename);
mysql> explain select * from emp where ename = 'KING';
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_ename_index | emp_ename_index | 33 | const | 1 | Using where |
+----+-------------+-------+------+-----------------+-----------------+---------+-------+------+-------------+
添加索引后,查询效率大大提升。
索引有失效的时候,什么时候索引失效呢?
失效的第1种情况(使用模糊查询且以%开头):
mysql> explain select * from emp where ename like '%T';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
ename上即使添加了索引,也不会走索引,因为模糊匹配当中以“%”开头了!
尽量避免模糊查询的时候以“%”开始。
这是一种优化的手段/策略。
失效的第2种情况(使用模糊查询且以_开头):
mysql> explain select * from emp where ename like '_T';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | emp | NULL | ALL | NULL | NULL | NULL | NULL | 14 | 11.11 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
失效的第3种情况(使用or关键字):
如果使用or,当且仅当or两边的条件字段都要有索引时,才会走索引,如果其中一边有一个字段没有索引,那么另一个字段上的索引也会失效。所以这就是为什么不建议使用or的原因。
mysql> explain select * from emp where ename = 'KING' or job = 'MANAGER';
+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | emp_ename_index | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+-----------------+------+---------+------+------+-------------+
失效的第4种情况(使用复合索引且按右侧的列查找):
使用复合索引的时候,没有使用左侧的列查找,索引失效。
create index emp_job_sal_index on emp(job,sal);
mysql> explain select * from emp where job = 'MANAGER';
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_job_sal_index | emp_job_sal_index | 30 | const | 3 | Using where |
+----+-------------+-------+------+-------------------+-------------------+---------+-------+------+-------------+
mysql> explain select * from emp where sal = 800;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
失效的第5种情况:
在where当中索引列参加了运算,索引失效。
mysql> create index emp_sal_index on emp(sal);
explain select * from emp where sal = 800;
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| 1 | SIMPLE | emp | ref | emp_sal_index | emp_sal_index | 9 | const | 1 | Using where |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
mysql> explain select * from emp where sal+1 = 800;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
失效的第6种情况:
在where当中索引列使用了函数
explain select * from emp where lower(ename) = 'smith';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | emp | ALL | NULL | NULL | NULL | NULL | 14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
失效的第7...
失效的第8...
索引的分类:
索引在数据库当中分了很多类:
单一索引:一个字段上添加索引。
复合索引:两个字段或者更多的字段上添加索引。
主键索引:主键上添加索引。
唯一性索引:具有unique约束的字段上添加索引。
.....
注意:唯一性比较弱的字段上添加索引用处不大。
注意:
1、多个索引同时存在时也没有主次关系,用哪个索引为条件来查询则使用哪个索引。
2、union不会使索引失效,可以使用拼接。
视图(view):
怎么创建视图对象?怎么删除视图对象?
创建视图对象:
create view 视图名称 as 一个DQL语句(select查询语句);
create view dept2_view as select * from dept2;
删除视图对象:
drop view dept2_view;
注意:as后只能是一个DQL语句,可以是一个联合查询的DQL语句。
视图对象也是一个文件,在数据库中也是以文件的形式存储。
视图的特性:
我们可以面向视图对象进行增删改查,对视图对象的增删改查,会导致原表被操作!(视图的特点:通过对视图的操作,会影响到原表数据。)
// 面向视图查询
select * from dept2_view;
// 面向视图插入
insert into dept2_view(deptno,dname,loc) values(60,'SALES', 'BEIJING');
// 查询原表数据
mysql> select * from dept2;
+--------+------------+----------+
| DEPTNO | DNAME | LOC |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
| 60 | SALES | BEIJING |
+--------+------------+----------+
// 面向视图删除
mysql> delete from dept2_view;
// 查询原表数据
mysql> select * from dept2;
Empty set (0.00 sec)
// 创建视图对象
create view
emp_dept_view
as
select
e.ename,e.sal,d.dname
from
emp e
join
dept d
on
e.deptno = d.deptno;
// 查询视图对象
mysql> select * from emp_dept_view;
+--------+---------+------------+
| ename | sal | dname |
+--------+---------+------------+
| CLARK | 2450.00 | ACCOUNTING |
| KING | 5000.00 | ACCOUNTING |
| MILLER | 1300.00 | ACCOUNTING |
| SMITH | 800.00 | RESEARCH |
| JONES | 2975.00 | RESEARCH |
| SCOTT | 3000.00 | RESEARCH |
| ADAMS | 1100.00 | RESEARCH |
| FORD | 3000.00 | RESEARCH |
| ALLEN | 1600.00 | SALES |
| WARD | 1250.00 | SALES |
| MARTIN | 1250.00 | SALES |
| BLAKE | 2850.00 | SALES |
| TURNER | 1500.00 | SALES |
| JAMES | 950.00 | SALES |
+--------+---------+------------+
// 面向视图更新
update emp_dept_view set sal = 1000 where dname = 'ACCOUNTING';
// 原表数据被更新
mysql> select * from 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 | 1000.00 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000.00 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 1000.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 | 1000.00 | NULL | 10 |
+-------+--------+-----------+------+------------+---------+---------+--------+
视图对象的作用:
假设有一条非常复杂的SQL语句,而这条SQL语句需要在不同的位置上反复使用。每一次使用这个sql语句的时候都需要重新编写,很长,很麻烦,怎么办?
可以把这条复杂的SQL语句以视图对象的形式新建。在需要编写这条SQL语句的位置直接使用视图对象,可以大大简化开发。并且利于后期的维护,因为修改的时候也只需要修改一个位置就行,只需要修改视图对象所映射的SQL语句。
我们以后面向视图开发的时候,使用视图的时候可以像使用table一样。可以对视图进行增删改查等操作。视图不是在内存当中,视图对象也是存储在硬盘上的,不会消失。
再提醒一下:
视图对应的语句只能是DQL语句。
但是视图对象创建完成之后,可以对视图进行增删改查等操作。
注:
增删改查,又叫做:CRUD。CRUD是在公司中程序员之间沟通的术语。
C:Create(增)
R:Retrieve(查:检索)
U:Update(改)
D:Delete(删)
DBA常用命令:
新建用户:
create user 用户名 identified by '密码';
密码可以为空,如果为空则该用户可以不需要密码即可登陆服务器.
create user p361 identified by '123';
新用户登录后默认只可以看见一个库 information_schema 。
授权:
命令详解:
mysql> grant all privileges on dbname.tbname to 'username'@'login ip' identified by 'password' with grant option;
all privileges:表示将所有权限授予给用户。也可指定具体的权限,如:SELECT、CREATE、INSERT、DROP等。
on:表示这些权限对哪些数据库和表生效,格式:数据库名.表名。授权所有表可以写为*.* 。如果我要指定将权限应用到test库的user表中,可以这么写:test.user。
to:将权限授予哪个用户。格式:'用户名'@'登录IP或域名'。%表示没有限制(login也可以),在任何主机都可以登录。比如:”yangxin”@”192.168.0.%”,表示yangxin这个用户只能在192.168.0IP段登录。
identified by:指定用户的登录密码。
若password 为空,表示不需要密码即可登录。
with grant option; 表示该用户还可以授权给其他用户。
可以使用GRANT给用户添加权限,权限会自动叠加,不会覆盖之前授予的权限,比如你先给用户添加一个SELECT权限,后来又给用户添加了一个INSERT权限,那么该用户就同时拥有了SELECT和INSERT权限。
privileges包括(权限包括):
(1)、alter:修改数据库的表
(2)、create:创建新的数据库或表
(3)、delete:删除表数据
(4)、drop:删除数据库/表
(5)、index:创建/删除索引
(6)、insert:添加表数据
(7)、select:查询表数据
(8)、update:更新表数据
(9)、all:允许任何操作
(10)、usage:只允许登录
刷新权限:
mysql> flush privileges;
查看用户权限:
mysql> grant select,create,drop,update,alter on *.* to 'yangxin'@'localhost' identified by 'yangxin0917' with grant option;
回收权限:
mysql> revoke privileges on dbname.tbname from username;
mysql> flush privileges;
删除用户:
mysql> select host,user from user;
mysql> drop user 'yangxin'@'localhost';
用户重命名:
shell> rename user 'test3'@'%' to 'test1'@'%';
修改密码:
mysql> use mysql;
mysql5.7之前
mysql> update user set password=password('123456') where user='root';
mysql5.7之后
mysql> update user set authentication_string=password('123456') where user='root';
注意:以上的privileges、username、localhost等均是一个代名词,不属于关键字。
数据的导入和导出(数据的备份)(重点):
数据导出:
注意:在windows的dos命令窗口中:
mysqldump bjpowernode>D:\bjpowernode.sql -uroot -p123456
导出指定的表:
mysqldump bjpowernode emp>D:\bjpowernode.sql -uroot -p123456
数据导入:
注意:需要先登录到mysql数据库服务器上。
然后创建数据库:
create database bjpowernode;
再使用数据库:
use bjpowernode
最后导入数据初始化数据库:
source D:\bjpowernode.sql
数据库设计三范式
什么是数据库设计范式?
数据库表的设计依据、设计规范。
三范式的内容:
第一范式:要求任何一张表必须有主键,每一个字段原子性不可再分。
第二范式:建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。
第三范式:建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。
声明:三范式是面试官经常问的,所以一定要熟记在心!
设计数据库表的时候,按照以上的范式进行,可以避免表中数据的冗余,空间的浪费。
第一范式:
是最核心,最重要的范式,所有表的设计都需要满足。数据库表中不能出现重复记录,每个字段是原子性的不能再分。
不符合第一范式的示例:
原因:
第一:没有主键。
第二:联系方式可以分为邮箱地址和电话。(不是不可再分的)
解决方法如下:
第二范式:
建立在第一范式的基础之上,要求所有非主键字段必须完全依赖主键,不要产生部分依赖。
不符合第二范式的示例:
其中,学生编号和教师编号是复合主键。
以上虽然确定了主键,但出现了大量的冗余。原因在于:
学生姓名只依赖于学生编号,不依赖于教师编号,产生了部分依赖。
教师姓名只依赖于教师编号,不依赖于学生编号,产生了部分依赖。
解决方法如下:
1个学生可能有多个老师,1个老师有多个学生。这是非常典型的:多对多关系!
多对多设计思路:
多对多,三张表,关系表俩外键!
第三范式:
第三范式建立在第二范式的基础之上,要求所有非主键字典必须直接依赖主键,不要产生传递依赖。
有主键,满足第一范式。主键是单一主键,不是复合主键,没有产生部分依赖,满足第二范式。
第三范式要求:不要产生传递依赖!
班级名称依赖于班级编号,班级编号依赖于学生编号,产生了传递依赖,不符合第三范式的要求。产生了数据的冗余。
解决方法如下:
以上表的设计是描述:班级和学生的关系。很显然是一对多关系!
一对多设计思路:
一对多,两张表,多的表加外键!
一对一的设计思路:
一对一放到一张表中不就行了吗?为啥还要拆分表?
因为在实际的开发中,可能存在一张表字段太多,太庞大。这个时候要拆分表。
一对一怎么设计?
没有拆分表之前:一张表
t_user:
id login_name login_pwd real_name email address........
---------------------------------------------------------------------------------------------
1 zhangsan 123 张三 zhangsan@xxx xxxxxx
2 lisi 123 李四 lisi@xxx xxxxxx
...
这种庞大的表建议拆分为两张:
t_login 登录信息表:
id(pk) login_name login_pwd
------------------------------------------
1 zhangsan 123
2 lisi 123
t_user 用户详细信息表
id(pk) real_name email address........ login_id(fk+unique)
-----------------------------------------------------------------------------------------
100 张三 zhangsan@xxx xxxxxx 1
200 李四 lisi@xxx xxxxxx 2
其中,在t_user表中添加一个字段login_id,并为其添加外键约束和唯一性约束。有外键约束,则只能在外键id中选,不会出现外键中没有的id。有唯一性约束,不会重复。
即:一对一,外键唯一!
嘱咐:
数据库设计三范式是理论上的,实践和理论有的时候有偏差,最终的目的都是为了满足客户的需求,有的时候会拿冗余换执行速度,因为在sql当中,表和表之间连接次数越多,效率越低(笛卡尔积)。有的时候可能会存在冗余,但是为了减少表的连接次数,这样做也是合理的,并且对于开发人员来说,sql语句的编写难度也会降低。