数据库复习

DDL(Data Definition Language):数据定义语言,用来定义数据库对象:库、表、列等;
DML(Data Manipulation Language):数据操作语言,用来定义数据库记录(增、删、改)
DCL(Data Control Language):数据控制语言,用来定义访问权限和安全级别;
DQL(Data Query Language):数据查询语言,用来查询记录(数据)。

数据库列类型

数值:tinyint:十分小的数据,占一个字节
smallint:较小的数据,两个字节
mediumint 中等大小数据 三个字节
int: 标准整数 四个字节
bigint 较大数据 八个字节
float 浮点数 四个字节
double 浮点数 八个字节
decimal 字符串形式浮点数,精度问题,金融计算。

字符串: char 字符串固定大小 0-255
varchar 可变字符串 0-65535 常用变量
tinytext 微型文本 2^8-1
text 文本串 2^16-1 保存大文本

时间日期
java.util.Date
date: YYYY-MM-DD,日期格式
time:HH:mm:ss 时间格式
datetime :YYYY-MM-DD HH:mm-ss 最常用的时间格式
timestamp:时间戳
year:年份表示

null
没有值,未知,不要使用null进行运算,结果为null

每张表有固定的几个字段

每一个表,都必须存在以下五个字段(后面是阿里规范),表示一个字段存在的意义
id:主键
version:用来做乐观锁
id_delete:伪删除
gmt_creat 创建时间
gmt_update:修改时间

小知识

int(5)和varchar(20)什么区别?
答:int数据类型固定4个字节,int(5)和int(10)的区别:显示的数据的位数一个是5位,一个是10位,在开启0填充的情况下,会补0,实际位数大于的话,按照实际显示。
varchar(20)表示的是varchar数据的数据长度最大是20,超过则数据库不会存储。
总结:int(M)表示的不是数据的最大长度,只是数据宽度,并不影响存储多少位长度的数据,varchar(M)M表示的是varchar类型数据在数据库中存储的最大长度。,超过则不存。

几个小语句

SHOW CREAT DATABASE school --查看创建数据库的语句
SHOW CREAT TABLE student --查看student数据别哦的定义语句
DESC student --显示表的结构

修改和删除数据表字段

ALTER TABLE teacher RENAME AS TEACHER1; --修改表名
ALTER TABLE teacher1 ADD age INT(11); 增加表的字段
–修改表的字段(重命名或者修改约束)
ALTER TABLE teacher1 MODIFY age VARCHER(11); --修改约束
ALTER TABLE teacher1 CHANGE age age1 INT(1) --字段重命名

ALTER TABLE teacher1 DROP age1 --删除表字段

外键

方式一:创建表得时候增加约束,麻烦
–grade取名FK_grade_id
KEY ‘FK_grade_id’ (grade_id),
–设置FK_grade_id为外键, --关联grade表中的gradeid属性
CONSTRAINT ‘FK_grade_id’ FOREIGN KEY (‘grade_id’) REFERENCES ‘grade’ (‘gradeid’)

方式二:正常创建表,然后进行修改表添加外键。 约束名 约束字段 --表 字段
ALTER TABLE ‘student’ ADD CONSTRAINT ‘FK_gradeid’ FOREIGN KEY (‘gradeid’) REFERENCES ‘grade’ (‘gradeid’);

以上操作都是物理外键,数据库级别的外键,不建议使用,避免数据库过多造成的困扰。多表乱糟糟
(阿里的java规范:不得使用外键与级联,一切外键概念必须在应用层解决)
最佳实践
1.数据库就是单纯的表,只用来存放数据,只有行列
2.使用多张表的数据,用程序级别来进行外键的处理

关于数据库引擎

INNODB 默认使用的
MYISAM 早些年使用的
区别与联系:
在这里插入图片描述

myisam节约空间,速度较快
innodb安全性高,支持事务操作,支持多表多用户操作

在物理空间存在的位置:所有数据库文件都存在data目录下,本质还是文件的存储

MySQL引擎在物理文件上的区别:
innodb 在数据库表中只有一个*.frm文件,以及上级目录下的ibdata文件
myisam 对应文件: .frm(表结构的定义文件),.myd(数据文件,data),*myi (索引文件index)

DML语言

数据操作语言,insert、update、delete。

insert

