数据库知识点总结

1.数据库基础知识点

1.1数据库概念

什么是数据库

简而言之,就是存储数据,管理数据的仓库。

常见的数据库分为:

关系型数据库 : Oracle、MySQL、SQLServer、Access
非关系型数据库 : MongoDB、Redis、Solr、ElasticSearch、Hive、HBase



1.1.2关系型和非关系型

早期发展的数据库建立在数据的紧密关系基础之上(如:父子关系、师生关系),我们称其为关系型数据库,也称为传统数据库;现今数据库建立在数据的松散关系基础之上(如:中国人和美国人、中国人和印度人、视频、音频),我们称其为非关系型数据库nosql(not only sql)。业界总在争论nosql能否干掉传统数据库,很多初学者也有这个困惑。以我来看,两者没有矛盾,它们各有特点,根据业务情况互补才是真谛。但总的来说原来关系型数据库一统天下的格局早被打破,领土不断被蚕食,规模一再的缩小,虽然无法全面被替代,但却早已风光不在,沦落到一偶之地,Oracle的衰落就是最好的证明,早期只要是全球大企业无一例外都是部署Oracle,但现在都在去Oracle化,阿里就已经全面排斥Oracle。


既然干不掉,很多传统项目的还是围绕关系型数据库的居多,所以我们先来学习关系型数据库,目前最流行的关系型数据库是MySQL。

1.1.3关系型数据库
关系型数据库有特定的组织方式,其以行和列的形式存储数据,以便于用户理解。关系型数据库这一系列的行和列被称为表,一组表组成了数据库。用户通过查询来检索数据库中的数据,而查询是一个用于限定数据库中某些区域的执行代码。关系模型可以简单理解为二维表格模型,而一个关系型数据库就是由二维表及其之间的关系组成的一个数据集合。


1.2Mysql数据库

Mysql数据库
(1)mysql服务端,它来处理具体数据维护,保存磁盘
(2)mysql客户端,CRUD新增,修改,删除,查询

1.2.2MySQL数据存放在哪里?

在MySQL的配置文件my.ini中会进行默认配置


1.2.3MySQL服务端
mysql-5.5.27-winx64.msi





Mysql数据库默认的编码是latin1等价于iso-8859-1,修改为utf-8



注意:配置完,mysql开始执行,最后一步出错有时仍可以使用,使用SQLyog工具测试,如不行,再执行安装程序,选择remove,删除,然后重新安装。同时注意必须是管理员权限。

1.2.4MySQL客户端1:DOS窗口

mysql -uroot -proot

语法:mysql.exe执行文件

代表参数
-u 用户名,紧接着写的
-p 密码,紧接着写的

1.2.5MySQL客户端2:可视化工具



1.3数据库的结构

数据库结构


1.4SQL语句

1.4.1定义
结构化查询语言(Structured Query Language)简称SQL(发音:/ˈes kjuː ˈel/ “S-Q-L”),是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统;同时也是数据库脚本文件的扩展名。

SQL 是1986年10 月由美国国家标准局(ANSI)通过的数据库语言美国标准,接着,国际标准化组织(ISO)颁布了SQL正式国际标准。

1.4.2分类
(1)DML(Data Manipulation Language)数据操纵语言
如:insert,delete,update,select(插入、删除、修改、检索)简称CRUD操新增Create、查询Retrieve、修改Update、删除Delete

(2)DDL(Data Definition Language)数据库定义语言
如:create table之类

(3)DCL(Data Control Language)数据库控制语言
如:grant、deny、revoke等,只有管理员才有相应的权限

(4)DQL(Data Query Language)数据库查询语言
如: select 语法
注意:SQL不区分大小写

1.5数据库常用操作

1.5.1建库
创建数据库,数据库名称:cgb2022

create database cgb2022 DEFAULT CHARACTER SET utf8;

1.5.2删库
删除名称是cgb2022的数据库

drop database cgb2022;

1.5.3查看所有数据库
查看所有数据库

show databases;

1.5.4使用数据库

use cgb2022;

1.6表的常用操作

表设计
门店表:** tb_door**


订单详情表:** tb_order_detail**


1.6.1创建表
创建tb_door表,有id,door_name,tel字段

create table tb_door(
id int primary key auto_increment,
door_name varchar(100),
tel varchar(50)
);

1.6.2修改表
添加列

alter table tb_door add column money NUMERIC(7,2)

1.6.3删除表
删除名称是tb_door的表

drop table tb_door;

1.6.4查看所有表
查看所有表

show tables;

1.6.5查看表结构/设计表
查看tb_door表结构

desc tb_door;

1.7表记录的常用操作

1.7.1插入记录
向tb_door表中插入2条记录

insert into tb_door values(null,'永和大王1店',666);
insert into tb_door values(null,' 永和大王2店',888);

