目录
1 SQL的四个部分
1.1 数据定义语言
1.2 数据操作语言
1.3 数据查询语言
1.4 数据控制语言
2 数据库的基本操作
2.1 建库——数据库名,字符集,校对规则
2.2 改库——数据库名,字符集,校对规则
2.3 删库——数据库名
2.4 查库——show
2.5 选库——USE
2 数据表的基本操作
2.1 建表——表名,表定义
2.2 修改数据表
2.2.1 添加字段
2.3 删除数据表
2.3.1 删除被其它表关联的主表
2.4 查看表结构
2.5 增删改查
2.5.1 插入数据——INSERT
2.5.2 删除数据——DELETE
2.5.3 查询数据——SELECT
2.5.4 修改数据——UPDATE
2.6 连接——结合多张表的列
2.6.1 内连接
2.6.2 交叉连接——组合的结果
2.6.3 外连接
2.6.4 USING
2.6.5 自然连接
2.6.6 总结
2.7 联合——结合多张表的行
2.8 恢复数据库
3 视图
3.1 创建视图
3.2 更改或删除视图
3.3 更新视图
3.3.1 WITH CHECK OPTION
3.4 视图的优点
4 函数
4.1 MySQL内置函数
4.1.1 聚合函数
4.1.2 数值函数
4.1.3 字符串函数
4.1.4 日期函数
4.1.5 IFNULL和COALESCE
4.1.6 IF函数
4.1.7 CASE运算符
4.1.8 IF ... THEN ... ELSE ... END IF
4.2 自定义函数
4.2.1 变量
4.2.2 用户自定义函数
5 存储过程
5.1 创建存储过程
5.1.1 DELIMITER
5.1.3 输入参数
5.1.4 参数默认值
5.1.5 错误参数处理——SIGNAL
5.1.6 输出参数
5.2 删除存储过程
6 触发器和事件
6.1 触发器
6.1.1 创建触发器
6.1.2 查看触发器
6.1.3 删除触发器
6.1.4 使用触发器做审计
6.2 事件
6.2.1 开启事件
6.2.2 创建事件
6.2.3 查看事件
6.2.4 修改事件
6.2.5 删除事件
7 事务-Transaction
7.1 ACID属性
7.2 创建事务
7.3 并发和锁定
7.3.1 并发问题
7.3.2 隔离级别
7.4 死锁
9 高效的索引
9.1 创建索引
9.1.1 查看索引
9.2 索引的种类
9.2.1 聚集索引
9.2.2 前缀索引
9.2.3 全文索引
9.2.4 复合索引
9.2.5 使用索引排序
9.2.6 覆盖索引
9.2.7 维护索引
10 设计数据库
10.1 数据建模
10.1.1 概念模型
10.1.2 逻辑模型
10.1.3 物理模型
10.2 主键和外键
10.2.1 外键约束
10.2.2 链接表
10.3 三大范式
10.3.1 单一值
10.3.2 单一目的
10.3.3 列间无依赖
11 权限管理
11.1 用户与密码
11.1.1创建用户
11.1.2 查看用户
11.1.3 删除用户
11.1.4 修改密码
11.2 权限
11.2.1 授予权限
11.2.2 查看权限
11.2.3 删除用户权限
12 备份
导出数据
恢复数据
sql注释
#,注释单行
-- ,--+空格,注释单行
/* */,注释多行
查看帮助命令HELP可以用来查看相关命令
HELP 查询内容
查询内容为要查询的关键字。
- 查询内容中不区分大小写。
- 查询内容中可以包含通配符“%”和“_”,效果与 LIKE 运算符执行的模式匹配操作含义相同。例如,
HELP 'rep%'
用来返回以 rep 开头的主题列表。- 查询内容可以使单引号引起来,也可以不使用单引号,为避免歧义,最好使用单引号引起来。
用来创建或删除数据库以及表等对象,主要包含以下几种命令:
- DROP:删除数据库和表等对象
- CREATE:创建数据库和表等对象
- ALTER:修改数据库和表等对象的结构
创建(create),删除(drop),更改(alter)
用来变更表中的记录,主要包含以下几种命令:
- SELECT:查询表中的数据
- INSERT:向表中插入新数据
- UPDATE:更新表中的数据
- DELETE:删除表中的数据
增(insert),删(delete),改(update),查(select)
用来查询表中的记录,主要包含 SELECT 命令,来查询表中的数据。
用来确认或者取消对数据库中的数据进行的变更。
除此之外,还可以对数据库中的用户设定权限。主要包含以下几种命令:
- GRANT:赋予用户操作权限
- REVOKE:取消用户的操作权限
- COMMIT:确认对数据库中的数据进行的变更
- ROLLBACK:取消对数据库中的数据进行的变更
建库,查看库,使用库,修改库,删除库
CREATE DATABASE [IF NOT EXISTS] <数据库名>
[[DEFAULT] CHARACTER SET <字符集名>]
[[DEFAULT] COLLATE <校对规则名>];
[ ]
中的内容是可选的。语法说明如下:
- <数据库名>:创建数据库的名称。命名符合操作系统文件夹命名的要求。
- IF NOT EXISTS:建库前的判断,存在则不创建。
- [DEFAULT] CHARACTER SET:指定数据库的字符集。指定字符集的目的是为了避免在数据库中存储的数据出现乱码的情况。如果在创建数据库时不指定字符集,那么就使用系统的默认字符集。
- [DEFAULT] COLLATE:指定字符集的默认校对规则。
建完库之后,实际上我们所在的数据库并不是刚建好的库,要使用use databasename,来切换数据库
ALTER DATABASE [数据库名] {
[ DEFAULT ] CHARACTER SET <字符集名> |
[ DEFAULT ] COLLATE <校对规则名>}
语法说明如下:
- ALTER DATABASE 用于更改数据库的全局特性。
- 使用 ALTER DATABASE 需要获得数据库 ALTER 权限。
- 数据库名称可以忽略,此时语句对应于默认数据库。
- CHARACTER SET 子句用于更改默认的数据库字符集。
DROP DATABASE [ IF EXISTS ] <数据库名>
语法说明如下:
- <数据库名>:指定要删除的数据库名。
- IF EXISTS:用于防止当数据库不存在时发生错误。
- DROP DATABASE:删除数据库中的所有表格并同时删除数据库。使用此语句时要非常小心,以免错误删除。如果要使用 DROP DATABASE,需要获得数据库 DROP 权限。
SHOW DATABASES [LIKE '数据库名'];
查看或显示当前用户权限范围以内的数据库。
语法说明如下:
- LIKE 从句是可选项,用于匹配指定的数据库名称。LIKE 从句可以部分匹配,也可以完全匹配。用于模式匹配——正则表达式
- 数据库名由单引号
' '
包围。
USE <数据库名>
USE 语句用来完成一个数据库到另一个数据库的跳转
用 CREATE DATABASE 语句创建数据库之后,该数据库不会自动成为当前数据库,需要用 USE 来指定当前数据库。
CREATE TABLE <表名> ([表定义选项])[表选项][分区选项];
[表定义选项]:
<列名1> <类型1> [,…] <列名n> <类型n>
PS:
(1)表名不能使用关键字
(2)列之间用“ , ”隔开
示例
CREATE TABLE IF NOT EXISTS `runoob_tbl`( `runoob_id` INT UNSIGNED AUTO_INCREMENT, `runoob_title` VARCHAR(100) NOT NULL, `runoob_author` VARCHAR(40) NOT NULL, `submission_date` DATE, PRIMARY KEY ( `runoob_id` ) )ENGINE=InnoDB DEFAULT CHARSET=utf8;
实例解析:
- 如果你不想字段为 NULL 可以设置字段的属性为 NOT NULL, 在操作数据库时如果输入该字段的数据为NULL ,就会报错。
- AUTO_INCREMENT定义列为自增的属性,一般用于主键,数值会自动加1。这样在插入数据时,可以设置该字段为——DEFAULT
- PRIMARY KEY关键字用于定义列为主键。 您可以使用多列来定义主键,列间以逗号分隔。
- ENGINE 设置存储引擎,CHARSET 设置编码。
2.1.1 表选项
2.1.2 分区选项
ALTER TABLE <表名> [修改选项]
修改选项的语法格式如下
{ ADD COLUMN <列名> <类型>
| CHANGE COLUMN <旧列名> <新列名> <新列类型> #修改字段名称
| ALTER COLUMN <列名> { SET DEFAULT <默认值> | DROP DEFAULT }
| MODIFY COLUMN <列名> <类型> #修改字段数据类型
| DROP COLUMN <列名> #删除字段
| RENAME TO <新表名> #重命名表
| CHARACTER SET <字符集名>
| COLLATE <校对规则名> }
在末尾添加字段
ALTER TABLE <表名> ADD <新字段名><数据类型>[约束条件];
在开头添加字段
ALTER TABLE <表名> ADD <新字段名> <数据类型> [约束条件] FIRST;
在中间添加字段
ALTER TABLE <表名> ADD <新字段名> <数据类型> [约束条件] AFTER <已经存在的字段名>;
DROP TABLE [IF EXISTS] 表名1 [ ,表名2, 表名3 ...]
对语法格式的说明如下:
表名1, 表名2, 表名3 ...
表示要被删除的数据表的名称。DROP TABLE 可以同时删除多个表,只要将表名依次写在后面,相互之间用逗号隔开即可。- IF EXISTS 用于在删除数据表之前判断该表是否存在。如果不加 IF EXISTS,当数据表不存在时 MySQL 将提示错误,中断 SQL 语句的执行;加上 IF EXISTS 后,当数据表不存在时 SQL 语句可以顺利执行,但是会发出警告(warning)
删除父表有以下两种方法:
- 先删除与它关联的子表,再删除父表;但是这样会同时删除两个表中的数据。
- 将关联表的外键约束取消,再删除父表;适用于需要保留子表的数据,只删除父表的情况。
使用 SHOW CREATE TABLE 命令查看表的外键约束
删除外键约束
ALTER TABLE tb_emp5 DROP FOREIGN KEY fk_emp4_emp5
DESCRIBE <表名> 或 DESC <表名>
SHOW CREATE TABLE <表名>
INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN );
当插入一整行所有数据时,可以省略字段的选择
1、把表1的数据插入到表2
insert into table2 ( field1, field2,...fieldN ) select ( field1, field2,...fieldN ) from table1;
由于values需要把每个数据使用“ 逗号 ”分隔开,这里不需要使用values。
2、往多张表中插入数据
有的时候,表与表之间存在联系,往一张表中插入数据时,另一张表也要更细,此时就需要一个字段来联系这两张表,同时需要函数来获取新插入的该字段的值。
复制一张表
create table table1 (field1, field2, ... , fieldN) as select (field1, field2, ... , fieldN) from table2;
DELETE FROM <表名> [WHERE 子句] [ORDER BY 子句] [LIMIT 子句]
SELECT {* | <字段列名>} [ FROM <表 1>, <表 2>… [WHERE <表达式> [GROUP BY
[HAVING [{ }…]] [ORDER BY ] [LIMIT[ ,] ] ]
其中,各条子句的含义如下:
{*|<字段列名>}
包含星号通配符的字段列表,表示所要查询字段的名称。<表 1>,<表 2>…
,表 1 和表 2 表示查询数据的来源,可以是单个或多个。WHERE <表达式>
是可选项,如果选择该项,将限定查询数据必须满足该查询条件。GROUP BY< 字段 >
,该子句告诉 MySQL 如何显示查询出来的数据,并按照指定的字段分组。[ORDER BY< 字段 >]
,该子句告诉 MySQL 按什么样的顺序显示查询出来的数据,可以进行的排序有升序(ASC)和降序(DESC),默认情况下是升序。[LIMIT[
,该子句告诉 MySQL 每次显示查询出来的数据条数。,] ]
实验
1、表结构
2、查询
select * # 通配符,匹配一个或多个字段 from customer # 表名,查询的对象是表 where customer_id >= 1 # 筛选条件 order by first_name # 给查询出来的数据排序
3、数学运算
+,-,*,/,%
4、别名——as
5、字段值互异常的记录——distinct
distinct作用的对象是后面一组待查询的字段组成的整体。
where
1、比较运算
>、>=、<、<=、=、!=、<>(不等于)
用于条件表达式
2、逻辑运算符
AND、OR、
NOT——用户否定一个条件
用于组合多个筛选条件——AND的优先级比OR高,如果表达式中出现AND,则先计算AND的结果
3、IN和NOT IN操作符
IN——判断一个字段的值是否在集合中,集合使用 (.,...) 表示
NOT IN——判断一个字段的值是否不在集合中
4、BETWEEN ... AND ...
将某一属性与一个范围值相比较
5、LIKE——字符串模式匹配
匹配一组字符串模式——%,匹配任意数量的字符;_,匹配任意一个字符
从结果来看,日期也可以使用通配符
6、REGEXP——正则表达式
正则表达式在搜索字符串时极其强大
^——表示字符串的开头,在字符串前加上“ ^ ”,表示必须以该字符串开头
$——表示结尾,即以该字符串结尾
|——或运算符,搜索多个字符模式
|运算符与模式之间不能有空格
[]——表示一个集合,匹配集合中的任意一个字符
在[]中,可以使用“ - ”来表示一个范围——[a-f]
7、NULL
如何搜索缺失了属性的记录
IS NULL——判断字段值是否为空
IS NOT NULL——判断字段值是否不为空
order by
限定查询结果的排序方式
desc——以降序排列
可以按照表中的任意支持排序的字段进行排序
LIMIT——限定查询返回的记录
给数据分页——比如每三个数据为一页,想查询第三页的内容,则可以使用偏移量和记录总数来限定。
偏移量2,数量3:
group by
按照字段分组,放在order by之前。
每一组都是唯一的。
having
在分组后筛选数据,即用于group by之后,where可以用于在group by之前筛选数据。
having中使用的字段必须的是前面的select中存在的字段。并不是表中的任何字段都可以
with rollup
应用于聚合值的列(对非聚合值的列没有影响),把列中所有的值累加起来。
子查询
select语句返回集合时,这个集合对象可以用于任何需要集合的SQL语句中
ALL
ALL关键字指示一个集合中的所有元素。当存在一个数学不等式运算符时,ALL意味着与集合中的所有元素进行比较。
但是能用ALL的查询,都可以用聚合函数代替
SOME
集合中的任一值,或某些值
ANY
集合中的任一要给
= ANY 和 IN 运算符等效
相关子查询
子查询和外查询存在相关性——比如在子查询中需要用到外查询的表。其实这其中存在一个作用域的问题,我们可以在子查询中访问外查询中的对象,但是不能在外查询中访问子查询中的对象。
但是为了避免同名变量覆盖——比如子查询中的同名变量覆盖了外部查询的同名变量,此时可以把外部的变量起一个别名
EXISTS
判断结合中是否存在符合条件的值,存在则返回TRUE。此时EXISTS并不会把子查询的结果返回给外查询,而是返回一个布尔值,而这个值会附加到查询记录上。
对于IN而言,它会返回子查询的集合给外查询,如果子查询很大的时候,效率是非常低的。
UPDATE <表名> SET 字段 1=值 1 [,字段 2=值 2… ] [WHERE 子句 ] [ORDER BY 子句] [LIMIT 子句]
使用子查询
SELECT返回的查询字段的集合,该集合可以作为IN的查询集合,以及条件查询的集合
如何从多张表格中选择列?
ON子句中只能使用‘ = '运算符吗?——不是
连接,可以结合多张表的列
内连接的查询结果都是符合连接条件的记录
内连接(INNER JOIN)主要通过设置连接条件的方式,来移除查询结果中某些数据行的交叉连接。简单来说,就是利用条件表达式来消除交叉连接的某些数据行
SELECT <字段名> FROM <表1> INNER JOIN <表2> [ON子句] #二者是等价的 SELECT <字段名> FROM <表1>, <表2> WHERE [条件]
语法说明如下。
- 字段名:需要查询的字段名称。
- <表1><表2>:需要内连接的表名。
- INNER JOIN :内连接中可以省略 INNER 关键字,只用关键字 JOIN。
- ON 子句:用来设置内连接的连接条件。
连接的条件是二者字段的连接
INNER JOIN——连接多张表
使用ON关键字来限定连接的方式或者说桥梁
查询结果将在这张连接后的大表中查找
别名的使用
这里不用使用as,直接在表后面跟别名。
连接分散在多个数据库的表
在引用其他数据库表的时候,需要使用数据库作为作用域,使用 “ . ”运算符作为
自连接
使用不同的别名来指示同一个表
多表连接
连接的对象依然是from后面的主表,其他表join到主表,每连接的表与join一一对应
复合主键的表的连接
在ON子句中可以使用AND关键字连连接各个联合主键的元素的条件判断
笛卡尔积
A集合中的每个元素与B集合中的每个元素组合成新的集合
A = {1,2}
B = {3,4,5}A×B={(1,3), (1,4), (1,5), (2,3), (2,4), (2,5) };
B×A={(3,1), (3,2), (4,1), (4,2), (5,1), (5,2) };
交叉连接——返回连接表的笛卡尔积
SELECT <字段名> FROM <表1> CROSS JOIN <表2> [WHERE子句] #
内连接的查询结果都是符合连接条件的记录
外连接会先将连接的表分为基表和参考表,再以基表为依据返回满足和不满足条件的记录。
左连接——以左表为基表,返回左表中与参考表匹配和不匹配的记录
SELECT <字段名> FROM <表1> LEFT OUTER JOIN <表2>
customer中新插入‘ 6 ’记录
右连接——以右表为基表,返回右表中与参考表匹配和不匹配的记录
SELECT <字段名> FROM <表1> RIGHT OUTER JOIN <表2>
在custmoer_cost表中增加’ 6 ‘ 和 ’ 7 ‘元素。这里我用了’ > ‘号运算符。明显可以看出,最终的结果是将表1中的每一条与表2中的每一条进行比较。满足的则显示,表1无但表2有的也要显示
多表外连接
有多少需要连接的表,就需要多少JOIN(LEFT JOIN OR RIGHT JOIN)
自_外连接
即同一张表,自己连接到自己。需要别名
连接的‘ [ON子句] ’,条件往往比较复杂,可以使用USING关键字来判断字段是否相等
USING中不同的字段可以使用 ‘ , ’ 隔开。使用USING的前提是表1和表2具有相同字段
NATURAL JOIN——数据库引擎会基于共同的列连接。即无需指明连接的列了(无需ON子句或USING子句)。
但是不建议使用
从实验结果来看是一种内连接
连接的方式——ON子句
从交叉连接中,寻找一些符合条件的记录——外连接和内连接均是如此
无论什么连接方式都是交叉连接(无条件),然后经过ON子句或USING子句或WHERE子句筛选出来的结果
不加筛选条件,内连接就是交叉连接
外连接需要筛选条件,否则报错
UNION——合并两次查询的行结果
对于两次查询,二者之间使用UNION连接,而不是使用结束符——“ ; ”
PS——查询的列的数量要一致
对数据做了更删改查后
在目录下找到create-database.sql,执行。即数据库恢复如初
MySQL 视图(View)是一种虚拟存在的表,同真实表一样,视图也由列和行构成,但视图并不实际存在于数据库中。
视图不存储数据——视图就是从已知表结构中映射出来的新的表结构,且新表不存储数据,其数据存储在被映射的表中。
所有视图的定义都存储在information_schema数据库的views表中。
创建视图是指在已经存在的 MySQL 数据库表上建立视图。视图可以建立在一张表中,也可以建立在多张表中。
CREATE VIEW <视图名> AS
测试:
#更改视图 ALTER VIEW <视图名> AS
某些视图是可更新的。也就是说,可以使用 UPDATE、DELETE 或 INSERT 等语句更新基本表的内容。
可更新的视图,视图中的行和基本表的行之间必须具有一对一的关系。
如果视图包含以下结构中的任何一种,它就是不可更新的:
- 聚合函数 SUM()、MIN()、MAX()、COUNT() 等。
- DISTINCT 关键字。
- GROUP BY 子句。
- HAVING 子句。
- UNION 或 UNION ALL 运算符。
- 位于选择列表中的子查询。
- FROM 子句中的不可更新视图或包含多个表。
- WHERE 子句中的子查询,引用 FROM 子句中的表。
- ALGORITHM 选项为 TEMPTABLE(使用临时表总会使视图成为不可更新的)的时候。
WITH CHECK OPTION
当你更新视图可能会导致源表中的数据被删除时,在创建视图的最后可以使用该选项,这样在更新视图时会报错。
1、复用代码
2、应对数据库表结构的变化——视图提供了一种数据库表的抽象,减少了变动带来的影响
3、共享数据
4、加强数据安全
函数——一对可以反复使用的代码
从一系列值中,计算得到一个单一值
1、数学计算
MAX(),MIN(),AVG(),SUM(),COUNT()
1、处理数值
ROUND(V, P)——四舍五入,V表示值,P表示精度(几位小数)
TRUNCATE(V, P)——用来截断数字,V表示值,P表示精度(几位效数)
CEILING(V)——返回大于等于这个数字的最小整数
FLOOR(V)——返回小于等于这个数字的最大整数
ABS(V)——返回绝对值
RAND()——生成0-1之间的随机浮点数
LENGTH()——返回长度
UPPER()——大写
LOWER()——小写
LTRIM()——left trim(左修整),移除字符串左边的空白字符或其他预定义字符
RTRIM()——right trim(右修整),移除字符串右边的空白字符或预定义字符
TRIM()——删除前导或尾随空格
LEFT(Str, Dist)——返回左侧的几个字符
SUBSTRING(Str, Pos, Len)——返回从Pos开始的,Len个字符(或者说这个长度的字符)
LOCATE(SubStr, DstStr)——返回第一个字符或一串字符在目标字符串中匹配位置
REPLACE(SrcStr, SubStr, DstStr)——把SrcStr中的SubStr,替换成DisStr
CONCAT(firstStr, nextStr)——连接两个字符串
IFNULL(Src,Dst)——如果Src为空,则返回Dst
COALESCE(Src, Dst, Rev)——Src为空,则用Dst;Dst为空,则返回Rev
IF(expression, first, second)——如果expression为TRUE,则返回first;否则返回second
类似条件运算符—— ? :
CASE
WHEN ... THEN ...
WHEN ... THEN ...
ELSE
END
IF <条件>
THEN
... ELSE
... END IF
SET
定义用户变量
SET @<变量名>
变量在会话过程中保存,断开时清空,所以又叫做”用户变量或会话变量“
DECLARE
声明本地变量,本地变量在存储过程结束时清空
DECLARE value DECIMAL(9, 2) DEFAULT 0;
函数只能返回单一值,无法返回拥有多行和多列的结果集。(存储过程可以)
CREATE FUNCTION sp_name ([func_parameter[...]]) RETURNS type [characteristic ...] routine_body
其中:
- sp_name 参数:表示存储函数的名称;
- func_parameter:表示存储函数的参数列表;——每个参数由名称和类型组成[IN | OUT | INOUT] param_name type;
- RETURNS type:指定返回值的类型;
- characteristic 参数:指定存储函数的特性,该参数的取值与存储过程是一样的;
- routine_body 参数:表示 SQL 代码的内容,可以用 BEGIN...END 来标示 SQL 代码的开始和结束。
DETERMINISTIC(确定性)
指明函数具有确定性,同一组数据返回同一个值
READS SQL DATA
读取SQL数据
MODIFIES SQL DATA
修改SQL数据
PS:这些关键字用在BEGIN之前
RETURN
返回值
如果在编程语言中中编写查询语句,会使得代码的可读性变差,且不易维护;当你修改了一个SQL查询语句时,就必须重新编译代码才能生效。
存储过程是一个包含一堆SQL代码的数据库对象。
使用存储过程的目的是将常用或复杂的工作预先用 SQL 语句写好并用一个指定名称存储起来,这个过程经编译和优化后存储在数据库服务器中,因此称为存储过程。
存储过程的信息都存储在information_schema数据库下的Routines
CREATE PROCEDURE <过程名> ( [过程参数[,…] ] ) <过程体> [过程参数[,…] ] 格式 [ IN | OUT | INOUT ] <参数名> <类型> CREATE PROCEDURE <过程名> (参数...) BEGIN ... END
调用存储过程
CALL <过程名>
在 MySQL 中,服务器处理 SQL 语句默认是以分号作为语句结束标志的。
然而,在创建存储过程时,存储过程体可能包含有多条 SQL 语句,这些 SQL 语句如果仍以分号作为语句结束符,那么 MySQL 服务器在处理时会以遇到的第一条 SQL 语句结尾处的分号作为整个程序的结束符,而不再去处理存储过程体中后面的 SQL 语句,这样显然不行。
使用 DELIMITER 命令将结束命令修改为其他字符
使用方法——首先使用DELIMITER声明结束符,然后在END的结尾处使用该字符。
参数的声明和编程中的函数类似
参数的声明与表结构的声明类似
示例
示例1:
SIGNAL类似于编程中的异常处理
SIGNAL SQLSTATE <错误码(可以是字符串或字符串形式的错误码)> SET MESSAGE_TEXT = <字符串>; ##SIGNAL SQLSTATE 和 MESSAGE_TEXT在同一个执行语句中。当然可以不要MESSAGE_TEXT
默认情况下,存储过程的所有参数都是输入参数
OUT
在参数前使用OUT关键字,声明参数是输出参数
INTO
INTO关键字可以把SELECT查询出来的结果放在变量中——SELECT
INTO 变量
使用 SET @<变量名> 声明一个变量来存储输出参数
DROP PROCEDURE [ IF EXISTS ] <过程名>
在删除之前,必须确认该存储过程没有任何依赖关系,否则会导致其他与之关联的存储过程无法运行。
触发器是在插入、更新和删除语句前后自动执行的一堆SQL代码
通常使用触发器增强数据一致性
MySQL 的触发器和存储过程一样,都是嵌入到 MySQL 中的一段程序,是 MySQL 中管理数据的有力工具。
不同的是执行存储过程要使用 CALL 语句来调用,而触发器的执行不需要使用 CALL 语句来调用,也不需要手工启动,而是通过对数据表的相关操作来触发、激活从而实现执行。
PS:
(1)触发器不能有返回值——有返回值的SQL不能用在触发器
(2)触发器的触发主体不能作用于触发器所在的表——主体不能作用于自己所在的表
CREATE <触发器名> < BEFORE | AFTER >
ON <表名> FOR EACH Row<触发器主体> 如果要使用新插入或更新的数据,使用关键字NEW;若要使用旧的数据使用关键字OLD
FOR EACH ROW:行级触发,对于受触发事件影响的每一行都要激活触发器的动作。——有没有步第每一行都做激活的触发(或者说可以用where的触发)
示例
这里我做了一个触发器,在更新cost的y_cost列时,触发更新value的money列。但是当使用条件更新了cost的y_cost列(即只更新name='Me'的行),把value的每一行都更新了。这不是我想要的结果——或者是触发器不应用于这样的问题上??
SHOW TRIGGERS LIKE <模式串>
DROP TRIGGER IF EXISTS
创建一个表,作为日志表,每发生一个INSERT,UPDATE,DELETE操作,记录操作名称,操作对象,操作者,操作时间等
事件(Event)也可称为事件调度器(Event Scheduler),是用来执行定时任务的一组 SQL 集合,可以通俗理解成 MySQL 中的定时器。一个事件可调用一次,也可周期性的启动。
事件和触发器类似,都是在某些事情发生时启动。当数据库启动一条语句的时候,触发器就启动了,而事件是根据调度事件来启动的。由于他们彼此相似,所以事件也称为临时性触发器。
调度器 event_scheduler 负责调用事件
可以使用 SET GLOBAL 命令设定全局变量 event_scheduler 的值,开启或关闭事件。
CREATE EVENT
ON SCHEDULE
#AT DATE/DATETIME # 只执行一次
EVERY number HOUR/DAY/MONTH/YEAR STARTS DATE/DATETIME ENDS DATE/DATETIME
DO BEGIN
......
END
CREATE EVENT [IF NOT EXISTS] event_name ON SCHEDULE schedule [ON COMPLETION [NOT] PRESERVE] [ENABLE | DISABLE | DISABLE ON SLAVE] [COMMENT 'comment'] DO event_body;
ON SCHEDULE schedule:必选,用于定义执行的时间和时间间隔,schedule 表示触发点
ON COMPLETION [NOT] PRESERVE:可选,用于定义事件是否循环执行,即是一次执行还是永久执行,默认为一次执行,即 NOT PRESERVE
COMMENT 'comment':可选,用于定义事件的注释
查看事件状态:
3 种方式来查看事件的状态信息:
(1)查看 mysql.event
(2)查看 information_schema.events
(3)切换到相应的数据库后执行 SHOW EVENTS;
ALTER EVENT event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
DO event_body;
DROP EVENT IF EXISTS
事务代表单个工作单元的一组SQL语句,所有语句都应该成功完成,否则事务会运行失败。
默认设置下,每条 SQL 语句就是一个事务,即执行 SQL 语句后自动提交。
为了达到将几个操作做为一个整体的目的,需要使用 BEGIN 或 START TRANSACTION 开启一个事务,或者禁止当前会话的自动提交。
执行事务的语法和流程
(1)开始事务。BEGIN;或START TRANSACTION;
(2)提交事务。COMMIT;
(3)回滚(撤销)事务。ROLLBACK;
注意事项
(1)事务尽可能简短
(2)事务中访问数据量尽量少
(3)查询数据时尽量不要使用事务
(4)事务处理过程中尽量不要出现等待用户输入的操作
Atomicity——原子性。要么都执行成功且事务被提交;要么被退回,所有更改被撤销
Consistency——一致性。数据库始终保持一致的状态
Isolation——隔离性。事务相互隔离,当有同样的数据被更改时给自受到保护。如果多个事务想更新相同的资源,受影响的行会被锁定,因此只有一个事务可以更新行。其他事务必须等待那个事务完成。
Durability——事务一旦被提交,事务产生的更改是永久的。
START TRANSACTION;
# BODY
COMMIT;
START TRANSACTION;
# BODY
ROLLBACK;
在某些情况下,如果想进行一些错误检查,并手动退回事务,可以使用ROLLBACK,这样会退回事务并撤销所有更改
可以看到使用了ROLLBACK,最终数据并没有存入表中
多用户同时访问数据
如何最小化并发问题——当一个用户在修改数据时,另一个可能在修改或检索该数据
丢失更新
1、概念
当两个事务尝试更新相同的数据并且没有上锁时,较晚提交的事务会覆盖较早事务做的更改。
2、解决方法
锁机制,锁住访问的相同数据
脏读
1、概念
一个事务读取了尚未被提交的数据。
2、解决方法
为事务建立隔离级别,这样事务修改的数据不会立马被其他事务读取,触发它提交更新。
3、隔离级别——SQL定义了4个事务隔离级别
READ COMMITTED,读已提交。事务只能读取已提交的数据。
不可重复读(不一致读)
1、概念
同一事务中重复读了数据,却得到不同的结果
2、解决方法
将该事务与其他食物隔离,确保数据更改对事务不可见。
REPEATABLE READ——可重复读。读取的数据是可重复和一致的。就算有其他事务修改了数据,我们会看到首次读取就创建的快照。
幻读
1、概念
数据在执行查询后才添加、更新或删除的——存在先后关系
2、解决方法
SERIALIZABLE——序列化,当有别的事务在更新数据时,我们的事务能够知晓变动,如果有事务修改会影响查询的数据,我们的事务必须等待它们完成
设置隔离级别
SET TRANSACTION ISOLATION LEVEL
不同隔离级别所能解决的问题
隔离级别越高,存在月中的性能和可扩展性问题
READ UNCOMMITTED
(1)设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
事务是支持行锁机的,当它执行某个语句时,会对访问的数据上锁
事务对数据的上锁会持续到事务结束
当死锁发生时,可以报错或回退
索引本质是数据库引擎用来快速查找数据的数据结构
不应该基于表来创建索引,而是基于查询创建索引
在创建二级索引时,MySQL会主动把主键或ID纳入到二级索引中。
而索引会提高查找效率,所以查询时包含主键是优选
EXPLAIN
EXPLAIN关键字会解释查询语句的方式。type列的值为ALL,表示全表查询。
CREATE INDEX <索引名> ON <表名> (<列名> [<长度>] [ ASC | DESC])
<列名>
:指定要创建索引的列名
<长度>
:可选项。指定使用列前的 length 个字符来创建索引。使用列的一部分创建索引有利于减小索引文件的大小,节省索引列所占的空间
ASC|DESC
:可选项。ASC
指定索引按照升序来排列,DESC
指定索引按照降序来排列,默认为ASC
这里构建的索引,以条件查询的age为基础
1、查看索引
SHOW INDEXES IN
MySQL自动为主键建立一个索引,称为聚集索引
对于用户创建的idx_age,则为“二级索引”——一般来说,在创建二级索引时,MySQL会主动把主键或ID纳入到二级索引中。
每当我们为两张表创建一组关系时,MySQL会自动为外键创建索引。
以主键作为索引
指定使用列前的 length 个字符来创建索引。使用列的一部分创建索引有利于减小索引文件的大小,节省索引列所占的空间
CREATE FULLTEXT INDEX
on (columnName,...)
全文索引用于在应用程序中制作快速灵活的搜索引擎
全文索引包括了整个字符串列,而不只是存储前缀。它们会忽略任何停止词,比如“in”,“the”等。
全文搜索有两种模式——自然语言模式(默认模式);布尔模式(这个模式可以包含或排除某些单词)
本质上,它们存储了一套单词列表,对于每个单词,它们又存储了一列这些单词会出现的行或记录。
1、相关性得分
MySQL会基于若干因素,为包含了搜索短语的每一行计算相关性得分。“相关性得分”是一个介于0到1的浮点数,0表示没有相关性。
2、布尔模式
在字符串后面使用——IN BOOLEAN MODE
此时,在字符串中使用“ - ”表示排除某个单词,使用“ + ”表示包含某个单词;使用“”双引号,表示搜索短语。
示例:
1、网站,posts表存储了文章——文章标题,文章内容,创建时间
2、现在要查询“ react redux ”这个字符串
SELECT * FROM posts WHERE title LIKE '%react redux%' OR body LIKE '%react redux%'
这种用查询方式存在的问题:
(1)这些列上没有索引,查询变得很慢
(2)使用前缀索引不能确定一定包含这个字符串
(3)不会返回只有“react”、“redux”的文章
3、创建全文索引
CREATE FULLTEXT INDEX idx_title_body ON posts (title, body)
4、自然语言搜索
SELECT * , MATCH(title, body) AGAINST('react redux') FROM posts WHERE MATCH(title, body) AGAINST('react redux') # MATCH(title, body) AGAINST('react redux')返回相关性得分
5、布尔搜索
SELECT * , MATCH(title, body) AGAINST('react redux') FROM posts WHERE MATCH(title, body) AGAINST('react -redux +form' IN BOOLEAN MODE) # 在redux前加上“-”表示排除“redux”,即只寻找包含“react”的结果 # 使用“+”,则包含某个单词
当查询的条件是复合条件时,可以考虑创建复合索引——即多个列作为一个索引节点
复合索引中列的顺序
(1)让更频繁使用的列排在最前面
(2)把基数更高的列排序在最前面——基数表示索引中唯一值的数量
如果设计好了索引,那么使用索引列来进行排序效率更高
SHOW STATUS LIKE; #可以用来查看服务器使用的变量
有一个叫做last_query_cost的变量,会返回上一次查询的成本
包含所有满足查询需要的数据的索引
通过使用这个索引,MySQL可以在不读取表的情况下就执行查询
当设计索引时,先看WHERE子句,看看最常用的列是否包含在其中,然后再看ORDER BY中的列,最后看看SELECT中的列
删除重复索引
删除多余索引——Index(A, B)和Index(A),可以认为A时多余的,因为Index(A, B)已经可以优化A的查询;但是Index(A, B)和Index(B)不是多余的
良好的数据库设计可以
概念,逻辑,实体
1、理解和分析业务需求——最重要。(人生的扣子从一开始就要扣好)
2、构建概念模型——业务中的实体、事物或概念以及它们之间的关系
3、构建一个逻辑模型或数据结构——逻辑模型是独立于数据技术的抽象数据模型
4、构建一个实体模型——实体模型围绕特定数据库技术的逻辑模型实现。表格设计,主键,以及其他对象(存储过程,视图,触发器等)
1、确定业务都具有哪些概念和实体
2、使用ER图或UML图画出它们之间的关系
1、确定数据的类型(抽象一些即可,比如字符串,时间等,具体的实现由不同的数据库技术而定)
2、抽象实体之间的联系为数据类型(包括自定义的数据类型)
使用具体的数据库实现概念模型
1、主键
唯一标识给定表里每条记录的列——为主键设置自增数据可以方便插入数据且不用担心唯一性
2、外键
两个表之间的联系,是在一张表中引用了另一张表主键的那列。当在在两表之间添加关系时,关系的一端称为父表或主键表,另一端称为子表或外键表。
当主键表发生变化时,外键表也要同步更新;当然也可以选择不更新;
当子表中外键没有对应的父键时,称为孤儿记录
用于实现对对关系而建立的一种表
链接表可以作为表1的主键和表2的主键作为数据成员,将二者作为链接表复合主键
七范式实现标准化数据库
实际记住前三大范式即可,且第一是第二的前提,第二是第三的前提
一行中的每个单元格都应该有单一值,且不能重复出现。——即不能出现重复列
每张表都应该有一个单一目的——每张表应该改只能代表一种且仅有一种实体类型,且属性都是匹配的,不多余的。
有点类似设计模式中的单一职责
同一表中的列不应该派生自其他列
CREATE USER <用户> [ IDENTIFIED BY [ PASSWORD ] 'password' ] [ ,用户 [ IDENTIFIED BY [ PASSWORD ] 'password' ]]
(1)用户
user_name@‘host_name’
host_name为主机名,即用户连接 MySQL 时所用主机的名字。如果在创建的过程中,只给出了用户名,而没指定主机名,那么主机名默认为“%”,表示一组主机,即对所有主机开放权限。
GRANT priv_type ON database.table TO user [IDENTIFIED BY [PASSWORD] 'password']
(1)priv_type 参数表示新用户的权限
(2)database.table 参数表示新用户的权限范围,即只能在指定的数据库和表上使用自己的权限;
SELECT * FROM mysql.user; #在数据库mysql的表user中存放着各个用户
SHOW GRANTS FOR 'username'@'hostname';
DROP USER 'username'@'hostname';
DELETE FROM mysql.user WHERE Host='hostname' AND User='username';
SET PASSWORD FOR username=PASSWORD('password'); #root修改普通用户密码
SET PASSWORD = PASSWORD('password'); #修改当前用户密码,root也可以修改
mysqladmin -u username -h hostname -p password "newpwd" #命令行执行,修改root密码
GRANT <权限>,... ON <表> TO username;
GRANT ALL ON <表> TO username; #ALL,表示所有权限;*.*,表示所有数据库的所有表
SHOW GRANTS FOR username;
REVOKE <权限>,... ON <表> FROM username;
导出是指将 MySQL 数据表的数据复制到文本文件
SELECT 列名 FROM table [WHERE 语句] INTO OUTFILE '目标文件'[OPTIONS]
LOAD DATA INFILE ‘源文件' INTO
#前提是表结构一样