INSERT INTO 表名(字段名1,字段名2,字段名3) VALUES (‘值1’,‘值2’,‘值3’),(‘值1’,‘值2’,‘值3’),(‘值1’,‘值2’,‘值3’)

INSERT INTO student(name,age,sex) VALUES (‘xiaoming’,‘22’,‘man’),(‘xiaohong’,‘20’,‘woman’),(‘xiaoli’,‘25’,‘man’)

update

修改一个字段的值,无条件

– 改变,无条件。格式:UPDATE 表名 SET 字段名 = 值

UPDATE student SET name = ‘BeiHang’ – 效果为所有name字段下的值都为BeiHang

修改一个字段的值,指定条件

-- 格式:UPDATE `表名` SET `字段名` = 值 WHERE id=值1 OR id=值2

UPDATE `student` SET `name` = 'DongDa' WHERE id=1 OR id=3 
-- 效果为所有id为1和3的name字段下的值都为DongDa

修改多个字段的值,指定条件

-- 格式:UPDATE `表名` SET `字段名1` = '值1',`字段名2` = '值2' WHERE 字段名 BETWEEN 数值1 AND 数值2

UPDATE `student` SET `name` = 'BeiHang',`sex` = 'man' WHERE age BETWEEN 20 AND 25 
-- 效果为所有age在20到25范围内的name变为BeiHang,sex变为man

删除(delete命令 或者 truncate命令)

delete命令 删除表中全部数据 和 指定数据

DELETE FROM `student`; -- 效果是把student表完全删除

DELETE FROM `student` WHERE id=1; -- 效果是把student表中的指定数据删除

truncate命令 删除表中全部数据

完全清空数据库,表的结构和索引约束不会变

TRUNCATE `student`; -- 效果是把student表完全删除

两个命令删除表中全部数据的区别

delete删除后,再往表中加入数据,自增的id,会继续之前的往下排序;

truncate删除后,自增的id不会往下排序。计数器归零,不影响事务

因此,希望删除表中全部数据时,建议使用truncate。

delete删除的问题:

1引擎是innodb,使用delete删除之后,重启数据库后自增列会从1开始,因为是在内存之中
2引擎是myisam,会从上一个自增量开始,因为是存储到文件中的

on和where的区别

on是先筛选后关联,where是先关联后筛选。
数据库在通过连接两张或多张表来返回记录时,都会生成一张中间的临时表,然后再将这张临时表返回给用户。
在使用left jion时,on和where条件的区别如下:
1、 on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。
2、where条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有left join的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。

不管on上的条件是否为真都会返回left或right表中的记录,full则具有left和right的特性的并集。 而inner join没这个特殊性,则条件放在on中和where中,返回的结果集是相同的。on为了反映外连接中一方的全连接,而where没有这个功能,内连接配对是可以的。

on、where、having的区别

on、where、having这三个都可以加条件的子句中,on是最先执行,where次之,having最后。有时候如果这先后顺序不影响中间结果的话,那最终结果是相同的。但因为on是先把不符合条件的记录过滤后才进行统计,它就可以减少中间运算要处理的数据,按理说应该速度是最快的。

根据上面的分析,可以知道where也应该比having快点的,因为它过滤数据后才进行sum,所以having是最慢的。但也不是说having没用,因为有时在步骤3还没出来都不知道那个记录才符合要求时,就要用having了。

在两个表联接时才用on的,所以在一个表的时候,就剩下where跟having比较了。在这单表查询统计的情况下,如果要过滤的条件没有涉及到要计算字段,那它们的结果是一样的,只是where可以使用rushmore技术,而having就不能,在速度上后者要慢。

如果要涉及到计算的字段,就表示在没计算之前,这个字段的值是不确定的,根据上篇写的工作流程,where的作用时间是在计算之前就完成的,而having就是在计算后才起作用的,所以在这种情况下,两者的结果会不同。

在多表联接查询时,on比where更早起作用。系统首先根据各个表之间的联接条件,把多个表合成一个临时表后,再由where进行过滤,然后再计算,计算完后再由having进行过滤。由此可见,要想过滤条件起到正确的作用,首先要明白这个条件应该在什幺时候起作用,然后再决定放在那里