1.7.2查询记录
查询tb_door表中的所有记录

SELECT * FROM tb_door;

1.7.3修改记录
修改tb_door表中id为1的记录

update tb_door set tel=555 where id=1;

1.7.4删除记录
删除tb_door表中id为2的数据

Delete from tb_door where id=2;

1.7.5排序
将tb_door表记录按照tel排序

Select * from tb_door order by tel desc;

1.7.6记录总数
查询tb_door表中的总记录数

Select count(*) from tb_door;

1.8数据类型

1.8.1命名规则
(1)字段名必须以字母开头,尽量不要使用拼音
(2)长度不能超过30个字符(不同数据库,不同版本会有不同)
(3)不能使用SQL的保留字,如where,order,group
(4)只能使用如下字符az、AZ、0~9、$ 等
(5)racle习惯全大写:USER_NAME,mysql习惯全小写:user_name
(6)多个单词用下划线隔开,而非java语言的驼峰规则

1.8.2字符
(1)char长度固定,不足使用空格填充,最多容纳2000个字符,char(11)存储abc,占11位。查询速度极快但浪费空间
(2)varchar变长字符串,最多容纳4000个字符,varchar(11)存储abc,只占3位。查询稍慢,但节省空间。Oracle为varchar2
(3)大文本: 大量文字(不推荐使用,尽量使用varchar替代)

以utf8编码计算的话,一个汉字在u8下占3个字节
注:不同数据库版本长度限制可能会有不同

1.8.3数字
(1)tinyint,int整数类型
(2)float,double小数类型
(3)numeric(5,2) decimal(5,2)—也可以表示小数,表示总共5位,其中可以有两位小数
(4)decimal和numeric表示精确的整数数字

1.8.4日期
(1)date 包含年月日
(2)time时分秒
(3)datetime包含年月日和时分秒
(4)timestamp时间戳,不是日期,而是从1970年1月1日到指定日期的毫秒数

1.8.5图片
blob 二进制数据,可以存放图片、声音,容量4g。早期有这样的设计。但其缺点非常明显,数据库庞大,备份缓慢,这些内容去备份多份价值不大。同时数据库迁移时过大,迁移时间过久。所以目前主流都不会直接存储这样的数据,而只存储其访问路径,文件则存放在磁盘上。

1.9准备数据

1.9.1部门表 dept


