SQL 简介
🔔 本文只针对mysql
数据库术语
数据库(database) - 保存有组织的数据的容器(通常是一个文件或一组文件)。
数据表(table) - 某种特定类型数据的结构化清单。
模式(schema) - 关于数据库和表的布局及特性的信息。模式定义了数据在表中如何存储,包含存储什么样的数据,数据如何分解,各部分信息如何命名等信息。数据库和表都有模式。
行(row) - 表中的一条记录。
列(column) - 表中的一个字段。所有表都是由一个或多个列组成的。
主键(primary key) - 一列(或一组列),其值能够唯一标识表中每一行。
SQL 语法
SQL(Structured Query Language),标准 SQL 由 ANSI 标准委员会管理,从而称为 ANSI SQL。各个 DBMS 都有自己的实现,如 PL/SQL、Transact-SQL 等。
SQL 语法结构
SQL 语法结构包括:
- 子句 - 是语句和查询的组成成分,如:select、from、where、group by等。
- 表达式 - 表达式是能计算得出一个值的代码片段,如:><=、case、avg函数代码片段等。
- 谓词 - 谓词是返回布尔值(TRUE/FALSE/UNKNOWN) 的特殊表达式,主要用于WHERE、HAVING、ON等子句:><=、between、in、like等。
完整的sql语法层次结构:
1 | SQL语句 |
实际应用示例如下:
1 | -- 包含各种表达式和谓词的完整示例 |
SQL 语法要点
SQL 语句不区分大小写,但是数据库表名、列名和值是否区分,依赖于具体的 DBMS 以及配置。
例如:SELECT 与 select 、Select 是相同的。
多条 SQL 语句必须以分号(
;)分隔。处理 SQL 语句时,所有空格都被忽略。SQL 语句可以写成一行,也可以分写为多行。
1 | -- 一行 SQL 语句 |
- SQL 支持三种注释
1 | ## 注释1 |
SQL 分类
DDL
DDL,英文叫做 Data Definition Language,即 “数据定义语言”。DDL 用于定义数据库对象。
DDL 定义操作包括创建(CREATE)、删除(DROP)、修改(ALTER);而被操作的对象包括:数据库、数据表和列、视图、索引。
DML
DML,英文叫做 Data Manipulation Language,即 “数据操作语言”。DML 用于访问数据库的数据。
DML 访问操作包括插入(INSERT)、删除(DELETE)、修改(UPDATE)、查询(SELECT)。这四个指令合称 CRUD,英文单词为 Create, Read, Update, Delete,即增删改查。
TCL
TCL,英文叫做 Transaction Control Language,即 “事务控制语言”。TCL 用于管理数据库中的事务,实际上就是用于管理由 DML 语句所产生的数据变更,它还允许将语句分组为逻辑事务。
TCL 的核心指令是 COMMIT、ROLLBACK。
DCL
DCL,英文叫做 Data Control Language,即 “数据控制语言”。DCL 用于对数据访问权进行控制,它可以控制特定用户账户对数据表、查看表、预存程序、用户自定义函数等数据库对象的控制权。
DCL 的核心指令是 GRANT、REVOKE。
DCL 以控制用户的访问权限为主,因此其指令作法并不复杂,可利用 DCL 控制的权限有:CONNECT、SELECT、INSERT、UPDATE、DELETE、EXECUTE、USAGE、REFERENCES。根据不同的 DBMS 以及不同的安全性实体,其支持的权限控制也有所不同。
数据定义(CREATE、ALTER、DROP)
DDL 的主要功能是定义数据库对象(如:数据库、数据表、视图、索引等)。
数据库(DATABASE)
创建数据库
1 | CREATE DATABASE IF NOT EXISTS db_tutorial; |
删除数据库
1 | DROP DATABASE IF EXISTS db_tutorial; |
选择数据库
1 | USE db_tutorial; |
数据表(TABLE)
创建数据表
普通创建
1 | CREATE TABLE user ( |
根据已有的表创建新表
1 | CREATE TABLE vip_user AS |
查看表结构的定义
1 | desc table |
修改数据表
添加列
1 | ALTER TABLE user |
删除列
1 | ALTER TABLE user |
修改列
1 | ALTER TABLE `user` |
删除数据表
1 | DROP TABLE IF EXISTS user; |
视图(VIEW)
“视图”是基于 SQL 语句的结果集的可视化的表。视图是虚拟的表,本身不存储数据,也就不能对其进行索引操作。对视图的操作和对普通表的操作一样。
视图的作用:
- 简化复杂的 SQL 操作,比如将复杂的连接封装为视图。
- 通过只给用户访问视图的权限,保证数据的安全性。
- 只使用实际表的一部分数据。
- 更改数据格式和表示。
创建视图
1 | CREATE VIEW top_10_user_view AS |
删除视图
1 | DROP VIEW top_10_user_view; |
索引(INDEX)
“索引”是数据库为了提高查找效率的一种数据结构。
索引就像是书籍的目录,它帮助数据库快速找到数据,而不需要逐页扫描整本书。在数据量小且负载较低时,不恰当的索引对于性能的影响可能还不明显;但随着数据量逐渐增大,性能则会急剧下降。因此,设置合理的索引是数据库查询性能优化的最有效手段。
更新一个包含索引的表需要比更新一个没有索引的表花费更多的时间,这是由于索引本身也需要更新。因此,理想的做法是仅仅在常常被搜索的列(以及表)上面创建索引。
“唯一索引”表明此索引的每一个索引值只对应唯一的数据记录。
主键是一种特殊的索引,但并不是所有的索引都是主键。主键 ⊆ 索引(主键是索引的子集)
创建索引
1 | CREATE INDEX idx_email |
创建唯一索引
1 | CREATE UNIQUE INDEX uniq_name |
删除索引
1 | ALTER TABLE user |
添加主键
1 | ALTER TABLE user |
删除主键
1 | ALTER TABLE user |
约束
SQL 约束用于规定表中的数据规则。
如果存在违反约束的数据行为,行为会被约束终止。约束可以在创建表时规定(通过 CREATE TABLE 语句),或者在表创建之后规定(通过 ALTER TABLE 语句)。
约束类型
NOT NULL- 指示字段不能存储NULL值。UNIQUE- 保证字段的每行必须有唯一的值。PRIMARY KEY- PRIMARY KEY 的作用是唯一标识一条记录,不能重复,不能为空,即相当于NOT NULL+UNIQUE。确保字段(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。FOREIGN KEY- 保证一个表中的数据匹配另一个表中的值的参照完整性。CHECK- 用于检查字段取值范围的有效性。DEFAULT- 表明字段的默认值。如果插入数据时,该字段没有赋值,就会被设置为默认值。
创建表时使用约束条件:
1 | CREATE TABLE Users ( |
增删改查(CRUD)
增删改查,又称为 CRUD,是数据库基本操作中的基本操作。
插入数据
INSERT INTO语句用于向表中插入新记录。
插入完整的行
1 | INSERT INTO user |
插入行的一部分
1 | INSERT INTO user(username, password, email) |
插入查询出来的数据
1 | INSERT INTO user(username) |
更新数据
UPDATE语句用于更新表中的记录。
1 | UPDATE user |
删除数据
DELETE语句用于删除表中的记录。TRUNCATE TABLE可以清空表,也就是删除所有行。
删除表中的指定数据
1 | DELETE FROM user WHERE username = 'robot'; |
清空表中的数据
1 | TRUNCATE TABLE user; |
查询数据
SELECT语句用于从数据库中查询数据。DISTINCT用于返回唯一不同的值。它作用于所有列,也就是说所有列的值都相同才算相同。LIMIT限制返回的行数。可以有两个参数,第一个参数为起始行,从 0 开始;第二个参数为返回的总行数。
ASC:升序(默认)DESC:降序
查询单列
1 | SELECT prod_name FROM products; |
查询多列
1 | SELECT prod_id, prod_name, prod_price FROM products; |
查询所有列
1 | SELECT * FROM products; |
查询不同的值
1 | SELECT DISTINCT vend_id FROM products; |
限制查询数量
1 | -- 返回前 5 行 |
过滤数据(WHERE)
子查询是嵌套在较大查询中的 SQL 查询。子查询也称为内部查询或内部选择,而包含子查询的语句也称为外部查询或外部选择。
子查询可以嵌套在
SELECT,INSERT,UPDATE或DELETE语句内或另一个子查询中。子查询通常会在另一个
SELECT语句的WHERE子句中添加。您可以使用比较运算符,如
>,<,或=。比较运算符也可以是多行运算符,如IN,ANY或ALL。子查询必须被圆括号
()括起来。内部查询首先在其父查询之前执行,以便可以将内部查询的结果传递给外部查询。
子查询的子查询
1 | SELECT cust_name, cust_contact |
WHERE 子句
在 SQL 语句中,数据根据 WHERE 子句中指定的搜索条件进行过滤。
WHERE 子句的基本格式如下:
1 | SELECT ……(列名) FROM ……(表名) WHERE ……(子句条件) |
WHERE 子句用于过滤记录,即缩小访问数据的范围。WHERE 后跟一个返回 true 或 false 的条件。
WHERE 可以与 SELECT,UPDATE 和 DELETE 一起使用。
SELECT 语句中的 WHERE 子句
1 | SELECT * FROM Customers |
UPDATE 语句中的 WHERE 子句
1 | UPDATE Customers |
DELETE 语句中的 WHERE 子句
1 | DELETE FROM Customers |
可以在 WHERE 子句中使用的操作符:
比较操作符
| 运算符 | 描述 |
|---|---|
= |
等于 |
<> |
不等于。注释:在 SQL 的一些版本中,该操作符可被写成 != |
> |
大于 |
< |
小于 |
>= |
大于等于 |
<= |
小于等于 |
范围操作符
| 运算符 | 描述 |
|---|---|
BETWEEN |
在某个范围内 |
IN |
指定针对某个列的多个可能值 |
IN操作符在WHERE子句中使用,作用是在指定的几个特定值中任选一个值。BETWEEN操作符在WHERE子句中使用,作用是选取介于某个范围内的值。
IN 示例
1 | SELECT * |
BETWEEN 示例
1 | SELECT * |
逻辑操作符
| 运算符 | 描述 |
|---|---|
AND |
并且(与) |
OR |
或者(或) |
NOT |
否定(非) |
AND、OR、NOT 是用于对过滤条件的逻辑处理指令。
AND优先级高于OR,为了明确处理顺序,可以使用()。AND操作符表示左右条件都要满足。OR操作符表示左右条件满足任意一个即可。NOT操作符用于否定一个条件。
AND 示例
1 | SELECT prod_id, prod_name, prod_price |
OR 示例
1 | SELECT prod_id, prod_name, prod_price |
NOT 示例
1 | SELECT * |
通配符
| 运算符 | 描述 |
|---|---|
LIKE |
搜索某种模式 |
% |
表示任意字符出现任意次数 |
_ |
表示任意字符出现一次 |
[] |
必须匹配指定位置的一个字符 |
LIKE 操作符在 WHERE 子句中使用,作用是确定字符串是否匹配模式。只有字段是文本值时才使用 LIKE。
LIKE 支持以下通配符匹配选项:
%表示任何字符出现任意次数。_表示任何字符出现一次。[]必须匹配指定位置的一个字符。
注意:不要滥用通配符,通配符位于开头处匹配会非常慢。
% 示例:
1 | SELECT prod_id, prod_name, prod_price |
_ 示例:
1 | SELECT prod_id, prod_name, prod_price |
排序和分组
ORDER BY
ORDER BY用于对结果集进行排序。
ORDER BY 有两种排序模式:
ASC:升序(默认)DESC:降序
可以按多个列进行排序,并且为每个列指定不同的排序方式。
指定多个列的排序示例:
1 | SELECT * FROM products |
GROUP BY
GROUP BY子句将记录分组到汇总行中,GROUP BY为每个组返回一个记录。
GROUP BY 可以按一列或多列进行分组。
GROUP BY 通常还涉及聚合函数:COUNT,MAX,SUM,AVG 等。
GROUP BY 按分组字段进行排序后,ORDER BY 可以以汇总字段来进行排序。
分组示例:
1 | SELECT cust_name, COUNT(cust_address) AS addr_num |
分组后排序示例:
1 | SELECT cust_name, COUNT(cust_address) AS addr_num |
HAVING
HAVING用于对汇总的GROUP BY结果进行过滤。HAVING要求存在一个GROUP BY子句。
WHERE 和 HAVING 可以在相同的查询中。
HAVING vs WHERE:
WHERE和HAVING都是用于过滤。HAVING适用于汇总的组记录;而WHERE适用于单个记录。
使用 WHERE 和 HAVING 过滤数据示例:
1 | SELECT cust_name, COUNT(*) AS num |
连接和组合
连接(JOIN)
在 SELECT, UPDATE 和 DELETE 语句中,“连接”可以用于联合多表查询。连接使用 JOIN 关键字,并且条件语句使用 ON 而不是 WHERE。
连接可以替换子查询,并且一般比子查询的效率更快。
JOIN 有以下类型:
- 内连接 - 内连接又称等值连接,用于获取两个表中字段匹配关系的记录,使用
INNER JOIN关键字。在没有条件语句的情况下返回笛卡尔积。- 笛卡尔积 - “笛卡尔积”也称为交叉连接(
CROSS JOIN),它的作用就是可以把任意表进行连接,即使这两张表不相关。 - 自连接(=) - “自连接(=)”可以看成内连接的一种,只是连接的表是自身而已。
- 自然连接(NATURAL JOIN) - “自然连接”会自动连接所有同名列。自然连接使用
NATURAL JOIN关键字。
- 笛卡尔积 - “笛卡尔积”也称为交叉连接(
- 外连接
- 左连接(LEFT JOIN) - “左外连接”会获取左表所有记录,即使右表没有对应匹配的记录。左外连接使用
LEFT JOIN关键字。 - 右连接(RIGHT JOIN) - “右外连接”会获取右表所有记录,即使左表没有对应匹配的记录。右外连接使用
RIGHT JOIN关键字。
- 左连接(LEFT JOIN) - “左外连接”会获取左表所有记录,即使右表没有对应匹配的记录。左外连接使用
内连接(INNER JOIN)
内连接又称等值连接,用于获取两个表中字段匹配关系的记录,使用 INNER JOIN 关键字。在没有条件语句的情况下返回笛卡尔积。
1 | SELECT vend_name, prod_name, prod_price |
笛卡尔积
“笛卡尔积”也称为交叉连接(CROSS JOIN),它的作用就是可以把任意表进行连接,即使这两张表不相关。但通常进行连接还是需要筛选的,因此需要在连接后面加上 WHERE 子句,也就是作为过滤条件对连接数据进行筛选。
笛卡尔积是一个数学运算。假设我有两个集合 X 和 Y,那么 X 和 Y 的笛卡尔积就是 X 和 Y 的所有可能组合,也就是第一个对象来自于 X,第二个对象来自于 Y 的所有可能。
【示例】求 t1 和 t2 两张表的笛卡尔积
1 | -- 以下两条 SQL,执行结果相同 |
自连接(=)
“自连接”可以看成内连接的一种,只是连接的表是自身而已。
1 | SELECT c1.cust_id, c1.cust_name, c1.cust_contact |
自然连接(NATURAL JOIN)
“自然连接”会自动连接所有同名列。自然连接使用 NATURAL JOIN 关键字。
1 | SELECT * |
外连接(OUTER JOIN)
外连接返回一个表中的所有行,并且仅返回来自此表中满足连接条件的那些行,即两个表中的列是相等的。外连接分为左外连接、右外连接、全外连接(Mysql 不支持)。
左连接(LEFT JOIN)
“左外连接”会获取左表所有记录,即使右表没有对应匹配的记录。左外连接使用 LEFT JOIN 关键字。
1 | SELECT customers.cust_id, orders.order_num |
右连接(RIGHT JOIN)
“右外连接”会获取右表所有记录,即使左表没有对应匹配的记录。右外连接使用 RIGHT JOIN 关键字。
1 | SELECT customers.cust_id, orders.order_num |
组合(UNION)
UNION运算符将两个或更多查询的结果组合起来,并生成一个结果集,其中包含来自UNION中参与查询的提取行。
UNION 基本规则:
- 所有查询的列数和列顺序必须相同。
- 每个查询中涉及表的列的数据类型必须相同或兼容。
- 通常返回的列名取自第一个查询。
默认会去除相同行,如果需要保留相同行,使用 UNION ALL。
只能包含一个 ORDER BY 子句,并且必须位于语句的最后。
应用场景:
- 在一个查询中从不同的表返回结构数据。
- 对一个表执行多个查询,按一个查询返回数据。
组合查询示例:
1 | SELECT cust_name, cust_contact, cust_email |
JOIN vs UNION
JOIN中连接表的列可能不同,但在UNION中,所有查询的列数和列顺序必须相同。UNION将查询之后的行放在一起(垂直放置),但JOIN将查询之后的列放在一起(水平放置),即它构成一个笛卡尔积。
关键字
- INTERVAL:语法是
INTERVAL value [day hour minute second],表示时间间隔。
示例:
1 | --- 查找三百天前的数据 |
函数
🔔 注意:不同数据库的函数往往各不相同,因此不可移植。本节主要以 Mysql 的函数为例。
字符串函数
| 函数 | 说明 |
|---|---|
CONCAT() |
合并字符串 |
LEFT()、RIGHT() |
左边或者右边的字符 |
LOWER()、UPPER() |
转换为小写或者大写 |
LTRIM()、RTIM() |
去除左边或者右边的空格 |
LENGTH() |
长度 |
SOUNDEX() |
转换为语音值 |
其中, SOUNDEX() 可以将一个字符串转换为描述其语音表示的字母数字模式。
1 | SELECT * |
时间函数
- 日期格式:
YYYY-MM-DD - 时间格式:
HH:MM:SS
| 函 数 | 说 明 |
|---|---|
ADDDATE() |
增加一个日期(天、周等) |
ADDTIME() |
增加一个时间(时、分等) |
CURRENT_DATE() |
返回当前日期 |
CURRENT_TIME() |
返回当前时间 |
DATE() |
返回日期时间的日期部分 |
DATEDIFF() |
计算两个日期之差 |
DATE_ADD() |
高度灵活的日期运算函数 |
DATE_FORMAT() |
返回一个格式化的日期或时间串 |
DAY() |
返回一个日期的天数部分 |
DAYOFWEEK() |
对于一个日期,返回对应的星期几 |
HOUR() |
返回一个时间的小时部分 |
MINUTE() |
返回一个时间的分钟部分 |
MONTH() |
返回一个日期的月份部分 |
NOW() |
返回当前日期和时间 |
SECOND() |
返回一个时间的秒部分 |
TIME() |
返回一个日期时间的时间部分 |
YEAR() |
返回一个日期的年份部分 |
1 | mysql> SELECT NOW(); |
数学函数
常见 Mysql 数学函数:
| 函数 | 说明 |
|---|---|
ABS() |
取绝对值 |
MOD() |
取余 |
ROUND() |
四舍五入 |
... |
聚合函数
| 函 数 | 说 明 |
|---|---|
AVG() |
返回某列的平均值 |
COUNT() |
返回某列的行数 |
MAX() |
返回某列的最大值 |
MIN() |
返回某列的最小值 |
SUM() |
返回某列值之和 |
AVG() 会忽略 NULL 行。
使用 DISTINCT 可以让汇总函数值汇总不同的值。
1 | SELECT AVG(DISTINCT col1) AS avg_col |
转换函数
| 函 数 | 说 明 | 示例 |
|---|---|---|
CAST() |
转换数据类型 | SELECT CAST("2017-08-29" AS DATE); -> 2017-08-29 |
事务
事务隔离级别
(以下为 DCL 语句用法)
权限控制
GRANT 和 REVOKE 可在几个层次上控制访问权限:
- 整个服务器,使用
GRANT ALL和REVOKE ALL; - 整个数据库,使用 ON database.*;
- 特定的表,使用 ON database.table;
- 特定的列;
- 特定的存储过程。
新创建的账户没有任何权限。
账户用 username@host 的形式定义,username@% 使用的是默认主机名。
MySQL 的账户信息保存在 mysql 这个数据库中。
1 | USE mysql; |
创建账户
1 | CREATE USER myuser IDENTIFIED BY 'mypassword'; |
修改账户名
1 | UPDATE user SET user='newuser' WHERE user='myuser'; |
删除账户
1 | DROP USER myuser; |
查看权限
1 | SHOW GRANTS FOR myuser; |
授予权限
1 | GRANT SELECT, INSERT ON *.* TO myuser; |
删除权限
1 | REVOKE SELECT, INSERT ON *.* FROM myuser; |
更改密码
1 | SET PASSWORD FOR myuser = 'mypass'; |
存储过程
存储过程的英文是 Stored Procedure。它可以视为一组 SQL 语句的批处理。一旦存储过程被创建出来,使用它就像使用函数一样简单,我们直接通过调用存储过程名即可。
定义存储过程的语法格式:
1 | CREATE |
存储过程定义语句类型:
CREATE PROCEDURE用于创建存储过程DROP PROCEDURE用于删除存储过程ALTER PROCEDURE用于修改存储过程
不同厂商的数据库系统往往语法不一致,要做修改或者重写。
关键语法
声明存储过程:
1 | CREATE PROCEDURE demo_in_parameter(IN p_in int) |
存储过程开始和结束符号:
1 | BEGIN .... END |
变量复制:
1 | SET @p_in=1 -- 存储过程体中,可以通过 select p_in;去打印变量值 |
变量定义:
1 | DECLARE l_int int unsigned default 4000000; |
创建mysql存储过程、存储函数:
1 | create procedure 存储过程名(参数) |
存储过程体:
1 | create function 存储函数名(参数) |
实例
1 | DELIMITER $$ |
调用:call delete_matches(10);
存储过程的参数
- IN 输入参数:表示调用者向过程传入值(传入值可以是字面量或变量)
- OUT 输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)
- INOUT 输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)
注意:
如果过程没有参数,也必须在过程名后面写上小括号例:CREATE PROCEDURE sp_name ([proc_parameter[,...]]) ……
确保参数的名字不等于列的名字,否则在过程体中,参数名被当做列名来处理
变量
- 变量定义
局部变量声明一定要放在存储过程体的开始:
1 | DECLAREvariable_name [,variable_name...] datatype [DEFAULT value]; |
其中,datatype 为 MySQL 的数据类型,如: int, float, date,varchar(length)
例如:
1 | DECLARE l_int int unsigned default 4000000; |
- 变量赋值
1 | SET 变量名 = 表达式值 [,variable_name = expression ...] |
- 用户变量
在MySQL客户端使用用户变量
1 | mysql > SELECT 'Hello World' into @x; |
在存储过程中使用用户变量
1 | mysql > CREATE PROCEDURE GreetWorld( ) SELECT CONCAT(@greeting,' World'); |
在存储过程间传递全局范围的用户变量
1 | mysql> CREATE PROCEDURE p1() SET @last_procedure='p1'; |
注意:
- 用户变量名一般以@开头
- 滥用用户变量会导致程序难以理解及管理
使用存储过程
创建存储过程的要点:
DELIMITER用于定义语句的结束符- 存储过程的 3 种参数类型:
IN:存储过程的入参OUT:存储过程的出参INPUT:既是存储过程的入参,也是存储过程的出参
- 流控制语句:
BEGIN…END:BEGIN…END中间包含了多个语句,每个语句都以(;)号为结束符。DECLARE:DECLARE用来声明变量,使用的位置在于BEGIN…END语句中间,而且需要在其他语句使用之前进行变量的声明。SET:赋值语句,用于对变量进行赋值。SELECT…INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值。每次只能给一个变量赋值,不支持集合的操作。IF…THEN…ENDIF:条件判断语句,可以在IF…THEN…ENDIF中使用ELSE和ELSEIF来进行条件判断。CASE:CASE语句用于多条件的分支判断。
创建存储过程示例:
1 | DROP PROCEDURE IF EXISTS `proc_adder`; |
使用存储过程示例:
1 | set @b=5; |
cursor
游标的的类型:
- 只读游标:只能读取数据,不能修改
- 敏感游标:反映底层数据的实时变化
- 不敏感游标:使用数据的快照,不反映后续变化
必须使用 DECLARE HANDLER 来处理 NOT FOUND 状态:
1 | DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; |
游标的生命周期:
- DECLARE - 声明游标
- OPEN - 打开游标,执行查询
- FETCH - 逐行获取数据
- CLOSE - 关闭游标,释放资源
数据迁移和转换
1 | DROP PROCEDURE IF EXISTS `migrate_student_data`; |
批量数据处理
1 | CREATE PROCEDURE update_student_status() |
存储过程的利弊
存储过程的优点:
- 执行效率高:一次编译多次使用。
- 安全性强:在设定存储过程的时候可以设置对用户的使用权限,这样就和视图一样具有较强的安全性。
- 可复用:将代码封装,可以提高代码复用。
- 性能好
- 由于是预先编译,因此具有很高的性能。
- 一个存储过程替代大量 T_SQL 语句 ,可以降低网络通信量,提高通信速率。
存储过程的缺点:
- 可移植性差:存储过程不能跨数据库移植。由于不同数据库的存储过程语法几乎都不一样,十分难以维护(不通用)。
- 调试困难:只有少数 DBMS 支持存储过程的调试。对于复杂的存储过程来说,开发和维护都不容易。
- 版本管理困难:比如数据表索引发生变化了,可能会导致存储过程失效。我们在开发软件的时候往往需要进行版本管理,但是存储过程本身没有版本控制,版本迭代更新的时候很麻烦。
- 不适合高并发的场景:高并发的场景需要减少数据库的压力,有时数据库会采用分库分表的方式,而且对可扩展性要求很高,在这种情况下,存储过程会变得难以维护,增加数据库的压力,显然就不适用了。
综上,存储过程的优缺点都非常突出,是否使用一定要慎重,需要根据具体应用场景来权衡。
触发器
触发器可以视为一种特殊的存储过程。
触发器是一种与表操作有关的数据库对象,当触发器所在表上出现指定事件时,将调用该对象,即表的操作事件触发表上的触发器的执行。
触发器特性
可以使用触发器来进行审计跟踪,把修改记录到另外一张表中。
MySQL 不允许在触发器中使用 CALL 语句 ,也就是不能调用存储过程。
BEGIN 和 END
当触发器的触发条件满足时,将会执行 BEGIN 和 END 之间的触发器执行动作。
🔔 注意:在 MySQL 中,分号
;是语句结束的标识符,遇到分号表示该段语句已经结束,MySQL 可以开始执行了。因此,解释器遇到触发器执行动作中的分号后就开始执行,然后会报错,因为没有找到和 BEGIN 匹配的 END。这时就会用到
DELIMITER命令(DELIMITER是定界符,分隔符的意思)。它是一条命令,不需要语句结束标识,语法为:DELIMITER new_delemiter。new_delemiter可以设为 1 个或多个长度的符号,默认的是分号;,我们可以把它修改为其他符号,如$-DELIMITER $。在这之后的语句,以分号结束,解释器不会有什么反应,只有遇到了$,才认为是语句结束。注意,使用完之后,我们还应该记得把它给修改回来。
NEW 和 OLD
- MySQL 中定义了
NEW和OLD关键字,用来表示触发器的所在表中,触发了触发器的那一行数据。 - 在
INSERT型触发器中,NEW用来表示将要(BEFORE)或已经(AFTER)插入的新数据; - 在
UPDATE型触发器中,OLD用来表示将要或已经被修改的原数据,NEW用来表示将要或已经修改为的新数据; - 在
DELETE型触发器中,OLD用来表示将要或已经被删除的原数据; - 使用方法:
NEW.columnName(columnName 为相应数据表某一列名)
触发器指令
提示:为了理解触发器的要点,有必要先了解一下创建触发器的指令。
CREATE TRIGGER 指令用于创建触发器。
语法:
1 | CREATE TRIGGER trigger_name |
说明:
- trigger_name:触发器名
- trigger_time: 触发器的触发时机。取值为
BEFORE或AFTER。 - trigger_event: 触发器的监听事件。取值为
INSERT、UPDATE或DELETE。 - table_name: 触发器的监听目标。指定在哪张表上建立触发器。
- FOR EACH ROW: 行级监视,Mysql 固定写法,其他 DBMS 不同。
- trigger_statements: 触发器执行动作。是一条或多条 SQL 语句的列表,列表内的每条语句都必须用分号
;来结尾。
创建触发器示例:
1 | DELIMITER $ |
查看触发器示例:
1 | SHOW TRIGGERS; |
删除触发器示例:
1 | DROP TRIGGER IF EXISTS trigger_insert_user; |
游标
游标(CURSOR)是一个存储在 DBMS 服务器上的数据库查询,它不是一条
SELECT语句,而是被该语句检索出来的结果集。在存储过程中使用游标可以对一个结果集进行移动遍历。
游标主要用于交互式应用,其中用户需要对数据集中的任意行进行浏览和修改。
使用游标的步骤:
- 定义游标:通过
DECLARE cursor_name CURSOR FOR <语句>定义游标。这个过程没有实际检索出数据。 - 打开游标:通过
OPEN cursor_name打开游标。 - 取出数据:通过
FETCH cursor_name INTO var_name ...获取数据。 - 关闭游标:通过
CLOSE cursor_name关闭游标。 - 释放游标:通过
DEALLOCATE PREPARE释放游标。
游标使用示例:
1 | DELIMITER $ |