对于JOIN参与的表的关联操作,如果需要不满足连接条件的行也在我们的查询范围内的话,我们就必需把连接条件放在ON后面,而不能放在WHERE后面,如果我们把连接条件放在了WHERE后面,那么所有的LEFT,RIGHT,等这些操作将不起任何作用,对于这种情况,它的效果就完全等同于INNER连接。对于那些不影响选择行的条件,放在ON或者WHERE后面就可以。
记住:所有的连接条件都必需要放在ON后面,不然前面的所有LEFT,和RIGHT关联将作为摆设,而不起任何作用。

DQL语言

查询小王子
基本的select语句就省略了,作为复习,抓重点和薄弱部分

  1. 使用别名:给结果起一个名字
SELECT `STUDENTNo` As 学号, `StudentName` AS 学生姓名 FROM student AS s;
  1. 函数 Concat(a,b)拼接字符串
SELECT CONCAT('姓名:',StudentName) AS 新名字 FROM student;
  1. 去重 DISTINCT
SELECT DISTINCT `StudentNo` FROM result;
  1. 其他查询
    查询系统版本(函数)
SELECT VERSION();

查询计算结果(表达式)

SELECT 100*3-1 AS 计算结果;

查询自增的步长(变量)

SELECT @@auto_increment_increment 

所有人分数+1

SELECT `StudentNo`,`StudentResult`+1 AS '提分后' FROM result

数据库的列(表达式):文本值,列,null,函数,计算表达式,系统变量。。。
select 表达式

模糊查询

LIKE 和%

--查找以刘开头的同学
SELECT `STUDENTNO`,`STUDENTNAME` FROM ``STUDENT WHERE SUTDENTNAME LIKE '刘%';

--查询刘X。
SELECT `STUDENTNO`,`STUDENTNAME` FROM ``STUDENT WHERE SUTDENTNAME LIKE '刘_';

--查询名字中间有小的同学
SELECT `STUDENTNO`,`STUDENTNAME` FROM ``STUDENT WHERE SUTDENTNAME LIKE '%小%';

IN 是接一个具体的值,

--查询1001,1002,1003号学院
SELECT `STUDENTNO`,`STUDENTNAME` FROM ``STUDENT WHERE STUDENTNO IN (1001,1002,1003);

NOT NULL

--查询有出生日期的同学
SELECT `STUDENTNO`,`STUDENTNAME` FROM `STUDENT` WHERE 'BORNDATE' IS NOT NULL

联表查询

在这里插入图片描述

小总结:
inner join:如果表中至少有一个匹配,就返回行
left join:即使右表种没有匹配,左边表中只要有,就返回行
right join:即使左表种没有匹配,右边表中只要有,就返回行

自连接

核心:自己的表和自己的表连接,一张表拆为两张表,

SELECT A.`CATEGORYNAME` AS '父栏目' , B.`CATEGORYNAME` AS '子栏目'
FROM `CATEGORY` AS A,`CATEGORY` AS B
WHERE A.`CATEGORY` =B.`PID` 

分页和排序

排序 order

一般前端进行排序展示

  1. 升序
ORDER BY STUDENTRESULT ASC
  1. 降序
ORDER BY STUDENTRESULT DESC

分页 limit

  1. 缓解数据库压力
  2. 给人好的体验
LIMIT 1,5;  --limit 起始值,数据量,这里是从1开始,5条数据

子查询

where语句中嵌套一个子查询语句。

SELECT `STUDENTNO`,`SUBJECTNO`,`STUDENTRESULT`
FROM `RESULT`
WHERE SUBJECT = (
	SELECT SUBJECTNO FROM `SUBJECT`
	WHERE SUBJECTNAME = `数据库`
)

MySQL函数

常用函数

数学函数

(1)ABS(x)

返回x的绝对值

(2)PI()

返回圆周率π,默认显示6位小数

(3)SQRT(x)

返回非负数的x的二次方根

(4)MOD(x,y)

返回x被y除后的余数

(5)CEIL(x)、CEILING(x)

返回不小于x的最小整数

(6)FLOOR(x)

返回不大于x的最大整数

(7)ROUND(x)、ROUND(x,y)

前者返回最接近于x的整数,即对x进行四舍五入;后者返回最接近x的数,其值保留到小数点后面y位,若y为负值,则将保留到x到小数点左边y位

(8)SIGN(x)

返回参数x的符号,-1表示负数,0表示0,1表示正数

(9)POW(x,y)和、POWER(x,y)

返回x的y次乘方的值

(10)EXP(x)

返回e的x乘方后的值

(11)LOG(x)

返回x的自然对数,x相对于基数e的对数

