1 数据库
数据库是存储数据,管理数据的仓库。
常见的数据库有两种:
关系型数据库:数据关系紧密。关系模型是二维表格模型,一个关系型数据库就是由二维表及其之间的关系组成的一个数据集合。
Oracle、MySQL、SQLServer、Access
非关系型数据库:数据关系松散。有:
MongoDB、Redis、Solr、ElasticSearch、Hive、HBase
2 MySql的安装
1)版本:mysql-5.5.27-winx64.msi
2)Mysql数据库默认的编码是latin1等价于iso-8859-1,修改为utf-8
3)用户名和密码统一设置成“root”
3 SQL语句
结构化查询语言
4 字段类型
1 命名规则
1)字段名必须以字母开头,尽量不要使用拼音
2)长度不能超过30个字符(不同数据库,不同版本会有不同)
3)不能使用SQL的保留字,如where,order,group
4)只能使用如下字符a~z、A~Z、0~9、$ 等
5)Oracle习惯全大写:USER_NAME,mysql习惯全小写:user_name
6)多个单词用下划线隔开,而非java语言的驼峰规则
2 字符
1)char:长度固定,不足使用空格填充,最多容纳2000个字符,char(11)存储abc,占11位。查询速度极快但浪费空间
2)varchar:变长字符串,最多容纳4000个字符,varchar(11)存储abc,只占3位。查询稍慢,但节省空间。Oracle为varchar2
3)不同数据库版本长度限制可能会有不同
3 数字
1)int、int(3):oracle不支持,使用number替代
2)numeric(5,2):3位整数,2位小数
3)decimal和numeric等效
4 日期
1)date 包含年月日
2)datetime 包含年月日和时分秒
3)timestamp 时间戳,不是日期,而是从1970年1月1日到指定日期的毫秒数
5 大文本
大量文字(不推荐使用,尽量使用varchar替代)。
以utf8编码计算的话,一个汉字在占3个字节
1)LANGTEXT:1431655765个汉字,14亿。存储空间占用:4G的数据
2)MEDIUMTEXT:5592405个汉字,560万。存储空间占用:16M的数据
3)TEXT:21845个汉字,约20000。存储空间占用:64K的数据
6图片
1)blob 二进制数据,可以存放图片、声音,容量4g
2)缺点明显,数据库庞大,备份缓慢
3)目前主流只存储其访问路径,文件则存放在磁盘上
5表操作
1创建库
#创建数据库,设置字符集u8,防止中文乱码,注意下面的`刀秋不是单撇哦
CREATE DATABASE `mysql-db` DEFAULT CHARSET=utf8;
2创建表
#学生表
CREATE TABLE student(
id INT(4), #学号
NAME VARCHAR(20), #姓名
sex CHAR(2), #性别
birthday DATE, #出生日期
salary NUMERIC(7,2), #奖学金
PRIMARY KEY(id) #主键,记录唯一标识
);
#查看表结构
DESC student;
注意:
1)SQL可以写成一行中间无需换行
2)SQL不区分大小写,oracle中习惯表名字段名都大写,mysql中习惯小写
3 修改表
不是重点,一般都使用可视化方式修改,了解即可
#添加字段
ALTER TABLE student ADD(classid INT);
#修改字段长度
ALTER TABLE student MODIFY COLUMN NAME VARCHAR(30);
#注意长度不能小于已有的数据长度,否则会被永久破坏,不可修复
#删除字段
ALTER TABLE student DROP COLUMN classid;
#修改表名
RENAME TABLE student TO tb_student;
RENAME TABLE tb_student TO student;
4删除表
#删除表
DROP TABLE student;
删除表,数据都自动被删除,数据将永久性丢失
6 数据操作 CRUD
1新增
#不声明字段,顺序必须和表的一致
INSERT INTO student VALUES(1,'陈子枢','男','1988-10-11',3000);
#声明全部字段,顺序可以不一致,推荐方式
INSERT INTO student (id,NAME,sex,salary,birthday)
VALUES(2,'陈晨','女',30000,'2020-02-02');
#声明部分字段,没有的字段默认值null
INSERT INTO student (id,NAME) VALUES(3,'王涛');
2修改
#修改一个字段,必须设置where条件
UPDATE student SET sex='女' WHERE id=1;
#修改某个字段设置为null
UPDATE student SET NAME=NULL WHERE id=3;
#删库跑路蹲局子
UPDATE student SET NAME='我爸是李刚';修改了所有数据
如何预防这种行为?一般在企业中开发者会有测试数据库,不会直接连接生产环境的数据库。
3删除
#删除一条记录,必须有where条件
DELETE FROM student WHERE id=3;
#删除所有数据
DELETE FROM student;
4查询
#查询所有数据
SELECT * FROM student;
#id为1的记录
SELECT * FROM student WHERE id=1;
7 约束 constraints
1 非空约束 not null
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user(
id INT AUTO_INCREMENT,
NAME VARCHAR(30) UNIQUE NOT NULL,
age INT,
phone VARCHAR(20) UNIQUE NOT NULL,
email VARCHAR(30) UNIQUE NOT NULL,
PRIMARY KEY (id)
);
DESC tb_user;
#id为自增主键,null值无效,数据库会自动用下一个id值替代
#age因为运行为null,所以可以设置为null
INSERT INTO tb_user (id,age) VALUES(NULL,NULL);
2 唯一约束 unique
Name字段创建了唯一约束,插入数据时数据库会进行检查,如果插入的值相同,就会检查报错:
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user(
id INT,
NAME VARCHAR(30) UNIQUE NOT NULL,
phone VARCHAR(20) UNIQUE NOT NULL,
email VARCHAR(30) UNIQUE NOT NULL,
PRIMARY KEY (id)
);
DESC tb_user;
INSERT INTO tb_user (id,NAME) VALUES(1,'tony');
INSERT INTO tb_user (id,NAME) VALUES(2,'tony');
执行上面语句出错:
Query : INSERT INTO tb_user (id,NAME) VALUES(2,'tony')
Error Code : 1062
Duplicate entry 'tony' for key 'name'
展示表结构:
DESC tb_user;
3 主键约束 primary key
主键是一条记录的唯一标识,具有唯一性,不能重复
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user(
id INT,
NAME VARCHAR(30),
PRIMARY KEY (id)
);
INSERT INTO tb_user (id,NAME) VALUES(1,'tony');
INSERT INTO tb_user (id,NAME) VALUES(1,'hellen');
第二句插入就会报错:
Query : INSERT INTO tb_user (id,NAME) VALUES(1,'hellen')
Error Code : 1062
Duplicate entry '1' for key 'PRIMARY'
提示主键1的值已经存在,重复了
4 外键约束 forgrein key
DROP TABLE IF EXISTS tb_user_address; #如果表存在则删除,慎用会丢失数据
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, #自增主键
NAME VARCHAR(50) NOT NULL UNIQUE, #非空,唯一索引
sex CHAR(2) DEFAULT '男', #默认值
phone CHAR(18),
age INT,
CHECK (age>0 AND age<=200),
createdTime DATE DEFAULT NOW()
);
CREATE TABLE tb_user_address (
user_id INT PRIMARY KEY NOT NULL,
address VARCHAR(200),
FOREIGN KEY(user_id) REFERENCES tb_user(id)
);
DESC tb_user;
tb_user_address中user_id字段录入tb_user表不存在的主键值,将报错
5默认约束 default
设置默认值
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, #自增主键
NAME VARCHAR(50) NOT NULL UNIQUE, #非空,唯一索引
sex CHAR(2) DEFAULT '男', #默认值
phone CHAR(18),
age INT,
createdTime DATE DEFAULT NOW()
);
DESC tb_user;
6检查约束 check
很少使用,了解即可
DROP TABLE IF EXISTS tb_user; #如果表存在则删除,慎用会丢失数据
CREATE TABLE tb_user (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT, #自增主键
NAME VARCHAR(50) NOT NULL UNIQUE, #非空,唯一索引
sex CHAR(2) DEFAULT '男', #默认值
phone CHAR(18),
age INT,
CHECK (age>0 AND age<=200),
createdTime DATE DEFAULT NOW()
);
DESC tb_user;
录入age超过200将报错
8 数据表
使用表来说明Mysql其他功能
1部门表 dept
字段名称 | 数据类型 | 是否为空 | 备注 |
deptno | int | 部门编号,PK主键 | |
dname | varchar(20) | Y | 部门名称 |
loc | varchar(13) | Y | 部门所在地点 |
Mysql:
CREATE TABLE dept(
deptno NUMERIC(2) NOT NULL,
dname VARCHAR(14),
loc VARCHAR(13)
);
INSERT INTO dept VALUES(1,'accounting','一区');
INSERT INTO dept VALUES(2,'research','二区');
INSERT INTO dept VALUES(3,'operations','二区');
2 员工表 emp
字段名称 | 数据类型 | 是否为空 | 备注 |
empno | int | 员工编号,PK主键 | |
ename | varchar(10) | Y | 员工名称 |
job | varchar(10) | Y | 职位 |
mgr | int | Y | 上级编号 |
hiredate | datetime | Y | 月工资 |
sal | decimal(8,2) | Y | 奖金 |
comm | decimal(8,2) | Y | 奖金 |
deptno | int | Y | 所属部门 FK外键 |
Mysql:
CREATE TABLE emp(
empno NUMERIC(4) NOT NULL,
ename VARCHAR(10),
job VARCHAR(9),
mgr NUMERIC(4),
hiredate DATE,
sal NUMERIC(7,2),
comm NUMERIC(7,2),
deptno NUMERIC(2)
);
INSERT INTO emp VALUES(100,'jack','副总',NULL,'2002-05-03',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);
3 员工扩展信息表 empext
字段名称 | 数据类型 | 是否为空 | 备注 |
empno | int | 员工编号,PK主键 | |
address | varchar(300) | Y | 员工名称 |
phone | varchar(20) | Y | 电话 |
cardno | varchar(20) | Y | 省份证号 |
Mysql:
CREATE TABLE `empext` (
`empno` int(11) NOT NULL,
`address` varchar(300) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`cardno` varchar(20) DEFAULT NULL,
PRIMARY KEY (`empno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
9基础函数
lower:转小写
SELECT 'ABC',LOWER('ABC');
SELECT LOWER(dname),UPPER(dname) FROM dept;
upper:转大写
SELECT LOWER(dname),UPPER(dname) FROM dept;
length:长度
SELECT dname,LENGTH(dname) FROM dept;
substr:切割字符串
SELECT dname,SUBSTR(dname,1,3) FROM dept;
concat:拼接字符串
SELECT CONCAT(dname,'(',loc,')') FROM dept;
Aaa:首字母大写
SELECT
dname,
SUBSTR(dname,1,1),UPPER(SUBSTR(dname,1,1)),
CONCAT(UPPER(SUBSTR(dname,1,1)),SUBSTR(dname,2,LENGTH(dname)))
FROM dept;
replace:字符替换
SELECT loc,REPLACE(loc,'区','区域') FROM dept;
SELECT REPLACE(REPLACE(REPLACE(loc,'一',1),'二',2),'三',3) FROM dept;
round & ceil & floor
round四舍五入,ceil向上取整,floor向下取整
SELECT ename,
comm,ROUND(comm,1),ROUND(comm,2),
CEIL(comm),
FLOOR(comm) FROM emp;
uuid:返回uuid
SELECT UUID()
SELECT
REPLACE(UUID(),'-','') UUID,
LENGTH(REPLACE(UUID(),'-','')) len
返回uuid:a08528ca-741c-11ea-a9a1-005056c00001
转义字符:\
单撇作为sql语句符号,内容中出现单撇就会乱套,进行转义即可
SELECT 'ab\'cde'
10日期函数
now:获取当前时间
SELECT NOW(),CURDATE(),CURTIME(),SYSDATE()
select sysdate from dual #oracle当前时间,dual内置虚拟表
year & month & day
年月日,时分秒
SELECT hiredate,YEAR(hiredate) ,MONTH(hiredate),DAY(hiredate)
FROM emp
SELECT HOUR(NOW()),MINUTE(NOW()),SECOND(NOW()),NOW()
date_format
日期转字符串,注意格式的大小写,不错错误
SELECT DATE_FORMAT(NOW(),'%Y-%m-%d')
SELECT DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%s')
last_day
每月最大日期
SELECT LAST_DAY(NOW())
SELECT ename,hiredate,LAST_DAY(hiredate) FROM emp
#每月的第一天和最后一天
SELECT DATE_FORMAT(hiredate,'%Y-%m-01'),LAST_DAY(hiredate) FROM emp
str_to_date
字符串转日期
SELECT STR_TO_DATE('2020-05-08','%Y-%m-%d')
11条件查询
distinct
去除重复的记录行
SELECT loc FROM dept;
SELECT DISTINCT loc FROM dept;
where
Oracle区分大小写,Mysql不区分大小写
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' AND deptno=1;
#Oracle区分大小写,Mysql不区分大小写
SELECT * FROM dept WHERE dname='ACCOUNTING';
SELECT * FROM dept WHERE dname='Accounting';
SELECT * FROM dept WHERE dname='accounting';
case when..then..else end
类似java的if-elseif-else选择,根据条件展示值
SELECT nickname,user_name,
CASE WHEN user_rank = '5' THEN '经销商'
WHEN user_rank = '6' THEN '代理商'
WHEN user_rank = '7' THEN 'VIP'
ELSE '注册用户' END AS user_rank
end
FROM at_users
like
通配符%代表0到n个字符,通配符下划线_代表1个字符
SELECT * FROM emp WHERE ename LIKE 't%'; #t字母开头,效率高
SELECT * FROM emp WHERE ename LIKE '%n%'; #中间含有n,惯用
SELECT * FROM emp WHERE ename LIKE 't___'; #3个下划线
SELECT * FROM emp WHERE ename LIKE '__n%'; #2个下划线
and & or
and并且,or或者
SELECT * FROM dept WHERE dname='accounting' AND loc='一区';
SELECT * FROM dept WHERE dname='accounting' OR loc='二区';
null
SELECT * FROM emp WHERE mgr IS NULL; #字段内容为null的
SELECT * FROM emp WHERE mgr IS NOT NULL; #字段内容不为null的
nvl
字段值为null时替换,mgr为null替换为‘无’,comm数字替换为0
SELECT nvl(mgr,'空'),nvl(comm,0) FROM emp
between-and
between x and y 在x和y之间的值 [x,y]
SELECT * FROM emp
WHERE sal BETWEEN 5000 AND 10000;
等价于
SELECT * FROM emp
WHERE sal >= 5000 AND sal <= 10000;
union
把多个结果集合并,前提条件,两个结果集列对应,个数和类型一致
SELECT * FROM emp WHERE empno=100
UNION
SELECT * FROM emp WHERE empno=200
#报错:The used SELECT statements have a different number of columns
SELECT * FROM emp
UNION
SELECT * FROM dept
#字段个数相同,类型相同,下面SQL没有实际意义
SELECT empno,ename FROM emp
UNION
SELECT deptno,dname FROM dept
limit
limit n,返回前n条。
SELECT * FROM emp LIMIT 3 #返回前3条
SELECT * FROM emp LIMIT 0,3 #返回从0开始,取3条
SELECT * FROM emp LIMIT 1,3 #返回从1开始,取3条
Oredr By:排序
DESC:降序
ASC:升序
12聚合函数 aggregation
count:记录总数
select * from emp;
select count(*) from emp;
select count(1) from emp;
select count(empno) from emp;
习惯使用*的方式,推荐使用后两种方式
max:最大值
SELECT MAX(sal) FROM emp;
SELECT MAX(empno),MAX(sal),MAX(comm) FROM emp;
min:最小值
SELECT MIN(sal) FROM emp;
SELECT MIN(sal),MAX(sal) FROM emp;
avg:平均值
SELECT AVG(sal) FROM emp
sum:合计
SELECT SUM(sal) FROM emp
SELECT SUM(sal),SUM(comm) FROM emp;
13分组 group
group by
1)group by 用于对查询的结果进行分组统计
2)结果中的非聚合列必须出现在分组中
SELECT deptno,MAX(sal),AVG(sal) FROM emp
GROUP BY deptno
ORDER BY MAX(sal)
#每个部门每个岗位的最高薪资和平均薪资,结果中的非聚合列必须出现在分组中,否则业务意义不对
SELECT deptno,job,MAX(sal),AVG(sal) FROM emp
GROUP BY deptno,job
ORDER BY MAX(sal)
having
having 子句类似where限制返回结果,where用在主句中,having用在分组中
分组后数据的过滤,having专门配合groupby
#平均工资小于8000的部门
SELECT deptno,AVG(sal) FROM emp
GROUP BY deptno
HAVING AVG(sal)<8000
#学生姓名重名名单*
SELECT NAME,COUNT(NAME) FROM student
GROUP BY NAME
HAVING COUNT(NAME)>1
14 注意事项
1 char和varchar的区别
1)char为定长字符串,最大为255
2)varchar为不定长字符串,最大长度为65535
3)char会用空格补齐;而varchar只有固定长度
2 datetime和timestamp有什么区别?
1)date日期、time时间、datetime日期+时间、timestamp时间戳
2)datetime日期+时间,存储和显示是一样的
3)timestamp时间戳,存储的不是个日期,而是从1970年1月1日到指定日期的毫秒数
3中文乱码
如果在dos命令下执行insert插入中文数据,数据乱码,那现在sqlYog客户端执行下面命令:
set names utf8;
set names gbk;
1)设置客户端字符集和服务器端相同。两个都尝试下,哪个最后操作完成,查询数据库不乱码,就用哪个。
2)Mysql数据库默认字符集是lantin1,也就是以后网页中遇到的ISO8859-1,它是英文字符集,不支持存放中文。我们创建库时,可以指定字符集:
create database yhdb charset utf8;
3)保证创建数据库时用utf8,使用可视化工具一般就基本正确。
4别名
select * from tb_user; #查询所有记录
select name from tb_user; #查询tb_user表的name字段
select name as n1 from tb_user; #查询tb_user的name字段,结果显示为n1
5注释
/* 很多注释内容 */
# 一行注释内容
-- 一行注释内容,这个使用较多
6主键、外键、唯一索引的区别?
Primary Key 主键约束,自动创建唯一索引
Foreign Key 外键约束,外键字段的内容是引用另一表的字段内容
Unique Index 唯一索引,唯一值但不是主键,
数据库会进行检查,违反约束会报错,操作失败。
7drop、delete和truncate之间的区别
1)drop:删除库或者表,数据和表结构
2)delete和truncate:只是删除表的数据
3)delete可以指定where条件,删除满足条件的记录,tuncate删除所有记录
4)对于自增字段的表,delete不会自增值清零,而truncate是把表记录和定义都删除了,然后重建表的定义,所以自增主键会重头开始计数
15 事务 transaction
1 事务4个条件
一般来说,事务是必须满足4个条件
1)原子性:一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。
事务在执行过程中如果发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
2)一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
3)隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力。
隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
4)持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
2使用前提
1)在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务
2)事务处理可以用来维护数据的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行
3)事务用来管理 insert、update、delete 语句,因为这些操作才会“破坏”数据,查询select语句是不会的
4)默认MySQL数据库的事务是开启,执行SQL后自动提交
3提交 commit
#多条语句时,批量执行,事务提交
SET @@autocommit = 0;
BEGIN;
INSERT INTO student (id) VALUES(5);
UPDATE student SET NAME='陈曦' WHERE id=5;
COMMIT;
#有了事务,多步操作就形成了原子性操作,高并发下也不会引起数据错乱
4回滚 rollback
#多条语句,批量执行,insert插入重复的主键导致失败,事务回滚
SET @@autocommit = 0;
BEGIN;
INSERT INTO student (id) VALUES(5);
UPDATE student SET NAME='陈曦2' WHERE id=5;
ROLLBACK;
5保存点 savepoint
删除数据是非常危险,怎么防止误操作?
先设置存储点,如果发现发现误操作删了数据,还可以回滚
#先设置手动提交,默认是自动提交
SET @@autocommit = 0;
SAVEPOINT chen;
DELETE FROM student;
ROLLBACK TO chen;
RELEASE SAVEPOINT chen;
COMMIT; #提交之后检查点就自动作废,删除了
16子查询 subquery
1概念
子查询是指嵌入在其他select语句中的select语句,也叫嵌套查询。
子查询执行效率低。记录少时效率影响不大、图方便直接使用,记录多时最好使用其它方式替代。
1单行子查询
返回结果为一个
--列出tony所在部门的所有人员
select deptno from emp where ename='tony';
select * from emp where deptno = (select deptno from emp where ename='tony');
deptno = 使用等号,后面的查询结果只能为一个值
2多行子查询
in子查询
select * from emp where job in ('经理','员工');
select * from emp where job in (select distinct job from emp);
17 多表联查 join
1 多表联查
多表查询是指基于两个和两个以上的表的查询。在实际应用中,查询单个表可能不能满足你的需求,如显示员工表emp中不只显示deptno,还要显示部门名称,而部门名称dname在dept表中。
#把两个表的数据都拼接起来
SELECT * FROM dept,emp
#查询出员工编号、员工工资及所在部门的名称
SELECT
d.dname,
e.empno,e.ename,e.sal
FROM emp e,dept d
1)多表查询都是先生成笛卡尔积,再进行数据的筛选过滤。
2)多表查询过程中,现在内存中构建一个大大的结果集,然后进行数据的过滤。
3)构建过程,使用的内存资源,包括过滤时的判断,都是既耗费资源,又浪费时间。因此,阿里规范中禁止3张表以上的联查
2 内连接 inner join
列出两张表都有的数据。
#显示部门2的员工和工资
SELECT * FROM emp e, dept d #会列出两张表的所有字段
WHERE e.deptno=d.deptno AND e.deptno=2
SELECT d.dname,e.ename,e.sal FROM emp e,dept d
WHERE e.deptno=d.deptno AND e.deptno=2
SELECT
d.dname,e.ename,e.job
FROM
(SELECT deptno,dname FROM dept) d
INNER JOIN
(SELECT deptno,ename,job FROM emp) e
ON d.deptno = e.deptno
WHERE d.deptno=2
3左连接 left join
把关联到的数据,从右表加入左表
SELECT
d.dname,e.ename,e.job
FROM
(SELECT deptno,dname FROM dept) d
LEFT JOIN
(SELECT deptno,ename,job FROM emp) e
ON d.deptno=e.deptno
4右连接 right join
把关联到的数据从左表加入右表,可以转化为左连接
select
d.dname,e.ename,e.job
from
(select deptno,dname from dept) d
right join
(select deptno,ename,job from emp) e
on d.deptno = e.deptno
5万能连接
所有的连接方式都可以转换为左连接!
左连接实现步骤:
第一步:左边查询括号写别名
第二步:右边查询括号写别名
第三步:left join横中间,后面加on是条件
第四步:select挑字段:别名后面写字段
18索引 index
1定义
1)索引是一种排好序的快速查找的数据结构,帮助数据库高效检索数据。
2)一般来说索引本身也很大,不可能全部存储在内存中,因此往往以索引文件的形式存放在磁盘中。
3)目前大多数索引都采用BTree树方式构建。
2分类
单值索引:一个索引只包括一个列,一个表可以有多个列
唯一索引:索引列的值必须唯一,但允许有空值
复合索引:一个索引同时包括多列
3优缺点
优点:
1)索引是数据库优化
2)表的主键会默认自动创建唯一索引
3)降低磁盘读写成本,极大提高检索速度
4)索引事先对数据进行排序,提高查询的速度,降低CPU的消耗
缺点:
1)索引本身也是一张表,保存主键与索引字段,并指向实体表的记录,所以索引列也要占用空间
2)索引表中的内容,在业务表中都有,数据是重复的,空间是“浪费的”
3)虽然索引大大提高查询的速度,但反向影响增、删、改操作的效率。
4)随着业务不断变化,之前建立的索引可能不能满足查询需求,需要更新索引
4创建索引
ALTER TABLE empext ADD UNIQUE (phone)
ALTER TABLE empext ADD UNIQUE (cardno)
ALTER TABLE empext ADD INDEX phone_cardno_idx (phone,cardno)
SHOW INDEX FROM empext
5检查索引
EXPLAIN SELECT * FROM tb_user
EXPLAIN SELECT * FROM tb_user WHERE id=1
ALTER TABLE tb_user ADD UNIQUE (phone)
EXPLAIN SELECT * FROM tb_user WHERE phone='13572801415'
6判断是否创建索引
创建索引:
1)频繁出现在where查询条件的字段
2)多表查询中与其它表进行on关联的字段,外键关系
3)查询中经常用来排序的字段
4)查询中经常用来统计或者分组字段
5)单列索引/复合索引的选择? 高并发下倾向于创建复合索引
不创建索引:
1)频繁更新的字段: 每次更新都会影响索引树
2)where条件查询中用不到的字段
3)表记录太少
4)经常增删改的表: 更新了表,索引也得更新才行
5)如果一张表中重复的记录非常多,建立索引没有太大意义
7索引为什么快?
1)对数据排序
2)使用树形结构,类似二分查找,效率快
19 MySql执行顺序
1)FROM [left_table]:选择表
2)ON <join_condition>:链接条件
3)<join_type> JOIN <right_table>:链接
4)WHERE:条件过滤
5)GROUP BY:分组
6)AGG_FUNC:聚合
7)HAVING:分组过滤
8)SELECT:选择
9)DISTINCT:选择字段、去重
10)ORDER BY :排序
11)LIMIT count OFFSET count:分页
20 基础SQL优化
1 查询SQL尽量不要使用select *,而是具体字段
1)只取需要的字段,节省资源、减少网络开销
2)select * 进行查询时,很可能不会用到索引,就会造成全表扫描
反例:
SELECT * FROM student
正例:
SELECT id,NAME FROM student
2去重distinct过滤字段要少
#索引失效
EXPLAIN
SELECT DISTINCT * FROM student
#索引生效
EXPLAIN
SELECT DISTINCT id,NAME FROM student
EXPLAIN
SELECT DISTINCT NAME FROM student
3 使用varchar代替char
1)varchar变长字段按数据内容实际长度存储,存储空间小,可以节省存储空间
2)char按声明大小存储,不足补空格
3)对于查询来说,在一个较小的字段内搜索,效率更高
反例:
`deptname`char(100) DEFAULT NULL COMMENT '部门名称'
正例:
`deptname` varchar(100) DEFAULT NULL COMMENT '部门名称'
4 尽量使用数值替代字符串类型
1)主键(id):primary key优先使用数值类型int,tinyint
2)性别(sex):0-代表女,1-代表男
3)支付方式(payment):1-现金、2-微信、3-支付宝、4-信用卡、5-银行卡
4)服务状态(state):1-开启、2-暂停、3-停止
5)商品状态(state):1-上架、2-下架、3-删除
5 查询尽量避免返回大量数据
1)如果查询数据量很大,查询时间过长,网络传输时间过长
2)返回很多数据,用户看不过来
3)通常采用分页
6使用explain分析SQL是否使用了索引
EXPLAIN
SELECT * FROM student WHERE id=1
返回结果:
是否使用索引及其扫描类型
type:
1)ALL 全表扫描,没有优化,最慢的方式
2)index 索引全扫描
3)range 索引范围扫描,常用语<,<=,>=,between等操作
4)ref 使用非唯一索引或唯一索引前缀,返回单条记录,常出现在关联查询中
5)eq_ref 类似ref,区别在于使用的是唯一索引,使用主键的关联查询
6)const/system 单条记录,系统会把匹配行中的其他列作为常数处理,如主键或唯一索引查询
7)null MySQL不访问任何表或索引,直接返回结果
8)possible_keys:显示可能应用在这张表中的索引
9)key:真正使用的索引方式
7 优化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%'
8 索引不宜太多,一般5个以内
1)索引并不是越多越好,虽其提高查询的效率,但却会降低插入和更新的效率
2)索引可以存储数据,占空间
3)索引表的数据要排序,排序要花时间
4)insert或update时有可能会重建索引,如果数据量巨大,重建将进行记录的重新排序
5)一个表的索引数最好不要超过5个
9索引不适合建在有大量重复数据的字段上
如性别字段。如果索引列有大量重复数据,Mysql查询优化器推算发现不走索引的成本更低,很可能就放弃索引。
10 避免在内置函数中使用索引列
可能会导致索引失效
反例:
EXPLAIN
SELECT * FROM student
WHERE DATE_ADD(birthday,INTERVAL 7 DAY) >=NOW();
正例:
EXPLAIN
SELECT * FROM student
WHERE birthday >= DATE_ADD(NOW(),INTERVAL 7 DAY);
11 where限定查询的数据
需要什么数据,就去查什么数据,避免返回不必要的数据,节省开销
反例:
SELECT id,NAME FROM student WHERE sex='男'
正例:
SELECT id,NAME FROM student WHERE id=1 AND sex='男'
12避免在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
13避免在where子句中使用!=或<>操作符
有操作符,会放弃使用索引而进行全表扫描。
反例:
EXPLAIN
SELECT * FROM student WHERE salary!=3000
14 where中使用默认值代替null
1)is null,is not null可能让索引失效
2)把null值,换成默认值,让走索引成为可能
反例:
EXPLAIN
SELECT * FROM student WHERE age IS NOT NULL
正例:
EXPLAIN
SELECT * FROM student WHERE age>0
15 避免在where子句中使用or来连接条件
1)使用or可能会使索引失效,从而全表扫描
反例:
SELECT * FROM student WHERE id=1 OR salary=30000
正例:
# 使用union all
SELECT * FROM student WHERE id=1
UNION ALL
SELECT * FROM student WHERE salary=30000
# 分开两条sql写
SELECT * FROM student WHERE id=1
SELECT * FROM student WHERE salary=30000
21 高级SQL优化
1 批量插入性能提升
1)大量数据提交,批量性能非常快,mysql独有
2)默认新增SQL有事务控制,导致每条都需要事务开启和事务提交;而批量处理是一次事务开启和提交,速度快。
多条提交:
INSERT INTO student (id,NAME) VALUES(4,'齐雷');
INSERT INTO student (id,NAME) VALUES(5,'刘昱江');
批量提交:
INSERT INTO student (id,NAME) VALUES(4,'齐雷'),(5,'刘昱江');
2避免批量删除或修改过多数据,采取分批操作
1)避免同时修改或删除过多数据,因为会造成cpu利用率过高,会造成锁表,影响别人对数据库的访问。
2)采取分批操作,如每次500
反例:
#一次删除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;
3伪删除设计
1)删除只是一个标识,并没有从数据库表中真正删除,可以作为历史记录备查
2)大型系统中,表关系是非常复杂的,不能直接删除,其它模块可能引用
3)伪删除的数据用户就看不到,不影响用户的使用
4)操作速度快,特别数据量很大情况下
商品状态:1-上架、2-下架、3-删除
4 先过滤,后分组,提高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;
5 复合索引最左特性
1)当创建复合索引的时,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则
2)没有出现最左边的字段,复合索引不满足最左原则,索引会失效,跟Mysql优化器有关
3)满足复合索引的左侧顺序,哪怕只是部分,复合索引生效
创建复合索引,也就是多个字段
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='陈子枢'
6 排序字段创建索引,提高效率
7 删除冗余和重复的索引
8 不要有超过5个以上的表连接
1)关联的表个数越多,编译的时间和开销也就越大
2)每次关联内存中都生成一个临时表
3)阿里规范中,建议多表联查三张表以下
9 连接中,优先使用inner join,如果使用left join左边表尽量小
10尽量使用union all替代union
1)union和union all的区别是,union会自动去重,而union all则将所有的结果全部显示出来
2)union:对两个结果集进行并集操作,并去重,同时进行默认规则排序
3)多数情况,重复数据不多。
反例:
SELECT * FROM student
UNION
SELECT * FROM student
正例:
SELECT * FROM student
UNION ALL
SELECT * FROM student