CREATE TABLE dept(
deptno int primary key auto_increment ,
dname VARCHAR(20),
loc VARCHAR(13)
INSERT INTO dept VALUES(null,'accounting','一区');
INSERT INTO dept VALUES(null,'research','二区');
INSERT INTO dept VALUES(null,'operations','二区');

1.9.2员工表 emp


Mysql:

CREATE TABLE emp(
empno int primary key auto_increment,
ename VARCHAR(10),
job VARCHAR(10),
mgr int,
hiredate DATE,
sal double,
comm NUMERIC(7,2),
deptno int
INSERT INTO emp VALUES(100,'jack','副总',NULL,'2002-05-1',90000,NULL,1);
INSERT INTO emp VALUES(200,'tony','总监',100,'2015-02-02',10000,2000,2);
INSERT INTO emp VALUES(300,'hana','经理',200,'2017-02-02',8000,1000,2);
INSERT INTO emp VALUES(400,'leo','员工',300,'2019-02-22',3000,200.12,2);
INSERT INTO emp VALUES(500,'liu','员工',300,'2019-03-19',3500,200.58,2);

1.10字段约束

1.10.1主键约束
**主键约束:**如果为一个列添加了主键约束,那么这个列就是主键,主键的特点是唯一且不能为空。通常情况下,每张表都会有主键。

添加主键约束,例如将id设置为主键:

主键自增策略 当主键为数值类型时,为了方便维护,可以设置主键自增策略(auto_increment),设置了主键自增策略后,数据库会在表中保存一个AUTO_INCREMENT变量值,初始值为1,当需要id值,不需要我们指定值,由数据库负责从AUTO_INCREMENT获取一个id值,作为主键值插入到表中。而且每次用完AUTO_INCREMENT值,都会自增1. AUTO_INCREMENT=1

create table abc(
id int primary key auto_increment
insert into abc values(null);
insert into abc values(null);
insert into abc values(null);
select * from abc;

1.10.2非空约束
非空约束:如果为一个列添加了非空约束,那么这个列的值就不能为空,但可以重复。

添加非空约束,例如为password添加非空约束:

create table user(
id int primary key auto_increment,
password varchar(50) not null
show tables;
insert into user values(null,null);//不符合非空约束
insert into user values(null,123;);//OK

1.10.3唯一约束
唯一约束:如果为一个列添加了唯一约束,那么这个列的值就必须是唯一的(即不能重复),但可以为空。

添加唯一约束,例如为username添加唯一约束及非空约束:

create table test(
id int primary key auto_increment,
username varchar(50) unique--唯一约束
show tables;
insert into test values(null,'lisi');
insert into test values(null,'lisi');--username的值要唯一,重复会报错的
select * from test;

2.数据库进阶

2.1基础函数

lower

SELECT 'ABC',LOWER('ABC') from dept; --数据转小写

upper

select upper(dname) from dept --数据转大写

length

select length(dname) from dept --数据的长度

substr

SELECT dname,SUBSTR(dname,1,3) FROM dept; --截取[1,3]

concat

select dname,concat(dname,'123') X from dept --拼接数据

replace

select dname,replace(dname,'a','666') X from dept --把a字符替换成666

ifnull

select ifnull(comm,10) comm from emp #判断,如果comm是null,用10替换

round & ceil & floor
round四舍五入,ceil向上取整,floor向下取整

–直接四舍五入取整

select comm,round(comm) from emp

–四舍五入并保留一位小数

select comm,round(comm,1) from emp

–ceil向上取整,floor向下取整

select comm,ceil(comm) ,floor(comm) from emp

uuid
SELECT UUID()

返回uuid:a08528ca-741c-11ea-a9a1-005056c00001

now

select now() -- 年与日 时分秒
select curdate() --年与日
select curtime() --时分秒

year & month & day
–hour()时 minute()分 second()秒

select now(),hour(now()),minute(now()),second(now()) from emp ;

–year()年 month()月 day()日

select now(),year(now()),month(now()),day(now()) from emp ;

转义字符
'作为sql语句符号,内容中出现单撇就会乱套,进行转义即可

select 'ab'cd' -- 单引号是一个SQL语句的特殊字符
select 'ab\'cd' --数据中有单引号时,用一个\转义变成普通字符

2.2条件查询

distinct
使用distinct关键字,去除重复的记录行

SELECT loc FROM dept;
SELECT DISTINCT loc FROM dept;

where
注意:where中不能使用列别名!!

select * from emp
select * from emp where 1=1 --类似没条件
select * from emp where 1=0 --条件不成立
select * from emp where empno=100 --唯一条件
select * from emp where ename='tony' and deptno=2 --相当于两个条件的&关系
select * from emp where ename='tony' or deptno=1 --相当于两个条件的|关系
select name, sal from emp where sal=1400 or sal=1600 or sal=1800;
select name, sal from emp where sal in(1400,1600,1800);
select name, sal from emp where sal not in(1400,1600,1800);

like
通配符%代表0到n个字符,通配符下划线_代表1个字符

select * from emp where ename like 'l%' --以l开头的
select * from emp where ename like '%a' --以a结束的
select * from emp where ename like '%a%' --中间包含a的
select * from emp where ename like 'l__' --l后面有两个字符的 _代表一个字符位置

null

select * from emp where mgr is null --过滤字段值为空的
select * from emp where mgr is not null --过滤字段值不为空的

between and

SELECT * FROM emp
select * from emp where sal<3000 and sal>10000
select * from emp where sal<=3000 and sal>=10000--等效
select * from emp where sal between 3000 and 10000--等效

limit
分数最高的记录:按分数排序后,limit n,返回前n条。Oracle做的很笨,实现繁琐,后期有介绍,而mysql做的很棒,语法简洁高效。在mysql中,通过limit进行分页查询:

select * from emp limit 2 --列出前两条
select * from emp limit 1,2 --从第二条开始,展示2条记录
select * from emp limit 0,3 --从第一条开始,展示3条记录--前三条

order by

SELECT * FROM emp order by sal #默认升序
SELECT * FROM emp order by sal desc #降序

2.3统计案例

入职统计
#2015年以前入职的老员工

SELECT * FROM emp WHERE DATE_FORMAT(hiredate,'%Y-%m-%d')<'2015-01-01';
SELECT * FROM emp WHERE YEAR(hiredate)<2015

#2019年以后签约的员工,日期进行格式转换后方便比较

SELECT * FROM emp WHERE YEAR(DATE_FORMAT(hiredate,'%Y-%m-%d'))<=2019;

#2015年到2019年入职的员工

SELECT * FROM emp
WHERE
STR_TO_DATE(hiredate,'%Y-%m-%d')>='2015-01-01'
STR_TO_DATE(hiredate,'%Y-%m-%d')<='2019-12-31'

年薪统计
公司福利不错13薪,年底双薪,统计员工的年薪=sal13+comm13

SELECT empno,ename,job,sal*13+comm*13 FROM emp;
SELECT empno,ename,job,sal*13+comm*13 **as**  **年薪** FROM emp;--用as给列起个别名
SELECT empno,ename,job,sal*13+comm*13 年薪 FROM emp; --as也可以省略
select ename, sal+comm from emp
select ename, sal , comm, sal+ifnull(comm,0) from emp--用0替换掉null

2.4聚合 aggregation

根据一列统计结果

count
select count(*) from emp --底层优化了
select count(1) from emp --效果和*一样
select count(comm) from emp --慢,只统计非NULL的

max / min

select max(sal) from emp --求字段的最大值
select max(sal) sal,max(comm) comm from emp
select min(sal) min from emp --获取最小值
select min(sal) min,max(sal) max from emp --最小值最大值
SELECT ename,MAX(sal) FROM emp group by ename --分组

sum / avg

select count(*) from emp --总记录数
select sum(sal) from emp --求和
select avg(sal) from emp --平均数

2.5分组 group

用于对查询的结果进行分组统计
group by表示分组, having 子句类似where过滤返回的结果

group by
#每个部门每个岗位的最高薪资和平均薪资,结果中的非聚合列必须出现在分组中,否则业务意义不对

SELECT deptno,MAX(sal),AVG(sal) FROM emp
GROUP BY deptno #按照deptno分组
SELECT job,MAX(sal),AVG(sal) FROM emp
GROUP BY job #按照job分组
SELECT deptno,job,MAX(sal),AVG(sal) FROM emp
GROUP BY deptno,job #deptno和job都满足的

having
#平均工资小于8000的部门

select deptno, AVG(sal) from emp
group by deptno #按部门分组
having AVG(sal)<8000 #查询条件,类似where,但是group by只能配合having
#deptno出现的次数
SELECT deptno,COUNT(deptno) FROM emp
GROUP BY deptno #按deptno分组
HAVING COUNT(deptno)>1 #次数多的

2.6扩展

char和varchar有什么区别?
char为定长字符串,char(n),n最大为255
varchar为不定长字符串,varchar(n),n最大长度为65535
char(10)和varchar(10)存储abc,那它们有什么差别呢?
char保存10个字符,abc三个,其它会用空格补齐;而varchar只用abc三个位置。

datetime和timestamp有什么区别?
数据库字段提供对日期类型的支持,是所有数据类型中最麻烦的一个,慢慢使用就会体会出来。

date 是 年与日

time是 时分秒

datetime年月日时分秒,存储和显示是一样的

timestamp时间戳,存储的不是个日期,而是从1970年1月1日到指定日期的毫秒数

中文乱码
如果在dos命令下执行insert插入中文数据,数据又乱码,那现在sqlYog客户端执行下面命令:

set names gbk;

设置客户端字符集和服务器端相同。如果不知道它到底用的什么编码?怎么办呢?很简单,两个都尝试下,哪个最后操作完成,查询数据库不乱码,就用哪个。

那为何会造成乱码呢?

Mysql数据库默认字符集是lantin1,也就是以后网页中遇到的ISO8859-1,它是英文字符集,不支持存放中文。我们创建库时,可以指定字符集:

create database yhdb charset utf8;

但这样很容易造成服务器和客户端编码集不同,如服务器端utf8,客户端ISO8859-1。mysql和客户端工具都有习惯的默认编码设置,好几个地方,要都统一才可以保证不乱码。

我们只要保证创建数据库时用utf8,使用可视化工具一般就基本正确。

注释
/* 很多注释内容 */

#行注释内容

– 行注释内容,这个使用较多

主键、外键、唯一索引的区别?
Primary Key 主键约束,自动创建唯一索引
Foreign Key 外键约束,外键字段的内容是引用另一表的字段内容,不能瞎写
Unique Index 唯一索引,唯一值但不是主键

对于约束的好处时,数据库会进行检查,违反约束会报错,操作失败。数据库提供了丰富的约束检查,还有其他约束,但现今弱化关系型数据库的前提下,基本已经很少使用,记住上面三个即可。

drop、delete和truncate之间的区别?
drop删除库或者表,数据和结构定义

delete和truncate只是删除表的数据

delete可以指定where条件,删除满足条件的记录,tuncate删除所有记录

对于自增字段的表,delete不会自增值清零,而truncate是把表记录和定义都删除了,然后重建表的定义,所以自增主键会重头开始计数

3.数据库整合

3.1事务 transaction

什么是事务

数据库事务(Database Transaction),是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。

简单的说:事务就是将一堆的SQL语句(通常是增删改操作)绑定在一起执行,要么都执行成功,要么都执行失败,即都执行成功才算成功,否则就会恢复到这堆SQL执行之前的状态。

下面以银行转账为例,A转100块到B的账户,这至少需要两条SQL语句:

给A的账户减去100元;
update 账户表 set money=money**-100** where name=‘A’;

给B的账户加上100元。
update 账户表 set money=money**+100** where name=‘B’;

如果在第一条SQL语句执行成功后,在执行第二条SQL语句之前,程序被中断了(可能是抛出了某个异常,也可能是其他什么原因),那么B的账户没有加上100元,而A却减去了100元,在现实生活中这肯定是不允许的。

如果在转账过程中加入事务,则整个转账过程中执行的所有SQL语句会在一个事务中,而事务中的所有操作,要么全都成功,要么全都失败,不可能存在成功一半的情况。

也就是说给A的账户减去100元如果成功了,那么给B的账户加上100元的操作也必须是成功的;否则,给A减去100元以及给B加上100元都是失败的。

事务4个特性ACID

一般来说,事务是必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中如果发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

隔离级别

事务隔离分为不同级别,包括

读未提交(Read uncommitted) 安全性最差,可能发生并发数据问题,性能最好
读提交(read committed) Oracle默认的隔离级别
可重复读(repeatable read)MySQL默认的隔离级别,安全性较好,性能一般
串行化(Serializable) 表级锁,读写都加锁,效率低下,安全性高,不能并发

查询mysql的隔离级别

在默认情况下,MySQL每执行一条SQL语句,都是一个单独的事务。如果需要在一个事务中包含多条SQL语句,那么需要手动开启事务和结束事务。

开启事务:start transaction;
结束事务:commit(提交事务)或rollback(回滚事务)。
在执行SQL语句之前,先执行start transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,最后要结束事务,commit表示提交,即事务中的多条SQL语句所做出的影响会持久化到数据库中。或者rollback,表示回滚,即回滚到事务的起点,之前做的所有操作都被撤消了!

SELECT @@tx_isolation;

Repeatable Read(可重读)

MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。

事务处理

在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务
事务处理可以用来维护数据的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行
事务用来管理 insert、update、delete 语句,因为这些操作才会“破坏”数据,查询select语句是不会的
MySQL默认数据库的事务是开启的,执行SQL后自动提交。
MySQL的事务也可以改成手动提交,那就有两个步骤:先开启,写完SQL后,再手动提交。

提交 commit

#多条语句时,批量执行,事务提交

#有了事务,多步操作就形成了原子性操作,高并发下也不会引起数据错乱

#mysql的事务默认就是开启的 – 多条语句一起操作时,要么一起成功要么一起失败

BEGIN; #关闭事务的自动提交,相当于start transaction
INSERT INTO user (id) VALUES(25);#成功
INSERT INTO user (id) VALUES(5);#已经存在5了,会失败
COMMIT; #手动提交事务

回滚 rollback

#多条语句,批量执行,insert插入重复的主键导致失败时,事务回滚

BEGIN;
INSERT INTO user (id) VALUES(15);
INSERT INTO user (id) VALUES(35);#存在了
ROLLBACK;#事务回滚,就不会再提交了

3.2表强化:6约束 constraints

非空约束 not null

唯一约束 unique
Name字段创建了唯一约束,插入数据时数据库会进行检查,如果插入的值相同,就会检查报错:

主键约束 primary key
主键是一条记录的唯一标识,具有唯一性,不能重复

外键约束 foreign key

默认约束 default

默认值

检查约束 check
很少使用,了解即可,录入age超过200将报错

3.3表关联 association

概念
表table代表了生活中一个主体,如部门表dept,员工表emp。表关联则代表了表之间的关系,如:部门和员工,商品和商品分类,老师和学生,教室和学生。

同时,也要知道,表并不都有关系,它们形成自己的小圈子。如商品和商品详情一圈,部门和员工一圈,出圈就可能没关系了,如商品和员工无关,商品和学生无关。

下面我们讨论表的关系分为四种:

一对一 one to one QQ和QQ邮箱,员工和员工编号
一对多 one to many 最常见,部门和员工,用户和订单
多对一 many to one 一对多反过来,员工和部门,订单和用户
多对多 many to many 老师和学生,老师和课程

创建表
表设计特点:

表都以s结束,标识复数
字段多以表的首字母作为开头,在多表联查时,方便标识出是哪个表的字段

drop table if exists courses;
drop table if exists scores;
drop table if exists students;
drop table if exists teachers;
create table courses
cno varchar(5) not null,
cname varchar(10) not null,
tno varchar(3) not null,
primary key (cno)
create table scores
sno varchar(3) not null,
cno varchar(5) not null,
degree numeric(10,1) not null,
primary key (sno, cno)
create table students
sno varchar(3) not null,
sname varchar(4) not null,
ssex varchar(2) not null,
sbirthday datetime,
class varchar(5),
primary key (sno)
create table teachers
tno varchar(3) not null,
tname varchar(4),
tsex varchar(2),
tbirthday datetime,
prof varchar(6),
depart varchar(10),
primary key (tno)
);

插入测试数据

3.4多表联查 join

笛卡尔积 Cartesian product
多表查询是指基于两个和两个以上的表的查询。在实际应用中,查询单个表可能不能满足你的需求,如显示员工表emp中不只显示deptno,还要显示部门名称,而部门名称dname在dept表中。

#把两个表的数据都拼接起来

SELECT * FROM dept,emp

上面这种查询两个表的方式称为:笛卡尔积(Cartesian product),又称直积。一般笛卡尔积没有实际的业务意义,但多表查询都是先生成笛卡尔积,再进行数据的筛选过滤。

这点很值得注意,实际开发中尽量少用多表联查,其根本原因就在这里,查询过程中,现在内存中构建一个大大的结果集,然后再进行数据的过滤。那这个构建过程,和所使用的内存资源,包括过滤时的判断,都是既耗费资源,又浪费时间。

这就是阿里规范中禁止3张表以上的联查的原因:

三种连接 join
内连接 inner join
左(外)连接 left join
右(外)连接 right join

案例:列出research部门下的所有员工的信息

SELECT * FROM emp
WHERE deptno = ( SELECT deptno FROM dept WHERE dname='research' )

inner join、left join、right join的区别?


INNER JOIN两边都对应有记录的才展示,其他去掉
LEFT JOIN左边表中的数据都出现,右边没有数据以NULL填充
RIGHT JOIN右边表中的数据都出现,左边没有数据以NULL填充
子查询 subquery
概念
子查询是指嵌入在其他select语句中的select语句,也叫嵌套查询。子查询执行效率低慎用。记录少时效率影响不大、图方便直接使用,记录多时最好使用其它方式替代。

单行子查询 =
返回结果为一个

--列出tony所在部门的所有人员
select deptno from emp where ename='tony';
select * from emp where deptno = (select deptno from emp where ename='tony');

多行子查询 in
in子查询

3.6索引 index

定义
索引是一种排好序的快速查找的数据结构,它帮助数据库高效的进行数据的检索。在数据之外,数据库系统还维护着满足特定查找算法的数据结构(额外的存储空间),这些数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高效的查找算法。这种数据结构就叫做索引。

一般来说索引本身也很大,不可能全部存储在内存中,因此往往以索引文件的形式存放在磁盘中。目前大多数索引都采用BTree树方式构建。

分类
单值索引:一个索引只包括一个列,一个表可以有多个列
唯一索引:索引列的值必须唯一,但允许有空值;主键会自动创建唯一索引
复合索引:一个索引同时包括多列
创建索引
#查看索引,主键会自动创建索引

show index from dept;

删除索引

alter table dept drop index fuhe_index

索引扫描类型
type:

ALL 全表扫描,没有优化,最慢的方式
index 索引全扫描,其次慢的方式
range 索引范围扫描,常用语<,<=,>=,between等操作
ref 使用非唯一索引扫描或唯一索引前缀扫描,返回单条记录,常出现在关联查询中
eq_ref 类似ref,区别在于使用的是唯一索引,使用主键的关联查询
const/system 单条记录,系统会把匹配行中的其他列作为常数处理,如主键或唯一索引查询,system是const的特殊情况
null MySQL不访问任何表或索引,直接返回结果

最左特性

当我们创建一个联合索引(复合索引)的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)、(k1,k3)和(k1,k2,k3)索引,这就是最左匹配原则,也称为最左特性。

为何索引快?
明显查询索引表比直接查询数据表要快的多,首先,索引表是排序了,可以类似二分查找,非常有效的提高了查询的速度。

其过程如下图,先到事先排序好的索引表中检索查询,找到其主键后,就直接定位到记录所在位置,然后直接返回这条数据。

排序,tree结构,类似二分查找
索引表小

小结
优点:
索引是数据库优化
表的主键会默认自动创建索引
每个字段都可以被索引
大量降低数据库的IO磁盘读写成本,极大提高了检索速度
索引事先对数据进行了排序,大大提高了查询效率

缺点:
索引本身也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也要占用空间
索引表中的内容,在业务表中都有,数据是重复的,空间是“浪费的”
虽然索引大大提高了查询的速度,但对数据的增、删、改的操作需要更新索引表信息,如果数据量非常巨大,更新效率就很慢,因为更新表时,MySQL不仅要保存数据,也要保存一下索引文件
随着业务的不断变化,之前建立的索引可能不能满足查询需求,需要消耗我们的时间去更新索引

3.7视图View

概念
可视化的表,视图当做是一个特殊的表,是指,把sql执行的结果,直接缓存到了视图中。
下次还要发起相同的sql,直接查视图。现在用的少,了解即可.
使用: 1,创建视图 2,使用视图

测试

3.8SQL优化

创建mysql-db库

CREATE DATABASE /*!32312 IF NOT EXISTS*/`mysql-db` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `mysql-db`;

准备student表

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` varchar(4) NOT NULL,
`NAME` varchar(20) DEFAULT NULL,
`sex` char(2) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`salary` decimal(7,2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `student`(`id`,`NAME`,`sex`,`birthday`,`salary`) values ('1','张慎政','男','2020-01-01','10000.00'),('2','刘沛霞','女','2020-01-02','10000.00'),('3','刘昱江','男','2020-01-03','10000.00'),('4','齐雷','男','2020-01-04','20000.00'),('5','王海涛','男','2020-01-05','20000.00'),('6','董长春','男','2020-01-06','10000.00'),('7','张久军','男','2020-01-07','20000.00'),('8','陈子枢','男','2020-10-11','3000.00');

准备tb_dept表

DROP TABLE IF EXISTS `tb_dept`;
CREATE TABLE `tb_dept` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL,
`sort` int(11) DEFAULT NULL,
`note` varchar(100) DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`updated` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
insert into `tb_dept`(`id`,`name`,`parent_id`,`sort`,`note`,`created`,`updated`) values (1,'集团',0,1,'集团总部','2018-10-02 09:15:14','2018-09-27 16:35:54'),(2,'财务部',1,2,'财务管理','2018-09-27 16:35:52','2018-09-27 16:34:15'),(3,'软件部',1,3,'开发软件、运维','2018-09-27 16:35:54','2018-09-27 16:34:51');

准备tb_user表

DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`dept_id` int(11) DEFAULT NULL,
`username` varchar(50) DEFAULT NULL,
`password` varchar(100) DEFAULT NULL,
`salt` varchar(50) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`mobile` varchar(100) DEFAULT NULL,
`valid` tinyint(4) DEFAULT NULL,
`created` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`updated` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
insert into `tb_user`(`id`,`dept_id`,`username`,`password`,`salt`,`email`,`mobile`,`valid`,`created`,`updated`) values (1,1,'陈集团','123456',NULL,'tony@sina.com','13572801415',1,'2018-09-30 09:32:18','2018-09-30 09:32:18'),(2,3,'牛软件','567890',NULL,'niu@sina.com','13208737172',0,'2018-10-02 09:23:19','2018-09-20 09:32:18');

* 查询SQL尽量不要使用select ,而是具体字段


查询尽量避免返回大量数据

如果查询返回数据量很大,就会造成查询时间过长,网络传输时间过长。同时,大量数据返回也可能没有实际意义。如返回上千条甚至更多,用户也看不过来。

通常采用分页,一页习惯10/20/50/100条。

使用explain分析你SQL执行计划
SQL很灵活,一个需求可以很多实现,那哪个最优呢?SQL提供了explain关键字,它可以分析你的SQL执行计划,看它是否最佳。Explain主要看SQL是否使用了索引。

EXPLAIN
SELECT * FROM student WHERE id=1

是否使用了索引及其扫描类型

type:

ALL 全表扫描,没有优化,最慢的方式
index 索引全扫描
range 索引范围扫描,常用语<,<=,>=,between等操作
ref 使用非唯一索引扫描或唯一索引前缀扫描,返回单条记录,常出现在关联查询中
eq_ref 类似ref,区别在于使用的是唯一索引,使用主键的关联查询
const/system 单条记录,系统会把匹配行中的其他列作为常数处理,如主键或唯一索引查询
null MySQL不访问任何表或索引,直接返回结果
key:

真正使用的索引方式
创建name字段的索引

ALTER TABLE student ADD INDEX index_name (NAME)

优化like语句
模糊查询,程序员最喜欢的就是使用like,但是like很可能让你的索引失效

反例:

EXPLAIN
SELECT id,NAME FROM student WHERE NAME LIKE '%1'
EXPLAIN
SELECT id,NAME FROM student WHERE NAME LIKE '%1%'

正例:

EXPLAIN
SELECT id,NAME FROM student WHERE NAME LIKE '1%'

字符串怪现象
反例:

#未使用索引
EXPLAIN
SELECT * FROM student WHERE NAME=123

正例:

#使用索引
EXPLAIN
SELECT * FROM student WHERE NAME='123'

理由:
为什么第一条语句未加单引号就不走索引了呢?这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为数值类型再做比较

索引不宜太多,一般5个以内
索引并不是越多越好,虽其提高了查询的效率,但却会降低插入和更新的效率
索引可以理解为一个就是一张表,其可以存储数据,其数据就要占空间
再者,索引表的一个特点,其数据是排序的,那排序要不要花时间呢?肯定要
insert或update时有可能会重建索引,如果数据量巨大,重建将进行记录的重新排序,所以建索引需要慎重考虑,视具体情况来定
一个表的索引数最好不要超过5个,若太多需要考虑一些索引是否有存在的必要
索引不适合建在有大量重复数据的字段上
如性别字段。因为SQL优化器是根据表中数据量来进行查询优化的,如果索引列有大量重复数据,Mysql查询优化器推算发现不走索引的成本更低,很可能就放弃索引了。

where限定查询的数据
数据中假定就一个男的记录

反例:

SELECT id,NAME FROM student WHERE sex='男'

正例:

SELECT id,NAME FROM student WHERE id=1 AND sex='男'

理由:

需要什么数据,就去查什么数据,避免返回不必要的数据,节省开销

避免在where中对字段进行表达式操作
反例:

EXPLAIN
SELECT * FROM student WHERE id+1-1=+1

正例:

EXPLAIN
SELECT * FROM student WHERE id=+1-1+1
EXPLAIN
SELECT * FROM student WHERE id=1

理由:

SQL解析时,如果字段相关的是表达式就进行全表扫描
避免在where子句中使用!=或<>操作符

应尽量避免在where子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。记住实现业务优先,实在没办法,就只能使用,并不是不能使用。如果不能使用,SQL也就无需支持了。

反例:

EXPLAIN
SELECT * FROM student WHERE salary!=3000
EXPLAIN
SELECT * FROM student WHERE salary<>3000

理由:

使用!=和<>很可能会让索引失效
去重distinct过滤字段要少

索引失效

EXPLAIN
SELECT DISTINCT * FROM student

索引生效

EXPLAIN
SELECT DISTINCT id,NAME FROM student
EXPLAIN
SELECT DISTINCT NAME FROM student

理由:

带distinct的语句占用cpu时间高于不带distinct的语句。因为当查询很多字段时,如果使用distinct,数据库引擎就会对数据进行比较,过滤掉重复数据,然而这个比较、过滤的过程会占用系统资源,如cpu时间

where中使用默认值代替null
#修改表,增加age字段,类型int,非空,默认值0

ALTER TABLE student ADD age INT NOT NULL DEFAULT 0;

批量插入性能提升
大量数据提交,上千,上万,批量性能非常快,mysql独有

多条提交:

INSERT INTO student (id,NAME) VALUES(4,'齐雷');
INSERT INTO student (id,NAME) VALUES(5,'刘昱江');

批量提交:

INSERT INTO student (id,NAME) VALUES(4,'齐雷'),(5,'刘昱江');

理由:

默认新增SQL有事务控制,导致每条都需要事务开启和事务提交;而批量处理是一次事务开启和提交。自然速度飞升
数据量小体现不出来

批量删除优化
避免同时修改或删除过多数据,因为会造成cpu利用率过高,会造成锁表操作,从而影响别人对数据库的访问。

反例:

#一次删除10万或者100万+?

delete from student where id <100000;

采用单一循环操作,效率低,时间漫长

for(User user:list){
delete from student;
}

正例:

//分批进行删除,如每次500

for(){
delete student where id<500;
delete student where id>=500 and id<1000;

理由:

一次性删除太多数据,可能造成锁表,会有lock wait timeout exceed的错误,所以建议分批操作

伪删除设计
商品状态(state):1-上架、2-下架、3-删除

理由:
这里的删除只是一个标识,并没有从数据库表中真正删除,可以作为历史记录备查
同时,一个大型系统中,表关系是非常复杂的,如电商系统中,商品作废了,但如果直接删除商品,其它商品详情,物流信息中可能都有其引用。
通过where state=1或者where state=2过滤掉数据,这样伪删除的数据用户就看不到了,从而不影响用户的使用
操作速度快,特别数据量很大情况下

提高group by语句的效率
可以在执行到该语句前,把不需要的记录过滤掉

反例:先分组,再过滤

select job,avg(salary) from employee
group by job
having job ='president' or job = 'managent';

正例:先过滤,后分组

select job,avg(salary) from employee
where job ='president' or job = 'managent'
group by job;

复合索引最左特性
创建复合索引,也就是多个字段

ALTER TABLE student ADD INDEX idx_name_salary (NAME,salary)

满足复合索引的左侧顺序,哪怕只是部分,复合索引生效

EXPLAIN
SELECT * FROM student WHERE NAME='陈子枢'

没有出现左边的字段,则不满足最左特性,索引失效

EXPLAIN
SELECT * FROM student WHERE salary=3000

复合索引全使用,按左侧顺序出现 name,salary,索引生效

EXPLAIN
SELECT * FROM student WHERE NAME='陈子枢' AND salary=3000

虽然违背了最左特性,但MYSQL执行SQL时会进行优化,底层进行颠倒优化

EXPLAIN
SELECT * FROM student WHERE salary=3000 AND NAME='陈子枢'

理由:
复合索引也称为联合索引
当我们创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则
联合索引不满足最左原则,索引一般会失效,但是这个还跟Mysql优化器有关的

排序字段创建索引
什么样的字段才需要创建索引呢?原则就是where和order by中常出现的字段就创建索引。

#使用 *,包含了未索引的字段,导致索引失效

EXPLAIN