(12)LOG10(x)

返回x的基数为10的对数

(13)RADIANS(x)

返回x由角度转化为弧度的值

(14)DEGREES(x)

返回x由弧度转化为角度的值

(15)SIN(x)、ASIN(x)

前者返回x的正弦,其中x为给定的弧度值;后者返回x的反正弦值,x为正弦

(16)COS(x)、ACOS(x)

前者返回x的余弦,其中x为给定的弧度值;后者返回x的反余弦值,x为余弦

(17)TAN(x)、ATAN(x)

前者返回x的正切,其中x为给定的弧度值;后者返回x的反正切值,x为正切

(18)COT(x)

返回给定弧度值x的余切

字符串函数

(1)CHAR_LENGTH(str)

计算字符串字符个数

(2)CONCAT(s1,s2,…)

返回连接参数产生的字符串,一个或多个待拼接的内容,任意一个为NULL则返回值为NULL

(3)CONCAT_WS(x,s1,s2,…)

返回多个字符串拼接之后的字符串,每个字符串之间有一个x

(4)INSERT(s1,x,len,s2)

返回字符串s1,其子字符串起始于位置x,被字符串s2取代len个字符

(5)LOWER(str)和LCASE(str)、UPPER(str)和UCASE(str)

前两者将str中的字母全部转换成小写,后两者将字符串中的字母全部转换成大写

(6)LEFT(s,n)、RIGHT(s,n)

前者返回字符串s从最左边开始的n个字符,后者返回字符串s从最右边开始的n个字符

(7)LPAD(s1,len,s2)、RPAD(s1,len,s2)

前者返回s1,其左边由字符串s2填补到len字符长度,假如s1的长度大于len,则返回值被缩短至len字符;前者返回s1,其右边由字符串s2填补到len字符长度,假如s1的长度大于len,则返回值被缩短至len字符

(8)LTRIM(s)、RTRIM(s)

前者返回字符串s,其左边所有空格被删除;后者返回字符串s,其右边所有空格被删除

(9)TRIM(s)

返回字符串s删除了两边空格之后的字符串

(10)TRIM(s1 FROM s)

删除字符串s两端所有子字符串s1,未指定s1的情况下则默认删除空格

(11)REPEAT(s,n)

返回一个由重复字符串s组成的字符串,字符串s的数目等于n

(12)SPACE(n)

返回一个由n个空格组成的字符串

(13)REPLACE(s,s1,s2)

返回一个字符串,用字符串s2替代字符串s中所有的字符串s1

SELECT REPLACE(STUDENTNAME,'粥','洲') FROM STUDENT WHERE STUDENTNAME LIKE '粥%';

(14)STRCMP(s1,s2)

若s1和s2中所有的字符串都相同,则返回0;根据当前分类次序,第一个参数小于第二个则返回-1,其他情况返回1

(15)SUBSTRING(s,n,len)、MID(s,n,len)

两个函数作用相同,从字符串s中返回一个第n个字符开始、长度为len的字符串

(16)LOCATE(str1,str)、POSITION(str1 IN str)、INSTR(str,str1)

三个函数作用相同,返回子字符串str1在字符串str中的开始位置(从第几个字符开始)

(17)REVERSE(s)

将字符串s反转

(18)ELT(N,str1,str2,str3,str4,…)

返回第N个字符串

日期和时间函数

(1)CURDATE()、CURRENT_DATE()

将当前日期按照"YYYY-MM-DD"或者"YYYYMMDD"格式的值返回,具体格式根据函数用在字符串或是数字语境中而定

(2)CURRENT_TIMESTAMP()、LOCALTIME()、NOW()、SYSDATE()

这四个函数作用相同,返回当前日期和时间值,格式为"YYYY_MM-DD HH:MM:SS"或"YYYYMMDDHHMMSS",具体格式根据函数用在字符串或数字语境中而定

(3)UNIX_TIMESTAMP()、UNIX_TIMESTAMP(date)

前者返回一个格林尼治标准时间1970-01-01 00:00:00到现在的秒数,后者返回一个格林尼治标准时间1970-01-01 00:00:00到指定时间的秒数

(4)FROM_UNIXTIME(date)

和UNIX_TIMESTAMP互为反函数,把UNIX时间戳转换为普通格式的时间

(5)UTC_DATE()和UTC_TIME()

