存储过程与函数
MySQL 中的存储过程和存储函数是一种在数据库服务器上存储复杂逻辑的方式,允许您封装和重用 SQL 代码。它们在管理复杂的数据库操作和提高性能方面非常有用。以下是对它们的详细介绍:
存储过程(Stored Procedures)
存储过程是一组预先编译好的 SQL 语句。它们可以执行复杂的业务逻辑,能够接受参数、执行 SQL 语句,并返回结果。
-
优点:
- 封装性:可以封装复杂的逻辑。
- 性能:减少了网络通信量,因为不需要多次发送 SQL 语句。
- 安全性:能够实现更细粒度的访问控制。
- 维护性:在服务器端维护,方便修改和维护。
-
创建存储过程:
DELIMITER // CREATE PROCEDURE procedure_name(parameters) --注意在这里存储过程的参数可以有in out inout三种类型,选择输入输出 BEGIN-- SQL statements END // DELIMITER ;
-
调用存储过程:
CALL procedure_name(arguments);
存储过程示例
假设我们要创建一个存储过程,用来插入新的记录到某个表中,并打印一个消息。
假设有一个表
students
,具有列id
(主键,自增)、name
和age
。首先,创建
students
表:CREATE TABLE students (id INT AUTO_INCREMENT,name VARCHAR(100),age INT,PRIMARY KEY (id) );
然后,创建存储过程:
DELIMITER // CREATE PROCEDURE add_student(IN student_name VARCHAR(100), IN student_age INT) BEGININSERT INTO students (name, age) VALUES (student_name, student_age);SELECT 'Student added successfully!' AS message; END // DELIMITER ;
这个存储过程接受两个参数(学生的名字和年龄),将它们插入到
students
表中,并返回一条消息表示操作成功。调用该存储过程的示例:
CALL add_student('John Doe', 20);
这将在
students
表中添加一个名为 ‘John Doe’、年龄为 20 的新记录,并返回消息 ‘Student added successfully!’。
存储函数(Stored Functions)
当然可以。我将提供一个简单的实际例子,展示一个存储函数和一个存储过程在 MySQL 中是如何定义和使用的。
-
优点:
- 可用于表达式:可以在 SQL 查询中直接调用。
- 返回值:必须返回一个值,可以是标量值或表类型。
-
创建存储函数:
DELIMITER // CREATE FUNCTION function_name(parameters) RETURNS data_type --确定返回的类型 BEGIN-- SQL statementsRETURN value; END // DELIMITER ;
-
调用存储函数:
SELECT function_name(arguments);
存储函数示例
假设我们要创建一个存储函数,用来计算给定数字的平方。
DELIMITER // CREATE FUNCTION square_number (num INT) RETURNS INT BEGINRETURN num * num; END // DELIMITER ;
这个函数接受一个整数参数
num
,然后返回这个数的平方。使用该函数的示例:
SELECT square_number(5);
这将返回
25
。
区别
- 返回值:存储过程不需要返回值,而存储函数必须返回一个值。
- 调用方式:存储过程使用
CALL
语句调用,存储函数可以直接在 SQL 表达式中调用。 - 用途:存储过程更适合执行复杂的业务逻辑,存储函数更适合进行计算并返回结果。
- 目的:存储过程主要执行并完成某个功能,存储函数主要用于计算并返回一个函数值
注释、定界符与语句块
MySQL 支持单行和多行注释:
-
单行注释:
- 以
--
(双破折号后跟一个空格)开头,直到行尾。 - 以
#
开头,直到行尾。
示例:
-- 这是一个单行注释 # 这也是一个单行注释
- 以
-
多行注释:
- 以
/*
开始,以*/
结束。
示例:
/* 这是一个多行注释 */
- 以
在 MySQL 中,默认的命令定界符是分号(;
)。但在编写存储过程或函数时,经常需要更改定界符,以允许在过程或函数体内使用分号。
-
更改定界符: 使用
DELIMITER
命令更改定界符。常见的做法是使用//
或$$
作为新的定界符。 -
定界符:一般都是默认为分号;,定界符的作用就是判断是不是要一起执行,这体现了事务的原子性,要么都执行要么都不执行,就如果我们进行复杂的表查询的时候,可以在不加分号的基础上直接换行,因为系统判断回车的时候有没有遇到定界符分号,一旦加了分号,系统就会判断这个语句是需要执行,但是我们在编写存储过程和函数的时其中的代码语句的分隔符也是分号,这会产生冲突,比如说我们定义一个 局部变量declare var ;这时候有个分号,就会判定定界符,开始执行
示例:
DELIMITER // CREATE PROCEDURE myProcedure() BEGIN-- 过程体 END // DELIMITER ;
这里,
//
被用作创建过程的定界符,而过程体内的语句仍然使用分号。
在 MySQL 的存储过程和函数中,语句块是由 BEGIN
和 END
关键字包围的一系列语句。语句块允许将多个语句组合在一起,作为一个单元执行。语句块在逻辑上被当做一个整体对待,因此,在程序执行流程中,语句块要么被执行,要么整体都被执行!!!
-
使用语句块: 通常用于控制流语句(如
IF
、LOOP
、WHILE
)内部或存储过程和函数的定义中。示例:
BEGIN-- 多个语句SET a = b + c;IF a > 10 THEN-- 更多语句END IF; END;
-
注释:对代码进行说明,提高可读性和维护性。
-
定界符:在编写存储过程或函数时,用于区分过程或函数体内的语句结束和整个过程或函数定义的结束。
-
语句块:允许在存储过程和函数中组织多个语句,用于控制流和逻辑分组。
在 MySQL 中,有几种不同类型的变量,包括用户会话变量、局部变量和系统变量。它们的作用范围和用途有所不同:
变量
用户会话变量(User-Defined Session Variables)
-
作用范围:用户会话变量在用户会话内有效,当会话结束时消失。不同会话之间的变量是隔离的。
-
设置和使用:
- 使用
SET @变量名 = 值;
或SELECT @变量名 := 值;
来赋值。 - 直接使用
@变量名
来引用。
- 使用
-
示例:
SET @myVar = 100; SELECT @myVar;
-
用途:常用于存储查询结果、传递数据等。
局部变量(Local Variables)
-
作用范围:局部变量仅在定义它们的存储过程或函数内部有效。
-
定义和使用:
- 在存储过程或函数中使用
DECLARE 变量名 数据类型;
来定义。 - 使用
SET 变量名 = 值;
或直接在 SQL 语句中引用来赋值和使用。
- 在存储过程或函数中使用
-
示例:
CREATE PROCEDURE myProcedure() BEGINDECLARE myVar INT;SET myVar = 100;SELECT myVar; END;
-
用途:用于存储过程和函数中的数据处理和控制流。
-
再来一个实例
#定义局部变total_sale,类型为int,初值为0
DECLAER total_sale INT DEFAULT 0;#同时定义x,y两个变量,类型都是int,初始为0
DECLAER X,Y INT DEFAULT 0;#定义局部变量myname,类型为VARCHAR(10),没有初始值就为NULL
DECLAER myname VARCHAR(10);DECLAER total_s INT DEFAULT 0;
SELECT COUNT(*) INTO total_s FROM S;
SELECT total_s;
系统变量(System Variables)
-
作用范围:系统变量分为全局变量和会话变量。全局变量对所有会话有效,会话变量只对当前会话有效。
-
设置和使用:
- 使用
SET GLOBAL 变量名 = 值;
设置全局变量(需要管理员权限)。 - 使用
SET SESSION 变量名 = 值;
或SET 变量名 = 值;
设置会话变量。 - 使用
SHOW VARIABLES LIKE '变量名';
查看变量值。
- 使用
-
示例:
SET GLOBAL max_connections = 200;#修改最大连接数的全局变量 max_connections SET sort_buffer_size = 1000000; SHOW VARIABLES LIKE 'max_connections';#查询全局变量的值 SELECT @@global.variable_name;
-
用途:用于配置和管理 MySQL 服务器的行为。它们包括了许多设置,如内存分配大小、连接设置、性能调优参数等。
-
用户会话变量:用于单个用户会话,灵活,易于使用,适用于临时存储和传递数据。
-
局部变量:用于存储过程和函数内部,用于模块化编程和封装。
-
系统变量:用于配置 MySQL 服务器,影响其整体行为和性能,操作需要谨慎。
运算符
算术运算符
在 MySQL 中,算术运算符和表达式用于执行基本的数学运算。以下是常见的算术运算符和一些相关函数的使用示例,我将它们按照您要求的表格形式呈现:
运算符/函数 | 用法 | 运算表达式 | 结果 |
---|---|---|---|
+ | 加法 | SELECT 5 + 3; | 8 |
- | 减法 | SELECT 5 - 3; | 2 |
* | 乘法 | SELECT 5 * 3; | 15 |
/ | 除法 | SELECT 6 / 2; | 3 |
% 或 MOD | 求余 | SELECT 5 % 2; 或 SELECT MOD(5, 2); | 1 |
ABS() | 取绝对值 | SELECT ABS(-5); | 5 |
CEILING() 或 CEIL() | 向上取整 | SELECT CEILING(5.2); | 6 |
FLOOR() | 向下取整 | SELECT FLOOR(5.8); | 5 |
ROUND() | 四舍五入 | SELECT ROUND(5.45); | 5 |
POW() 或 POWER() | 求幂 | SELECT POW(2, 3); | 8 |
SQRT() | 平方根 | SELECT SQRT(9); | 3 |
这些运算符和函数可以用于 SQL 查询中的表达式,用于进行数据计算和转换。例如,它们可以在 SELECT
语句、WHERE
子句、或者任何需要进行数学计算的地方使用。
比较运算符
在 MySQL 中,比较运算符用于进行值的比较操作,这些操作通常返回布尔值:TRUE
(真)、FALSE
(假)或 NULL
(在比较中涉及 NULL 值时)。以下是常见比较运算符的用法、表达式及其结果的表格:
运算符 | 用法 | 运算表达式 | 结果示例 |
---|---|---|---|
= | 等于 | SELECT 5 = 5; | TRUE |
!= 或 <> | 不等于 | SELECT 5 != 4; | TRUE |
< | 小于 | SELECT 4 < 5; | TRUE |
> | 大于 | SELECT 6 > 5; | TRUE |
<= | 小于或等于 | SELECT 5 <= 5; | TRUE |
>= | 大于或等于 | SELECT 5 >= 4; | TRUE |
BETWEEN | 在两值之间 | SELECT 5 BETWEEN 1 AND 10; | TRUE |
IN | 在集合中 | SELECT 'a' IN ('a', 'b', 'c'); | TRUE |
IS NULL | 是 NULL 值 | SELECT NULL IS NULL; | TRUE |
IS NOT NULL | 非 NULL 值 | SELECT 'a' IS NOT NULL; | TRUE |
LIKE | 字符串匹配 | SELECT 'abc' LIKE 'a%'; | TRUE |
NOT LIKE | 字符串不匹配 | SELECT 'abc' NOT LIKE 'b%'; | TRUE |
REGEXP | 正则表达式匹配 | SELECT 'abc' REGEXP '^a'; | TRUE |
NOT REGEXP | 正则表达式不匹配 | SELECT 'abc' NOT REGEXP '^b'; | TRUE |
这些比较运算符可以用于 SELECT
语句的条件中,如在 WHERE
或 HAVING
子句中,以及在 JOIN
条件中。它们是 SQL 查询和数据分析中的基本工具,用于过滤和比较数据。
- NULL 值的比较:在 SQL 中,任何与 NULL 的比较都返回 NULL。例如,
SELECT 'a' = NULL;
返回 NULL。要检查 NULL 值,应使用IS NULL
或IS NOT NULL
。 LIKE
和REGEXP
:这些运算符用于字符串的模式匹配。LIKE
用于简单的模式(其中%
表示任意字符串,_
表示任意单个字符),而REGEXP
用于更复杂的正则表达式匹配。
在 MySQL 中,逻辑运算符和位运算符用于执行逻辑和位级运算。下面是这些运算符的用法和示例。
逻辑运算符
逻辑运算符用于组合或修改布尔表达式的结果。常见的逻辑运算符包括:
运算符 | 用法 | 运算表达式 | 结果示例 |
---|---|---|---|
AND 或 && | 逻辑与 | SELECT TRUE AND FALSE; | FALSE |
OR 或 ` | ` | 逻辑或 | |
NOT 或 ! | 逻辑非 | SELECT NOT TRUE; | FALSE |
XOR | 逻辑异或 | SELECT TRUE XOR FALSE; | TRUE |
逻辑运算符通常用于 WHERE
和 HAVING
子句中,用于组合多个条件。
位运算符
位运算符用于对数值的二进制表示进行操作。常见的位运算符包括:
运算符 | 用法 | 运算表达式 | 结果示例 |
---|---|---|---|
& | 位与 | SELECT 6 & 3; | 2 |
` | ` | 位或 | `SELECT 6 |
^ | 位异或 | SELECT 6 ^ 3; | 5 |
~ | 位非 | SELECT ~1; | -2 |
<< | 左移 | SELECT 2 << 1; | 4 |
>> | 右移 | SELECT 4 >> 1; | 2 |
位运算符在处理二进制数据、进行位级操作时非常有用,例如在权限管理、标志位处理等场景中。
- 优先级:逻辑运算符和位运算符有不同的优先级,可能会影响到复合表达式的结果。在编写包含多个运算符的表达式时,最好使用括号明确指定运算顺序。
- NULL 值的处理:在逻辑运算中涉及 NULL 值时,结果可能是不直观的。例如,
TRUE AND NULL
的结果是 NULL。了解如何正确处理 NULL 值是编写有效 SQL 查询的关键。 - 位运算符的应用:位运算符直接作用于操作数的二进制表示,因此需要对操作数的二进制形式有所了解。在某些特定场景中,这些运算符可以非常高效。
优先级
使用数字来表示运算符优先级是一个清晰的方式。以下是 MySQL 中一些常见运算符的优先级列表,用数字表示,数字越小表示优先级越高:
优先级 | 运算符 |
---|---|
1 | BINARY , COLLATE |
2 | ! |
3 | - (负号), ~ (位非) |
4 | ^ (位异或) |
5 | * , / , DIV , % , MOD |
6 | - , + |
7 | << , >> |
8 | & |
9 | ` |
10 | = (赋值), := |
11 | IS , IS NOT , IS NULL , IS NOT NULL , IS TRUE , IS FALSE |
12 | <=> |
13 | >= , > , <= , < , = , <> , != , LIKE , REGEXP , IN |
14 | BETWEEN , CASE , WHEN , THEN , ELSE |
15 | NOT |
16 | AND , && |
17 | XOR |
18 | OR , ` |
这个表格按照 MySQL 中运算符的优先级顺序排列,有助于理解和编写包含多个运算符的复杂 SQL 表达式。在实际应用中,如果表达式中包含多个不同优先级的运算符,建议使用括号明确指定运算顺序,以避免潜在的混淆和错误。
流程控制语句
MySQL 支持多种流程控制语句,这些语句主要用于存储过程和函数中,允许执行基于条件的操作或重复执行一组操作。以下是 MySQL 中常见的流程控制语句及其使用方法和案例:
1. IF 语句
用于基于条件执行不同的代码块。
-
语法:
IF condition THEN-- 代码块 1 ELSEIF condition2 THEN-- 代码块 2 ELSE-- 代码块 3 END IF; #结束的时候用end if
-
示例:
IF user_age >= 18 THENSELECT 'Adult'; ELSESELECT 'Minor'; END IF;
2. CASE 语句
类似于其他编程语言中的 switch 语句,根据条件选择执行不同的代码块。
-
语法:
CASEWHEN condition1 THEN-- 代码块 1WHEN condition2 THEN-- 代码块 2ELSE-- 默认代码块 END CASE;
-
示例:
CASE user_statusWHEN 'active' THENSELECT 'User is active';WHEN 'inactive' THENSELECT 'User is inactive';ELSESELECT 'Unknown status'; END CASE;
注意在本实例中user_status是可以被选择状态的,如果case之后没有东西就会生成一列新的属性
3. LOOP 语句
用于无条件的循环执行一组语句,直到遇到 LEAVE 语句。与while语句相反,loop语句需要再判断条件为假的时候才会继续,一定判断条件为真,就会退出循环
-
语法:
label: LOOP-- 代码块IF condition THENLEAVE label;END IF; END LOOP label;
-
示例:
count_loop: LOOPSET counter = counter + 1;IF counter > 10 THENLEAVE count_loop;END IF; END LOOP count_loop;
理解案例,假设我们要编写一个存储过程,其中包含一个 LOOP 循环,该循环将计数器递增,并在计数器超过 10 时退出:
DELIMITER //CREATE PROCEDURE loop_example() BEGINDECLARE counter INT DEFAULT 0;count_loop: LOOPSET counter = counter + 1;-- 当计数器超过 10 时退出循环IF counter > 10 THENLEAVE count_loop;END IF;END LOOP count_loop;SELECT counter AS final_count; END //DELIMITER ;
在这个例子中,LOOP 会无条件地执行,每次循环将 counter 加 1。当 counter 的值超过 10 时,IF 条件成立,触发 LEAVE 语句,从而退出循环。
4. REPEAT 语句
类似于 do-while 循环,至少执行一次代码块,直到条件为真。
-
语法:
REPEAT-- 代码块 UNTIL condition END REPEAT;
-
示例:
REPEATSET counter = counter + 1; UNTIL counter > 10 END REPEAT;
5. WHILE 语句
只要条件为真,就重复执行代码块。
-
语法:
WHILE condition DO-- 代码块 END WHILE;
-
示例:
WHILE counter < 10 DOSET counter = counter + 1; END WHILE;