前者返回当前UTC(世界标准时间)日期值,其格式为"YYYY-MM-DD"或"YYYYMMDD",后者返回当前UTC时间值,其格式为"YYYY-MM-DD"或"YYYYMMDD"。具体使用哪种取决于函数用在字符串还是数字语境中

(6)MONTH(date)和MONTHNAME(date)

前者返回指定日期中的月份,后者返回指定日期中的月份的名称

(7)DAYNAME(d)、DAYOFWEEK(d)、WEEKDAY(d)

DAYNAME(d)返回d对应的工作日的英文名称,如Sunday、Monday等;DAYOFWEEK(d)返回的对应一周中的索引,1表示周日、2表示周一;WEEKDAY(d)表示d对应的工作日索引,0表示周一,1表示周二

(8)WEEK(d)、WEEKOFYEAD(d)

前者计算日期d是一年中的第几周,后者计算某一天位于一年中的第几周

(9)DAYOFYEAR(d)、DAYOFMONTH(d)

前者返回d是一年中的第几天,后者返回d是一月中的第几天

(10)YEAR(date)、QUARTER(date)、MINUTE(time)、SECOND(time)

YEAR(date)返回指定日期对应的年份,范围是19702069;QUARTER(date)返回date对应一年中的季度,范围是14;MINUTE(time)返回time对应的分钟数,范围是0~59;SECOND(time)返回制定时间的秒值

(11)EXTRACE(type FROM date)

从日期中提取一部分,type可以是YEAR、YEAR_MONTH、DAY_HOUR、DAY_MICROSECOND、DAY_MINUTE、DAY_SECOND

(12)TIME_TO_SEC(time)

返回以转换为秒的time参数,转换公式为"3600小时 + 60分钟 + 秒"

(13)SEC_TO_TIME()

和TIME_TO_SEC(time)互为反函数,将秒值转换为时间格式

(14)DATE_ADD(date,INTERVAL expr type)、ADD_DATE(date,INTERVAL expr type)

返回将起始时间加上expr type之后的时间,比如DATE_ADD(‘2010-12-31 23:59:59’, INTERVAL 1 SECOND)表示的就是把第一个时间加1秒

(15)DATE_SUB(date,INTERVAL expr type)、SUBDATE(date,INTERVAL expr type)

返回将起始时间减去expr type之后的时间

(16)ADDTIME(date,expr)、SUBTIME(date,expr)

前者进行date的时间加操作,后者进行date的时间减操作

条件判断函数

(1)IF(expr,v1,v2)

如果expr是TRUE则返回v1,否则返回v2

(2)IFNULL(v1,v2)

如果v1不为NULL,则返回v1,否则返回v2

(3)CASE expr WHEN v1 THEN r1 [WHEN v2 THEN v2] [ELSE rn] END

如果expr等于某个vn,则返回对应位置THEN后面的结果,如果与所有值都不想等,则返回ELSE后面的rn

系统信息函数

(1)VERSION()

查看MySQL版本号

(2)CONNECTION_ID()

查看当前用户的连接数

(3)USER()、CURRENT_USER()、SYSTEM_USER()、SESSION_USER()

查看当前被MySQL服务器验证的用户名和主机的组合,一般这几个函数的返回值是相同的

(4)CHARSET(str)

查看字符串str使用的字符集

(5)COLLATION()

查看字符串排列方式

加密函数

(1)PASSWORD(str)

从原明文密码str计算并返回加密后的字符串密码,注意这个函数的加密是单向的(不可逆),因此不应将它应用在个人的应用程序中而应该只在MySQL服务器的鉴定系统中使用

(2)MD5(str)

为字符串算出一个MD5 128比特校验和,改值以32位十六进制数字的二进制字符串形式返回

(3)ENCODE(str, pswd_str)

使用pswd_str作为密码,加密str

(4)DECODE(crypt_str,pswd_str)

使用pswd_str作为密码,解密加密字符串crypt_str,crypt_str是由ENCODE函数返回的字符串

其他函数

(1)FORMAT(x,n)

将数字x格式化,并以四舍五入的方式保留小数点后n位,结果以字符串形式返回

(2)CONV(N,from_base,to_base)

不同进制数之间的转换,返回值为数值N的字符串表示,由from_base进制转换为to_base进制

(3)INET_ATON(expr)

给出一个作为字符串的网络地址的点地址表示,返回一个代表该地址数值的整数,地址可以使4或8比特

(4)INET_NTOA(expr)

给定一个数字网络地址(4或8比特),返回作为字符串的该地址的点地址表示

(5)BENCHMARK(count,expr)

重复执行count次表达式expr,它可以用于计算MySQL处理表达式的速度,结果值通常是0(0只是表示很快,并不是没有速度)。另一个作用是用它在MySQL客户端内部报告语句执行的时间

(6)CONVERT(str USING charset)

使用字符集charset表示字符串str

聚合函数

all:默认值,对所有的值进行聚合函数运算包含重复值。

distinct: 消除重复值后进行聚合函数运算。

SELECT avg(ID) as ‘返回组中各值的平均值。空值将被忽略。 表达式为数值表达式。’ FROM TestTable

SELECT count([ActionType]) as '返回组中的项数。COUNT(*) 返回组中的项数。包括 NULL 值和重复项。如果指定表达式则忽略空值。表达式为任意表达式 ’ FROM TestTable
count(1) 或者count( ** ) ,如果是主键,count(列)比count(1)快,如果不是主键,则count(1)比count(列)快。本质都是计算行数。

select min(id) as ‘返回组中的最小值。空值将被忽略。表达式为数值表达式,字符串表达式,日期’ FROM TestTable

select max(id) as ‘返回组中的最大值。空值将被忽略。表达式为数值表达式,字符串表达式,日期’ FROM TestTable

select sum(id) as ‘返回组中所有值的和。空值将被忽略。表达式为数值表达式’ FROM TestTable

GROUP BY 分组

SELECT SUBJECTNAME, AVG(STUDENTRESULT) AS 平均分,MAX(STUDENTRESULT) AS 最高分, MIN(STUDENTRESULT) AS 最低分
FROM RESULT R
INNER JOIN SUBJECT SUB
ON R.SUBJECTNO = SUB.SUBJECTNO
--WHERE AVG(RESULT)>80, 分组后不能再用where进行筛选了,
GROUP BY R.SUBJECTNO
--用having,在group的下边
HAVING 平均分>80

MD5

--加密操作
SELECT testmd5 SET pwd=MD5(pwd) WHERE ID = 1;
--插入时加密
INSERT INTO testmd5 VALUES (4,'xiaoming',MD5('12345600'));

小结

SELECT [ALL | DISTINCT]
{* |table.* | [table.field1[as alias1][,table.field2[as alias2]][,...]]}
FROM table_name[as table_alias]
	[left | right | inner join table_name2]  --联合查询
	[WHERE...]       --指定结果需要满足的条件
	[GROUP BY...]    --指定结果需要按照哪几个字段来分组
	[HAVING...]      --过滤分组的记录必须满足的次要条件
	[ORDER BY ...]   --指定查询记录按一个或多个条件排序
	[LIMIT {[offset,]row_count | row_countOFFSET offset}];  --指定查询的记录从哪条至哪条

--[]可选,{}必选

事务

共同完成一个功能的几个方法的集合

维基百科:
ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)。

在数据库系统中,一个事务是指:由一系列数据库操作组成的一个完整的逻辑过程。例如银行转帐,从原账户扣除金额,以及向目标账户添加金额,这两个数据库操作的总和,构成一个完整的逻辑过程,不可拆分。这个过程被称为一个事务,具有ACID特性。

事物原则

Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。[1]

Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。[1]

Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。[1]

Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。[1]

并发下事务会产生的问题

举个例子,事务A和事务B操纵的是同一个资源,事务A有若干个子事务,事务B也有若干个子事务,事务A和事务B在高并发的情况下,会出现各种各样的问题。“各种各样的问题”,总结一下主要就是五种:第一类丢失更新、第二类丢失更新、脏读、不可重复读、幻读。五种之中,第一类丢失更新、第二类丢失更新不重要,不讲了,讲一下脏读、不可重复读和幻读。

1、脏读

所谓脏读,就是指事务A读到了事务B还没有提交的数据,比如银行取钱,事务A开启事务,此时切换到事务B,事务B开启事务–>取走100元,此时切换回事务A,事务A读取的肯定是数据库里面的原始数据,因为事务B取走了100块钱,并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读。

2、不可重复读

所谓不可重复读,就是指在一个事务里面读取了两次某个数据,读出来的数据不一致。还是以银行取钱为例,事务A开启事务–>查出银行卡余额为1000元,此时切换到事务B事务B开启事务–>事务B取走100元–>提交,数据库里面余额变为900元,此时切换回事务A,事务A再查一次查出账户余额为900元,这样对事务A而言,在同一个事务内两次读取账户余额数据不一致,这就是不可重复读。

3、幻读

所谓幻读,就是指在一个事务里面的操作中发现了未被操作的数据。比如学生信息,事务A开启事务–>修改所有学生当天签到状况为false,此时切换到事务B,事务B开启事务–>事务B插入了一条学生数据,此时切换回事务A,事务A提交的时候发现了一条自己没有修改过的数据,这就是幻读,就好像发生了幻觉一样。幻读出现的前提是并发的事务中有事务发生了插入、删除操作。

事务隔离级别

事务隔离级别,就是为了解决上面几种问题而诞生的。为什么要有事务隔离级别,因为事务隔离级别越高,在并发下会产生的问题就越少,但同时付出的性能消耗也将越大,因此很多时候必须在并发性和性能之间做一个权衡。所以设立了几种事务隔离级别,以便让不同的项目可以根据自己项目的并发情况选择合适的事务隔离级别,对于在事务隔离级别之外会产生的并发问题,在代码中做补偿。

事务隔离级别有4种,但是像Spring会提供给用户5种,来看一下:

1、DEFAULT

默认隔离级别,每种数据库支持的事务隔离级别不一样,如果Spring配置事务时将isolation设置为这个值的话,那么将使用底层数据库的默认事务隔离级别。顺便说一句,如果使用的MySQL,可以使用"select @@tx_isolation"来查看默认的事务隔离级别

2、READ_UNCOMMITTED

读未提交,即能够读取到没有被提交的数据,所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种,因此很少使用

3、READ_COMMITED

读已提交,即能够读到那些已经提交的数据,自然能够防止脏读,但是无法限制不可重复读和幻读

4、REPEATABLE_READ

重复读取,即在数据读出来之后加锁,类似"select * from XXX for update",明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。REPEATABLE_READ的意思也类似,读取了一条数据,这个事务不结束,别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决

5、SERLALIZABLE

串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了

网上专门有图用表格的形式列出了事务隔离级别解决的并发问题:
在这里插入图片描述

事务操作

mysql默认开启事务自动提交

SET autocommit =0   --关闭事务自动提交      

START TRANSACTION   --标记一个事务的开始,从这个之后的sql都在同一个事务内

INSERT xx
INSERT XX

COMMIT              --提交,持久化成功!
ROLLBACK            --不成功,事务回滚

SET autocommit =1   --开启事务自动提交

SAVEPOINT           --中间还可以有保存点,还可以让事务回滚到保存点,删除保存点。
ROLLBACK SAVEPOINT  --回滚到保存点
RELEASE SAVEPOINT   --删除保存点 

索引

索引分类

  1. 主键索引(PRIMARY KEY):唯一的标识,主键不可重复,只能有一个列作为主键。
  2. 唯一索引(UNIQUE KEY):避免重复的列出现,唯一索引可以重复,多个列都可以标识为唯一索引。
  3. 常规索引(key):默认的,可以用index关键字和key来设置
  4. 全文索引(FULLTEXT):在特定的数据库引擎下才有,快速定位数据
SHOW INDEX FROM STUDENT --显示所有索引  --    索引名        列名
ALTER TABLE shool.studnet ADD FULLTEXT INDEX studentname (studentname)   --添加一个索引
EXPLAIN SELECT * FROM student;   --分析sql执行的状况
CREATE INDEX id_app_user_name ON app_user('name');


索引原则

1.最适合索引的列是出现在 WHERE子句中的列,或连接子句中指定的列,而不是出现在 SELECT 关键字后的选择列表中的列。
使用惟一索引。考虑某列中值的分布。索引的列的基数越大,索引的效果越好。

例如,存放出生日期的列具有不同值,很容易区分各行。而用来记录性别的列,只含有“ M”
和“F”,则对此列进行索引没有多大用处,因为不管搜索哪个值,都会得出大约一半的行。

2.如果对字符串列进行索引,应该指定一个前缀长度,只要有可能就应该这样做。

例如,如果有一个 CHAR(200)列,如果在前 10 个或 20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。对前 10 个或 20 个字符进行索引能够节省大量索引空间,也可能会使查询更快。较小的索引涉及的磁盘 IO 较少,较短的值比较起来更快。更为重要的是,对于较短的键值,索引高速缓存中的块能容纳更多的键值,因此,MySQL 也可以在内存中容纳更多的值。这样就增加了找到行而不用读取索引中较多块的可能性。

3.利用最左前缀。

在创建一个 n 列的索引时,实际是创建了 MySQL 可利用的 n 个索引。多列索引可起几个索引的作用,因为可利用索引中最左边的列集来匹配行。这样的列集称为最左前缀。

4.不要过度索引。

不要以为索引“越多越好”,什么东西都用索引是错误的。每个额外的索引都要占用额外的磁盘空间,并降低写操作的性能。在修改表的内容时,索引必须进行更新,有时可能需要重构,因此,索引越多,所花的时间越长。如果有一个索引很少利用或从不使用,那么会不必要地减缓表的修改速度。此外,MySQL 在生成一个执行计划时,要考虑各个索引,这也要花费时间。创建多余的索引给查询优化带来了更多的工作。索引太多,也可能会使 MySQL 选择不到所要使用的最好索引。只保持所需的索引有利于查询优化。

5.对于 InnoDB 存储引擎的表,记录默认会按照一定的顺序保存,

如果有明确定义的主键,则按照主键顺序保存。

如果没有主键,但是有唯一索引,那么就是按照唯一索引的顺序保存。
如果既没有主键又没有唯一索引,那么表中会自动生成一个内部列,按照这个列的顺序保存。
按照主键或者内部列进行的访问是最快的,所以 InnoDB 表尽量自己指定主键,当表中同时有几个列都是唯一的,都可以作为主键的时候,
要选择最常作为访问条件的列作为主键,提高查询的效率。

另外,还需要注意,InnoDB 表的普通索引都会保存主键的键值,所以主键要尽可能选择较短的数据类型,可以有效地减少索引的磁盘占用,提高索引的缓存效果。

权限

1、创建用户

create user ‘test’@’%’ identified by ‘123456’; # 创建用户test,密码123456,%表示允许在所有主机登陆

2、修改密码

alter user ‘test’@’%’ identified by ‘654321’; # 修改密码为654321

3、删除用户

drop user ‘test’@’%’;

4、分配权限

grant语句会做并集处理,而且只能分配当前操作用户所拥有的权限。

1)对 test’@’%’ 用户分配所有库的全部表的全部操作权限,.:第一个表示全部库,第二个表示库下的所有表

grant all on . to ‘test’@’%’;
grant all on . to ‘test’@’%’ with grant option; // with grant option,权限可继续分配
2)分配具体库的全部表权限:

grant all on db_test.* to ‘test’@’%’;
3)只分配具体表的权限:

grant all on db_test.t_test to ‘test’@’%’;
4)指定具体操作权限:

grant select,insert,update,delete on db_test.* to ‘test’@’%’; // 分配db_test库的增删改查权限
操作权限可通过show privileges 查询:

show privileges;

5、查询权限

show grants for ‘test’@’%’;

6、收回权限。收回权限与分配权限语法一致,只是关键字变成了revoke。

revoke all on . from ‘test’@’%’;

7、刷新操作权限。在分配或收回权限后记得要执行该语句。

flush privileges;

三大范式

第一范式:确保每列的原子性.

如果每列(或者每个属性)都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式.
例如:顾客表(姓名、编号、地址、……)其中"地址"列还可以细分为国家、省、市、区等。

第二范式:在第一范式的基础上更进一层,目标是确保表中的每列都和主键相关.

如果一个关系满足第一范式,并且除了主键以外的其它列,都依赖于该主键,则满足第二范式.
例如:订单表(订单编号、产品编号、定购日期、价格、……),"订单编号"为主键,"产品编号"和主键列没有直接的关系,即"产品编号"列不依赖于主键列,应删除该列。

第三范式:在第二范式的基础上更进一层,目标是确保每列都和主键列直接相关,而不是间接相关.

如果一个关系满足第二范式,并且除了主键以外的其它列都不依赖于主键列,则满足第三范式.
为了理解第三范式,需要根据Armstrong公里之一定义传递依赖。假设A、B和C是关系R的三个属性,如果A-〉B且B-〉C,则从这些函数依赖中,可以得出A-〉C,如上所述,依赖A-〉C是传递依赖。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值