本章讨论存储的数据库对象,这些对象是根据存储在服务器上以供以后执行的SQL代码定义的。
存储的对象包括以下对象类型:
存储过程:
CREATE
PROCEDURE
使用该
CALL
语句
创建
并调用
的对象
。
过程没有返回值,但可以修改其参数以供调用者稍后检查。
它还可以生成要返回给客户端程序的结果集。
存储函数:
CREATE
FUNCTION
使用内置函数
创建
并使用
的对象
。
您在表达式中调用它,并在表达式求值期间返回一个值。
触发器:使用该对象创建的对象
CREATE
TRIGGER
与表关联。
当表发生特定事件(例如插入或更新)时,将激活触发器。
事件:
CREATE
EVENT
根据计划由服务器
创建
和调用
的对象
。
视图:使用该对象创建的对象
CREATE
VIEW
生成结果集。
视图充当虚拟表。
本文档中使用的术语反映了存储的对象层次结构:
存储例程包括存储过程和函数。
存储的程序包括存储的例程,触发器和事件。
存储的对象包括存储的程序和视图。
本章介绍如何使用存储的对象。 以下部分提供有关与这些对象相关的语句的SQL语法以及有关对象处理的其他信息:
为每个对象类型,还有
CREATE
,
ALTER
和
DROP
控制哪些对象存在以及它们是如何定义语句。
请参见
第13.1节“数据定义语句”
。
该
CALL
语句用于调用存储过程。
请参见
第13.2.1节“CALL语法”
。
存储的程序定义包括可以使用复合语句,循环,条件和声明变量的主体。 请参见 第13.6节“复合语句语法” 。
检测到存储程序引用的对象的元数据更改,并在下次执行程序时自动重新分析受影响的语句。 有关更多信息,请参见 第8.10.3节“准备语句和存储程序的缓存” 。
每个存储的程序都包含一个由SQL语句组成的主体。
此语句可能是由以分号(
;
)字符
分隔的多个语句组成的复合语句
。
例如,以下存储过程的主体由一个
BEGIN
...
END
包含
SET
语句
的
块
和一个
REPEAT
本身包含另一个
SET
语句
的
循环组成
:
CREATE PROCEDURE dorepeat(p1 INT) 开始 SET @x = 0; REPEAT SET @x = @x + 1; UNTIL @x> p1 END REPEAT; 结束;
如果使用 mysql 客户端程序定义包含分号字符的存储程序, 则会 出现问题。 默认情况下, mysql 本身将分号识别为语句分隔符,因此必须临时重新定义分隔符以使 mysql 将整个存储的程序定义传递给服务器。
要重新定义
mysql
分隔符,请使用该
delimiter
命令。
以下示例显示了如何对
dorepeat()
刚刚显示
的
过程
执行此操作
。
分隔符更改为
//
以使整个定义作为单个语句传递到服务器,然后
;
在调用过程之前
还原到
该定义。
这使得
;
过程体中使用
的
分隔符能够传递到服务器,而不是由
mysql
本身
解释
。
MySQL的>delimiter //
mysql>CREATE PROCEDURE dorepeat(p1 INT)
- >BEGIN
- >SET @x = 0;
- >REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;
- >END
- >//
查询正常,0行受影响(0.00秒) MySQL的>delimiter ;
MySQL的>CALL dorepeat(1000);
查询正常,0行受影响(0.00秒) MySQL的>SELECT @x;
+ ------ + | @x | + ------ + | 1001 | + ------ + 1排(0.00秒)
您可以将分隔符重新定义为除以外的字符串
//
,分隔符可以包含单个字符或多个字符。
您应该避免使用反斜杠(
\
)字符,因为这是MySQL的转义字符。
以下是获取参数,使用SQL函数执行操作并返回结果的函数示例。
在这种情况下,不必使用,
delimiter
因为函数定义不包含内部
;
语句分隔符:
mysql>CREATE FUNCTION hello (s CHAR(20))
mysql>RETURNS CHAR(50) DETERMINISTIC
- >RETURN CONCAT('Hello, ',s,'!');
查询正常,0行受影响(0.00秒) MySQL的>SELECT hello('world');
+ ---------------- + | 你好('世界')| + ---------------- + | 你好,世界!| + ---------------- + 1排(0.00秒)
MySQL支持存储的例程(过程和函数)。 存储例程是一组可以存储在服务器中的SQL语句。 完成此操作后,客户端不需要继续重新发布单个语句,但可以参考存储的例程。
存储例程在某些情况下特别有用:
当多个客户端应用程序以不同语言编写或在不同平台上工作时,需要执行相同的数据库操作。
当安全是至关重要的。 例如,银行使用存储过程和函数进行所有常见操作。 这提供了一致且安全的环境,并且例程可以确保正确记录每个操作。 在这样的设置中,应用程序和用户无法直接访问数据库表,但只能执行特定的存储例程。
存储的例程可以提供改进的性能,因为需要在服务器和客户端之间发送较少的信息。 权衡的是,这确实增加了数据库服务器上的负载,因为更多的工作在服务器端完成,而在客户端(应用程序)端完成的工作则更少。 如果许多客户端计算机(例如Web服务器)仅由一个或几个数据库服务器提供服务,请考虑这一点。
存储的例程还使您可以在数据库服务器中拥有函数库。 这是现代应用程序语言共享的一项功能,可在内部实现此类设计(例如,通过使用类)。 即使在数据库使用范围之外,使用这些客户端应用程序语言功能对程序员也是有益的。
MySQL遵循存储例程的SQL:2003语法,IBM的DB2也使用它。 支持此处描述的所有语法,并在适当的位置记录任何限制和扩展。
您可以在 使用存储过程和函数时 找到使用的 存储过程用户论坛 。
有关MySQL中存储例程的一些常见问题的解答,请参见 第A.4节“MySQL 8.0常见问题:存储过程和函数” 。
存储例程的使用存在一些限制。 请参见 第C.1节“存储程序的限制” 。
存储例程的二进制日志记录按 第24.7节“存储程序二进制日志记录”中所述进行 。
存储的例程是过程或函数。
使用
CREATE
PROCEDURE
和
CREATE
FUNCTION
语句
创建存储例程
(请参见
第13.1.17节“创建过程和创建函数语法”
)。
使用
CALL
语句
调用过程
(请参见
第13.2.1节“CALL语法”
),并且只能使用输出变量传回值。
可以从语句内部调用函数,就像任何其他函数一样(即通过调用函数的名称),并且可以返回标量值。
存储例程的主体可以使用复合语句(请参见
第13.6节“复合语句语法”
)。
可以使用
DROP
PROCEDURE
和
DROP
FUNCTION
语句
删除存储的例程
(请参见
第13.1.29节“DROP PROCEDURE和DROP FUNCTION语法”
),并使用
ALTER
PROCEDURE
和
ALTER
FUNCTION
语句进行
更改
(请参见
第13.1.7节“ALTER PROCEDURE语法”
)。
存储过程或函数与特定数据库相关联。 这有几个含义:
调用例程时,将
执行
隐式
(并在例程终止时撤消)。
不允许存储例程中的语句。
USE
db_name
USE
您可以使用数据库名称限定例程名称。
这可用于引用不在当前数据库中的例程。
例如,要调用
与
数据库
关联
的存储过程
p
或函数
,您可以说
或
。
f
test
CALL test.p()
test.f()
删除数据库时,也会删除与其关联的所有存储例程。
存储的函数不能递归。
允许存储过程中的递归,但默认情况下禁用。
要启用递归,请将
max_sp_recursion_depth
服务器系统变量设置为大于零的值。
存储过程递归增加了对线程堆栈空间的需求。
如果增加值
max_sp_recursion_depth
,可能需要通过增加
thread_stack
服务器启动时
的值来增加线程堆栈大小
。
有关
更多信息
,
请参见
第5.1.8节“服务器系统变量”
。
MySQL支持一个非常有用的扩展,它允许在
SELECT
存储过程中
使用常规
语句(即,不使用游标或局部变量)。
这种查询的结果集只是直接发送给客户端。
多个
SELECT
语句生成多个结果集,因此客户端必须使用支持多个结果集的MySQL客户端库。
这意味着客户端必须使用MySQL版本的客户端库,至少与4.1版本一样。
客户端还应
CLIENT_MULTI_RESULTS
在连接时
指定
选项。
对于C程序,可以使用
mysql_real_connect()
C API函数
完成此操作
。
请参见
第28.7.7.54节“mysql_real_connect()”
和
第28.7.23节“C API多语句执行支持”
。
MySQL授权系统将存储的例程考虑在内,如下所示:
将
CREATE ROUTINE
需要的权限来创建存储例程。
在
ALTER ROUTINE
需要的权限更改或删除保存的程序。
如有必要,此权限将自动授予例程的创建者,并在例程被删除时从创建者中删除。
EXECUTE
执行存储例程需要
该
特权。
但是,如果需要,此特权将自动授予例程的创建者(并在例程被删除时从创建者中删除)。
此外,
SQL SECURITY
例程
的默认
特征是
DEFINER
,允许访问与例程关联的数据库的用户执行例程。
如果
automatic_sp_privileges
系统变量为0,
EXECUTE
并且
ALTER ROUTINE
权限不会自动授予,并从日常的创造者下降。
例程的创建者是用于为其执行
CREATE
语句
的帐户
。
这可能
DEFINER
与例程定义中
命名的帐户
不同。
有关存储例程的元数据可以通过以下方式获得:
查询
数据库
ROUTINES
表
INFORMATION_SCHEMA
。
请参见
第25.23节“INFORMATION_SCHEMA ROUTINES表”
。
使用
SHOW CREATE PROCEDURE
和
SHOW CREATE FUNCTION
语句查看例程定义。
请参见
第13.7.6.9节“显示创建过程语法”
。
使用
SHOW PROCEDURE STATUS
和
SHOW
FUNCTION STATUS
语句查看常规特征。
请参见
第13.7.6.28节“显示过程状态语法”
。
在存储例程(过程或函数)或触发器
LAST_INSERT_ID()
的主体内
,
更改
的值与
在这些类型的对象的主体外执行的语句
的
更改方式相同(请参见
第12.15节“信息函数”
)。
LAST_INSERT_ID()
以下语句可以看出
存储的例程或触发器对其值的影响
取决于例程的类型:
如果存储过程执行更改值的语句,则更改的值
LAST_INSERT_ID()
将由过程调用之后的语句看到。
对于更改值的存储函数和触发器,当函数或触发器结束时,将恢复该值,因此后续语句不会看到更改的值。
触发器是与表关联的命名数据库对象,并在表发生特定事件时激活。 触发器的一些用途是执行对要插入表中的值的检查,或者对更新中涉及的值执行计算。
触发器定义为在语句插入,更新或删除关联表中的行时激活。
这些行操作是触发事件。
例如,可以通过
INSERT
或
LOAD
DATA
语句插入行,并为每个插入的行激活插入触发器。
可以将触发器设置为在触发事件之前或之后激活。
例如,您可以在插入表中的每一行之前或更新的每一行之后激活一个触发器。
MySQL触发器仅针对SQL语句对表所做的更改进行激活。
这包括对可更新视图的基础表的更改。
对于不将SQL语句传输到MySQL服务器的API所做的更改,触发器不会激活。
这意味着使用
NDB
API
进行的更新不会激活触发器
。
更改
INFORMATION_SCHEMA
或
performance_schema
表格
不会激活触发器
。
这些表实际上是视图上不允许的视图和触发器。
以下部分描述了创建和删除触发器的语法,显示了如何使用它们的一些示例,并指出了如何获取触发器元数据。
有关 MySQL中触发器的常见问题的解答 ,请参见 第A.5节“MySQL 8.0 FAQ:触发器” 。
使用触发器有一些限制; 请参见 第C.1节“存储程序的限制” 。
触发器的二进制日志记录按 第24.7节“存储程序二进制日志记录”中所述进行 。
要创建触发器或删除触发器,请使用
第13.1.22节“创建触发器语法”
和
第13.1.34节“DROP TRIGGER语法”中
所述
的
CREATE
TRIGGER
或
DROP
TRIGGER
语句
。
下面是一个简单的示例,它将触发器与表关联起来,以激活
INSERT
操作。
触发器充当累加器,将插入到表的一列中的值相加。
MySQL的>CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
查询正常,0行受影响(0.03秒) MySQL的>CREATE TRIGGER ins_sum BEFORE INSERT ON account
FOR EACH ROW SET @sum = @sum + NEW.amount;
查询OK,0行受影响(0.01秒)
该
CREATE
TRIGGER
语句创建一个
ins_sum
与该
account
表
关联的
名为的触发器
。
它还包括指定触发器操作时间,触发事件以及触发器激活时要执行的操作的子句:
关键字
BEFORE
表示触发操作时间。
在这种情况下,触发器在每行插入表格之前激活。
这里允许的另一个关键字是
AFTER
。
关键字
INSERT
表示触发事件;
也就是说,激活触发器的操作类型。
在该示例中,
INSERT
操作导致触发器激活。
您还可以创建触发器
DELETE
和
UPDATE
操作。
以下语句
FOR EACH ROW
定义了触发器主体;
也就是说,每次触发器激活时执行的语句,对于受触发事件影响的每一行都会发生一次。
在该示例中,触发器主体是一个简单的
SET
,它将插入到
amount
列中
的值累积到用户变量中
。
该语句引用列
NEW.amount
,表示
“
要插入新行
的
amount
列
的值
”
。
”
要使用触发器,请将accumulator变量设置为零,执行一个
INSERT
语句,然后查看该变量后面的值:
mysql>SET @sum = 0;
mysql>INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);
mysql>SELECT @sum AS 'Total amount inserted';
+ ----------------------- + | 插入总量| + ----------------------- + | 1852.48 | + ----------------------- +
在这种情况下,
语句执行
@sum
后
的值
INSERT
是
14.98 + 1937.50 - 100
,或
1852.48
。
要销毁触发器,请使用
DROP
TRIGGER
语句。
如果触发器不在默认架构中,则必须指定架构名称:
MySQL的> DROP TRIGGER test.ins_sum;
如果删除表,则表的任何触发器也将被删除。
模式名称空间中存在触发器名称,这意味着所有触发器必须在模式中具有唯一名称。 不同模式中的触发器可以具有相同的名称。
可以为具有相同触发事件和操作时间的给定表定义多个触发器。
例如,您可以
BEFORE UPDATE
为表创建
两个
触发器。
默认情况下,具有相同触发事件和操作时间的触发器按创建顺序激活。
要影响触发器顺序,请在
FOR EACH ROW
指示
之后指定一个子句
,
FOLLOWS
或者
指定
PRECEDES
具有相同触发事件和操作时间的现有触发器的名称。
使用时
FOLLOWS
,新触发器在现有触发器之后激活。
使用时
PRECEDES
,新触发器在现有触发器之前激活。
例如,以下触发器定义
BEFORE INSERT
为
account
表
定义了另一个
触发器
:
MySQL的>CREATE TRIGGER ins_transaction BEFORE INSERT ON account
FOR EACH ROW PRECEDES ins_sum
SET
@deposits = @deposits + IF(NEW.amount>0,NEW.amount,0),
@withdrawals = @withdrawals + IF(NEW.amount<0,-NEW.amount,0);
查询OK,0行受影响(0.01秒)
此触发器
ins_transaction
类似于
ins_sum
但分别累积存款和取款。
它有一个
PRECEDES
条款,使它在之前激活
ins_sum
;
没有该子句,它会在之后激活,
ins_sum
因为它是在之后创建的
ins_sum
。
在触发器主体中,使用
OLD
和
NEW
关键字可以访问受触发器影响的行中的列。
OLD
并且
NEW
是触发器的MySQL扩展;
它们不区分大小写。
在
INSERT
触发器中,只能
使用;
没有旧排。
在
触发器中,只能
使用;
没有新的行。
在
触发器中,您可以
在更新之前
引用行的列,并在更新后引用行的列。
NEW.
col_name
DELETE
OLD.
col_name
UPDATE
OLD.
col_name
NEW.
col_name
名为的列
OLD
是只读的。
您可以引用它(如果您有
SELECT
权限),但不能修改它。
NEW
如果您拥有该
SELECT
权限,则
可以引用名为的列
。
在
BEFORE
触发器中,
如果您拥有该
权限
,也可以更改其值
。
这意味着您可以使用触发器来修改要插入新行或用于更新行的值。
(这样的
语句对
触发器
没有影响,
因为行已经发生了变化。)
SET NEW.
col_name
=
value
UPDATE
SET
AFTER
在一个
BEFORE
触发器中,
NEW
对于一个值
AUTO_INCREMENT
列是0,实际上没有被插入新行时自动生成的序列号。
通过使用该
BEGIN
...
END
构造,您可以定义执行多个语句的触发器。
在
BEGIN
块中,您还可以使用存储例程(例如条件和循环)中允许的其他语法。
但是,就像存储例程一样,如果使用
mysql
程序定义执行多个语句的触发器,则必须重新定义
mysql
语句分隔符,以便可以
;
在触发器定义中
使用
语句分隔符。
以下示例说明了这些要点。
它定义了一个
UPDATE
触发器检查用于更新每一行的新值,并将值修改为0到100之间的范围。这必须是一个
BEFORE
触发器,因为在用于更新行之前必须检查该值:
mysql>delimiter //
mysql> mysql>CREATE TRIGGER upd_check BEFORE UPDATE ON account
FOR EACH ROW
BEGIN
IF NEW.amount < 0 THEN
SET NEW.amount = 0;
ELSEIF NEW.amount > 100 THEN
SET NEW.amount = 100;
END IF;
END;//
delimiter ;
可以更容易地单独定义存储过程,然后使用简单
CALL
语句
从触发器调用它
。
如果要从多个触发器中执行相同的代码,这也是有利的。
触发器在激活时执行的语句中可能出现的内容存在限制:
触发器不能使用该
CALL
语句来调用将数据返回给客户端或使用动态SQL的存储过程。
(允许存储过程通过
OUT
或
INOUT
参数
将数据返回到触发器
。)
触发不能使用语句或明或暗地开始或结束交易,如
START
TRANSACTION
,
COMMIT
或
ROLLBACK
。
(
ROLLBACK to
SAVEPOINT
被允许,因为它不会结束交易。)。
另请参见 第C.1节“存储程序的限制” 。
MySQL在触发执行期间处理错误,如下所示:
如果
BEFORE
触发器失败,则不执行相应行上的操作。
甲
BEFORE
触发由被激活的
尝试
插入或修改的行,而不管的尝试是否成功随后。
一个
AFTER
只有当任何触发器执行
BEFORE
触发器和行操作成功执行。
a
BEFORE
或
AFTER
触发
期间的错误
导致导致触发器调用的整个语句失败。
对于事务表,语句失败应导致回滚语句执行的所有更改。 触发器失败会导致语句失败,因此触发器失败也会导致回滚。 对于非事务性表,无法执行此类回滚,因此尽管语句失败,但在错误点之前执行的任何更改仍然有效。
触发器可以按名称包含对表的直接引用,例如
testref
此示例中显示
的命名触发器
:
CREATE TABLE test1(a1 INT); CREATE TABLE test2(a2 INT); CREATE TABLE test3(a3 INT NOT NULL AUTO_INCREMENT PRIMARY KEY); CREATE TABLE test4( a4 INT NOT NULL AUTO_INCREMENT PRIMARY KEY, b4 INT DEFAULT 0 ); 分隔符| 在插入test1之前创建TRIGGER testref 对于每一行 开始 INSERT INTO test2 SET a2 = NEW.a1; 从test3删除WHERE a3 = NEW.a1; UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1; 结束; | 分隔符; 插入test3(a3)VALUES (NULL),(NULL),(NULL),(NULL),(NULL), (NULL),(NULL),(NULL),(NULL),(NULL); 插入test4(a4)VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0);
假设您将以下值插入表中
test1
,如下所示:
MySQL的>INSERT INTO test1 VALUES
(1), (3), (1), (7), (1), (8), (4), (4);
查询OK,8行受影响(0.01秒) 记录:8个重复:0个警告:0
因此,这四个表包含以下数据:
MySQL的>SELECT * FROM test1;
+ ------ + | a1 | + ------ + | 1 | | 3 | | 1 | | 7 | | 1 | | 8 | | 4 | | 4 | + ------ + 8行(0.00秒) MySQL的>SELECT * FROM test2;
+ ------ + | a2 | + ------ + | 1 | | 3 | | 1 | | 7 | | 1 | | 8 | | 4 | | 4 | + ------ + 8行(0.00秒) MySQL的>SELECT * FROM test3;
+ ---- + | a3 | + ---- + | 2 | | 5 | | 6 | | 9 | | 10 | + ---- + 5行(0.00秒) MySQL的>SELECT * FROM test4;
+ ---- + ------ + | a4 | b4 | + ---- + ------ + | 1 | 3 | | 2 | 0 | | 3 | 1 | | 4 | 2 | | 5 | 0 | | 6 | 0 | | 7 | 1 | | 8 | 1 | | 9 | 0 | | 10 | 0 | + ---- + ------ + 10行(0.00秒)
触发器的元数据可以通过以下方式获得:
查询
数据库
TRIGGERS
表
INFORMATION_SCHEMA
。
请参见
第25.34节“INFORMATION_SCHEMA TRIGGERS表”
。
使用该
SHOW CREATE TRIGGER
声明。
请参见
第13.7.6.11节“显示创建触发器语法”
。
使用该
SHOW
TRIGGERS
声明。
请参见
第13.7.6.38节“显示触发器语法”
。
在 MySQL的事件调度 管理事件的调度和执行,也就是说,按照时间表运行的任务。 以下讨论涵盖事件调度程序,并分为以下几个部分:
第24.4.1节“事件调度程序概述” 提供了对MySQL事件的介绍和概念概述。
第24.4.3节“事件语法” 讨论了用于创建,更改和删除MySQL事件的SQL语句。
第24.4.4节“事件元数据” 显示了如何获取有关事件的信息以及MySQL服务器如何存储此信息。
第24.4.6节“事件调度程序和MySQL权限” 讨论了处理事件所需的权限以及事件在执行时对权限的影响。
存储例程需要
系统数据库中
的
events
数据字典表
mysql
。
此表是在MySQL 8.0安装过程中创建的。
如果要从早期版本升级到MySQL 8.0,请确保执行升级过程以确保您的系统数据库是最新的。
请参见
第2.11节“升级MySQL”
。
您可以在 使用预定事件时 找到使用的 MySQL事件调度程序用户论坛 。
对事件的使用有一些限制; 请参见 第C.1节“存储程序的限制” 。
事件的二进制日志记录按 第24.7节“存储程序二进制日志记录”中所述进行 。
MySQL事件是根据计划运行的任务。
因此,我们有时将它们称为
预定
事件。
创建事件时,您正在创建一个命名数据库对象,该对象包含一个或多个SQL语句,这些SQL语句将以一个或多个定期间隔执行,从特定日期和时间开始和结束。
从概念上讲,这类似于Unix
crontab
(也称为
“
cron作业
”
)或Windows任务计划程序的概念。
这种类型的计划任务有时也称为 “ 时间触发器 ” ,暗示这些是由时间推移触发的对象。 虽然这基本上是正确的,但我们更喜欢使用术语 事件 来避免与 第24.3节“使用触发器”中 讨论的类型的触发器混淆 。 事件应该更具体地不与 “ 临时触发器 ” 混淆 。 触发器是数据库对象,其语句是响应于在给定表上发生的特定类型的事件而执行的,((已调度的)事件是响应于指定时间间隔的通过而执行其语句的对象。
虽然SQL标准中没有提供事件调度的规定,但在其他数据库系统中也有先例,您可能会注意到这些实现与MySQL服务器中的实现之间存在一些相似之处。
MySQL Events具有以下主要特性和属性:
在MySQL中,事件由其名称和分配给它的模式唯一标识。
事件根据计划执行特定操作。
此操作由一个SQL语句组成,
BEGIN
...
END
如果需要
,该语句可以是
块中
的复合语句
(请参见
第13.6节“复合语句语法”
)。
事件的时间可以是
一次性
或
反复发作
。
一次性事件仅执行一次。
周期性事件以固定间隔重复其动作,并且可以为周期性事件的时间表分配特定的开始日期和时间,结束日期和时间,两者或两者都不分配。
(默认情况下,定期事件的计划在创建后立即开始,并且无限期地继续,直到它被禁用或删除。)
如果重复事件未在其调度间隔内终止,则结果可能是同时执行事件的多个实例。
如果这是不合需要的,您应该建立一个机制来防止同时发生。
例如,您可以使用
GET_LOCK()
函数,或行或表锁定。
用户可以使用用于这些目的的SQL语句创建,修改和删除预定事件。 语法无效的事件创建和修改语句失败,并显示相应的错误消息。 用户可以在事件的动作中包括需要用户实际上没有的特权的语句 。 事件创建或修改语句成功,但事件的操作失败。 有关详细 信息, 请参见 第24.4.6节“事件调度程序和MySQL权限” 。
可以使用SQL语句设置或修改事件的许多属性。 这些属性包括事件的名称,计时,持久性(即,是否在其计划到期后保留),状态(启用或禁用),要执行的操作以及分配给它的模式。 请参见 第13.1.3节“ALTER EVENT语法” 。
事件的默认定义者是创建事件的用户,除非事件已被更改,在这种情况下,定义者是发出
ALTER
EVENT
影响该事件
的最后一个
语句
的用户
。
任何具有
EVENT
定义事件的数据库特权的
用户都可以修改
事件。
请参见
第24.4.6节“事件调度程序和MySQL权限”
。
事件的操作语句可能包括存储例程中允许的大多数SQL语句。 有关限制,请参见 第C.1节“存储程序的限制” 。
事件由特殊
事件调度程序线程执行
;
当我们引用事件调度程序时,我们实际上是指这个线程。
运行时,具有
PROCESS
输出权限
的用户可以看到事件调度程序线程及其当前状态
SHOW
PROCESSLIST
,如下面的讨论所示。
全局
event_scheduler
系统变量确定是否在服务器上启用并运行事件调度程序。
它具有这3个值中的一个,这会影响事件调度,如此处所述。
默认是
ON
。
ON
:事件计划程序已启动;
事件调度程序线程运行并执行所有计划事件。
当事件调度程序是
ON
,事件调度程序线程在
SHOW PROCESSLIST
作为守护进程
的输出中列出
,其状态如下所示:
MySQL的> SHOW PROCESSLIST\G
*************************** 1。排******************** *******
Id:1
用户:root
主持人:localhost
db:NULL
命令:查询
时间:0
状态:NULL
信息:show processlist
*************************** 2.排******************** *******
Id:2
用户:event_scheduler
主持人:localhost
db:NULL
命令:守护进程
时间:3
州:等待下一次激活
信息:NULL
2行(0.00秒)
可以通过设置
event_scheduler
to
的值来停止事件调度
OFF
。
OFF
:事件计划程序已停止。
事件调度程序线程未运行,未在输出中显示
SHOW PROCESSLIST
,并且未执行任何预定事件。
当事件调度停止(
event_scheduler
是
OFF
),它可以通过设置的值开始
event_scheduler
到
ON
。
(见下一个项目。)
DISABLED
:此值使事件计划程序不可操作。
事件调度程序是
DISABLED
,事件调度程序线程不运行(因此不会出现在输出中
SHOW PROCESSLIST
)。
此外,无法在运行时更改事件计划程序状态。
如果事件调度程序状态尚未设置为
DISABLED
,
event_scheduler
则可以在
ON
和
之间切换
OFF
(使用
SET
)。
也可以使用
0
的
OFF
,而
1
对于
ON
设置该变量时。
因此,可以在
mysql
客户端中
使用以下4个语句中的任何一个
来打开事件调度程序:
SET GLOBAL event_scheduler = ON; SET @@ GLOBAL.event_scheduler = ON; SET GLOBAL event_scheduler = 1; SET @@ GLOBAL.event_scheduler = 1;
同样,这4个语句中的任何一个都可用于关闭事件调度程序:
SET GLOBAL event_scheduler = OFF; SET @@ GLOBAL.event_scheduler = OFF; SET GLOBAL event_scheduler = 0; SET @@ GLOBAL.event_scheduler = 0;
虽然
ON
与
OFF
具有数字等同物,显示该值
event_scheduler
通过
SELECT
或
SHOW
VARIABLES
总是之一
OFF
,
ON
或
DISABLED
。
DISABLED
没有数字等价物
。
出于这个原因,
ON
并且
OFF
通常是优选的超过
1
和
0
设置此变量时。
请注意,尝试设置
event_scheduler
而不将其指定为全局变量会导致错误:
mysql < ERROR 1229(HY000):变量'event_scheduler'是GLOBALSET @@event_scheduler = OFF;
变量,应该用SET GLOBAL设置
可以将事件调度程序设置为
DISABLED
仅在服务器启动时。
如果
event_scheduler
是
ON
或
OFF
,则无法
DISABLED
在运行时
将其设置为
。
此外,如果将事件计划程序设置为
DISABLED
启动时,则无法更改
event_scheduler
运行时
的值
。
要禁用事件调度程序,请使用以下两种方法之一:
作为启动服务器时的命令行选项:
--event调度= DISABLED
在服务器配置文件(
my.cnf
或
my.ini
Windows系统)中,包括服务器将读取它的行(例如,在一
[mysqld]
节中):
event_scheduler = DISABLED
要启用事件计划程序,请在不使用
--event-scheduler=DISABLED
命令行选项的
情况下重新启动服务器
,或者在删除或注释掉
event-scheduler=DISABLED
服务器配置文件中
包含的行后
(如果适用)。
或者,您可以
在启动服务器时
使用
ON
(或
1
)或
OFF
(或
0
)代替
DISABLED
值。
当您可以发出事件操作语句
event_scheduler
设置为
DISABLED
。
在这种情况下不会产生警告或错误(前提是这些语句本身有效)。
但是,在将此变量设置为
ON
(或
1
)
之前,无法执行预定事件
。
完成此操作后,事件调度程序线程将执行其调度条件满足的所有事件。
使用该
--skip-grant-tables
选项
启动MySQL服务器
会导致
event_scheduler
设置为
DISABLED
,覆盖命令行
my.cnf
或
my.ini
文件
中的任何其他值集
(Bug#26807)。
有关用于创建,更改和删除事件的SQL语句,请参见 第24.4.3节“事件语法” 。
MySQL
EVENTS
在
INFORMATION_SCHEMA
数据库中
提供了一个
表
。
可以查询此表以获取有关已在服务器上定义的预定事件的信息。
有关更多信息
,
请参见
第24.4.4节“事件元数据”
和
第25.10节“INFORMATION_SCHEMA事件表”
。
有关事件调度和MySQL权限系统的信息,请参见 第24.4.6节“事件调度程序和MySQL权限” 。
MySQL提供了几个用于处理预定事件的SQL语句:
使用该
CREATE
EVENT
语句
定义新事件
。
请参见
第13.1.13节“创建事件语法”
。
可以通过
ALTER
EVENT
语句
更改现有事件的定义
。
请参见
第13.1.3节“ALTER EVENT语法”
。
当不再需要或不需要预定事件时,可以使用该
DROP
EVENT
语句
通过其定义者从服务器中删除它
。
请参见
第13.1.25节“DROP EVENT语法”
。
事件是否持续超过其计划结束还取决于其
ON
COMPLETION
条款(如果有)。
请参见
第13.1.13节“创建事件语法”
。
任何具有
EVENT
定义事件的数据库特权的
用户都可以删除
事件。
请参见
第24.4.6节“事件调度程序和MySQL权限”
。
有关事件的元数据可以通过以下方式获得:
查询
数据库
EVENTS
表
INFORMATION_SCHEMA
。
请参见
第25.10节“INFORMATION_SCHEMA事件表”
。
使用该
SHOW CREATE EVENT
声明。
请参见
第13.7.6.7节“显示创建事件语法”
。
使用该
SHOW
EVENTS
声明。
请参见
第13.7.6.18节“显示事件语法”
。
事件调度程序时间表示
MySQL中的每个会话都有一个会话时区(STZ)。
这是
会话开始时
time_zone
从服务器的全局
time_zone
值
初始化
的会话
值,
但可以在会话期间更改。
执行
CREATE
EVENT
or
ALTER
EVENT
语句
时的当前会话时区
用于解释事件定义中指定的时间。
这成为事件时区(ETZ);
也就是说,用于事件调度的时区,它在执行时在事件中生效。
对于在事件信息表示
mysql.event
表中,
execute_at
,
starts
,和
ends
时间被转换为UTC和与事件的时区一起存储。
这使得事件执行能够按照定义继续执行,而不管服务器时区或夏令时效果的任何后续更改。
该
last_executed
时间也存储在UTC。
如果您从中选择信息
mysql.event
,则刚刚提到的时间将被检索为UTC值。
这些时间也可以通过从
INFORMATION_SCHEMA.EVENTS
表中
选择
或从中获得
SHOW
EVENTS
,但它们被报告为ETZ值。
从这些来源获得的其他时间表示事件创建或最后更改的时间;
这些显示为STZ值。
下表总结了事件时间的表示。
值 | mysql.event |
INFORMATION_SCHEMA.EVENTS |
SHOW
EVENTS |
---|---|---|---|
执行于 | 世界标准时间 | ETZ | ETZ |
启动 | 世界标准时间 | ETZ | ETZ |
完 | 世界标准时间 | ETZ | ETZ |
最后执行 | 世界标准时间 | ETZ | N / A |
创建 | STZ | STZ | N / A |
最后改变了 | STZ | STZ | N / A |
事件调度程序将有关事件执行的信息写入MySQL服务器的错误日志,该信息以错误或警告终止。 有关 示例, 请参见 第24.4.6节“事件调度程序和MySQL权限” 。
要获取有关事件调度程序状态的信息以进行调试和故障排除,请运行 mysqladmin debug (请参见 第4.5.2节“ mysqladmin - 管理MySQL服务器的客户端” ); 运行此命令后,服务器的错误日志包含与事件调度程序相关的输出,类似于此处显示的内容:
活动状态: LLA =最后锁定在LUA =最后解锁时间 WOC =等待条件DL =数据锁定 事件调度程序状态: 状态:INITIALIZED 线程ID:0 LLA:init_scheduler:313 LUA:init_scheduler:318 WOC:没有 工人:0 执行:0 数据已锁定:否 事件队列状态: 元素数:1 数据已锁定:否 试图锁定:没有 LLA:init_queue:148 LUA:init_queue:168 WOC:没有 下一次激活:0000-00-00 00:00:00
在作为事件调度程序执行的事件的一部分发生的语句中,诊断消息(不仅是错误,还包括警告)将写入错误日志,并在Windows上写入应用程序事件日志。
对于频繁执行的事件,可能会导致许多记录的消息。
例如,对于
语句,如果查询不返回任何行,则会发生错误代码为1329的警告(
),并且变量值保持不变。
如果查询返回多行,则发生错误1172(
)。
对于任何一种情况,您都可以通过声明条件处理程序来避免记录警告;
请参见
第13.6.7.2节“DECLARE ... HANDLER语法”
SELECT ... INTO
var_list
No data
Result consisted of more than one row
。
对于可能检索多行的语句,另一种策略是使用
LIMIT
1
将结果集限制为单行。
要启用或禁用已调度事件的执行,必须设置全局
event_scheduler
系统变量的值。
这需要足以设置全局系统变量的权限。
请参见
第5.1.9.1节“系统变量权限”
。
该
EVENT
权限控制事件的创建,修改和删除。
可以使用此权限
GRANT
。
例如,此
GRANT
语句赋予
用户
EVENT
指定的模式
的
特权
:
myschema
jon@ghidora
关于myschema的重大事件。* to jon @ ghidora;
(我们假设此用户帐户已存在,并且我们希望它保持不变。)
要授予此用户
EVENT
对所有模式
的
特权,请使用以下语句:
授予活动*。*至jon @ ghidora;
该
EVENT
特权具有全局或模式级范围。
因此,尝试在单个表上授予它会导致错误,如下所示:
mysql> ERROR 1144(42000):非法GRANT / REVOKE命令; 请GRANT EVENT ON myschema.mytable TO jon@ghidora;
请参阅手册以了解可以使用的权限
重要的是要理解事件是以其定义者的特权执行的,并且它不能执行其定义者没有必要特权的任何操作。
例如,假设
jon@ghidora
有
EVENT
权限
myschema
。
还假设此用户具有
此架构
的
SELECT
权限
myschema
,但没有其他权限。
可以
jon@ghidora
创建一个新事件,例如:
创建事件e_store_ts 按照时间表 每10秒 做 INSERT INTO myschema.mytable VALUES(UNIX_TIMESTAMP());
用户等待一分钟左右,然后执行
SELECT * FROM mytable;
查询,期望在表中看到几个新行。
相反,该表是空的。
由于用户没有
INSERT
相关表
的
权限,因此该事件无效。
如果检查MySQL错误日志(
),您可以看到事件正在执行,但它尝试执行的操作失败:
hostname
.err
2013-09-24T12:41:31.261992Z 25 [ERROR]事件调度程序: [jon @ ghidora] [cookbook.e_store_ts] INSERT命令被拒绝给用户 表'mytable'''jon'@'ghidora' 2013-09-24T12:41:31.262022Z 25 [注意]事件调度程序: [jon @ ghidora]。[myschema.e_store_ts]事件执行失败。 2013-09-24T12:41:41.271796Z 26 [ERROR]事件调度程序: [jon @ ghidora] [cookbook.e_store_ts] INSERT命令被拒绝给用户 表'mytable'''jon'@'ghidora' 2013-09-24T12:41:41.272761Z 26 [注意]事件调度程序: [jon @ ghidora]。[myschema.e_store_ts]事件执行失败。
由于此用户很可能无法访问错误日志,因此可以通过直接执行它来验证事件的操作语句是否有效:
mysql> ERROR 1142(42000):拒绝用户INSERT命令INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP());
表'mytable'''jon'@'ghidora'
检查
INFORMATION_SCHEMA.EVENTS
表显示
e_store_ts
存在并已启用,但其
LAST_EXECUTED
列为
NULL
:
MySQL的>SELECT * FROM INFORMATION_SCHEMA.EVENTS
>WHERE EVENT_NAME='e_store_ts'
>AND EVENT_SCHEMA='myschema'\G
*************************** 1。排******************** ******* EVENT_CATALOG:NULL EVENT_SCHEMA:myschema EVENT_NAME:e_store_ts DEFINER:jon @ ghidora EVENT_BODY:SQL EVENT_DEFINITION:INSERT INTO myschema.mytable VALUES(UNIX_TIMESTAMP()) EVENT_TYPE:RECURRING EXECUTE_AT:NULL INTERVAL_VALUE:5 INTERVAL_FIELD:第二 SQL_MODE:NULL STARTS:0000-00-00 00:00:00 ENDS:0000-00-00 00:00:00 状态:已启用 ON_COMPLETION:NOT PRESERVE 创建时间:2006-02-09 22:36:06 LAST_ALTERED:2006-02-09 22:36:06 LAST_EXECUTED:NULL EVENT_COMMENT: 1排(0.00秒)
要取消该
EVENT
权限,请使用该
REVOKE
语句。
在此示例中,
将从
用户帐户中
删除
EVENT
架构
的
权限
:
myschema
jon@ghidora
在myschema上重播活动。*来自jon @ ghidora;
假设用户
jon@ghidora
已被授予
EVENT
和
INSERT
特权上的
myschema
架构。
然后,此用户创建以下事件:
创建事件e_insert 按照时间表 每7秒 做 INSERT INTO myschema.mytable;
创建此事件后,
root
撤消该
EVENT
权限
jon@ghidora
。
但是,
e_insert
继续执行,
mytable
每7秒
插入一个新行
。
如果
root
发布以下任何一种陈述,情况也是如此:
DROP USER jon@ghidora;
RENAME USER jon@ghidora TO
someotherguy@ghidora;
您可以通过
在发出
或
声明
之前和之后
检查
mysql.event
表(在本节后面讨论)或
INFORMATION_SCHEMA.EVENTS
表(请参见
第25.10节“INFORMATION_SCHEMA事件表”
)
来验证这是否成立
。
DROP
USER
RENAME USER
事件定义存储在
mysql.event
表中。
要删除由其他用户帐户创建的事件,MySQL
root
用户(或具有必要权限的其他用户)可以从此表中删除行。
例如,要删除
e_insert
前面显示
的事件
,
root
可以使用以下语句:
从mysql.event删除 WHERE db ='myschema' AND name ='e_insert';
从
mysql.event
表中
删除行时,匹配事件名称和数据库模式名称非常重要
。
这是因为不同模式中可能存在同名的不同事件。
用户
EVENT
权限存储在
和
表
的
Event_priv
列中
。
在这两种情况下,此列都包含其中一个值'
'或'
'。
'
'是默认值。
仅当该用户具有全局
权限(即,如果使用该权限时
)
,则为
给定用户
设置为'
'
。
对于模式级
权限,
创建一个行
并将该行的
列
设置
为模式的名称,列为
用户名称
的
列,以及
mysql.user
mysql.db
Y
N
N
mysql.user.Event_priv
Y
EVENT
GRANT EVENT ON
*.*
EVENT
GRANT
mysql.db
Db
User
Event_priv
列设置为'
Y
”。
永远不需要直接操作这些表,因为
语句对它们执行所需的操作。
GRANT
EVENT
和
REVOKE EVENT
五个状态变量提供与事件相关的操作的计数(但 不 包括事件执行的语句;请参见 第C.1节“存储程序的限制” )。 这些是:
Com_create_event
:
CREATE
EVENT
自上次服务器重新启动以来执行
的
语句
数
。
Com_alter_event
:
ALTER
EVENT
自上次服务器重新启动以来执行
的
语句
数
。
Com_drop_event
:
DROP
EVENT
自上次服务器重新启动以来执行
的
语句
数
。
Com_show_create_event
:
SHOW CREATE EVENT
自上次服务器重新启动以来执行
的
语句
数
。
Com_show_events
:
SHOW
EVENTS
自上次服务器重新启动以来执行
的
语句
数
。
您可以通过运行语句一次查看所有这些的当前值
SHOW STATUS LIKE
'%event%';
。
MySQL支持视图,包括可更新视图。 视图是存储的查询,在调用时会生成结果集。 视图充当虚拟表。
以下讨论描述了创建和删除视图的语法,并显示了如何使用它们的一些示例。
有关MySQL中视图的一些常见问题的解答,请参见 第A.6节“MySQL 8.0常见问题解答:视图” 。
对视图的使用有一些限制; 请参见 第C.5节“视图限制” 。
该
CREATE VIEW
语句创建一个新视图(请参见
第13.1.23节“创建视图语法”
)。
要更改视图的定义或删除视图,请使用
ALTER
VIEW
(请参见
第13.1.11节“ALTER VIEW语法”
)或
DROP
VIEW
(参见
第13.1.35节“DROP VIEW语法”
)。
可以从多种
SELECT
语句
创建视图
。
它可以引用基表或其他视图。
它可以使用连接
UNION
和子查询。
该
SELECT
甚至不需要引用任何表。
以下示例定义了一个视图,该视图从另一个表中选择两列,以及从这些列计算的表达式:
mysql>CREATE TABLE t (qty INT, price INT);
mysql>INSERT INTO t VALUES(3, 50), (5, 60);
mysql>CREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t;
mysql>SELECT * FROM v;
+ ------ + ------- ------- + + | 数量| 价格| 价值| + ------ + ------- ------- + + | 3 | 50 | 150 | | 5 | 60 | 300 | + ------ + ------- ------- + + MySQL的>SELECT * FROM v WHERE qty = 5;
+ ------ + ------- ------- + + | 数量| 价格| 价值| + ------ + ------- ------- + + | 5 | 60 | 300 | + ------ + ------- ------- + +
可选
ALGORITHM
子句
CREATE VIEW
或是
ALTER
VIEW
标准SQL的MySQL扩展。
它会影响MySQL处理视图的方式。
ALGORITHM
三个值:
MERGE
,
TEMPTABLE
或
UNDEFINED
。
对于
MERGE
,引用视图和视图定义的语句的文本被合并,以便视图定义的部分替换语句的相应部分。
对于
TEMPTABLE
,视图的结果将被检索到临时表中,然后用于执行该语句。
因为
UNDEFINED
,MySQL选择使用哪种算法。
它喜欢
MERGE
在
TEMPTABLE
如果可能的话,因为
MERGE
通常是更高效的并且因为如果使用临时表的视图不能更新。
如果不存在
ALGORITHM
子句,则默认算法由
系统变量
的
derived_merge
标志
值确定
optimizer_switch
。
有关其他讨论,请参见
第8.2.2.4节“使用合并或实现优化派生表,视图引用和公用表表达式”
。
TEMPTABLE
明确
指定的一个原因
是,在创建临时表之后以及在用于完成处理语句之前,可以在基础表上释放锁。
这可能导致比
MERGE
算法
更快的锁定释放,
因此使用该视图的其他客户端不会被阻止。
视图算法有
UNDEFINED
三个原因:
声明中
没有
ALGORITHM
条款
CREATE
VIEW
。
该
CREATE
VIEW
声明有明确的
ALGORITHM = UNDEFINED
条款。
ALGORITHM = MERGE
为只能使用临时表处理的视图指定。
在这种情况下,MySQL会生成警告并将算法设置为
UNDEFINED
。
如前所述,
MERGE
通过将视图定义的相应部分合并到引用视图的语句中来处理。
以下示例简要说明了
MERGE
算法的工作原理。
这些示例假设有一个
v_merge
具有此定义
的视图
:
CREATE ALGORITHM = MERGE VIEW v_merge(vc1,vc2)AS SELECT c1,c2 FROM t WHERE c3> 100;
示例1:假设我们发出以下声明:
SELECT * FROM v_merge;
MySQL处理语句如下:
v_merge
变
t
*
成为
vc1, vc2
,对应于
c1, c2
视图
WHERE
子句已添加
生成的语句将变为:
SELECT c1,c2 FROM t WHERE c3> 100;
示例2:假设我们发出以下声明:
SELECT * FROM v_merge WHERE vc1 <100;
这种说法也同样处理前一个,只是
vc1 < 100
变得
c1 <
100
和视图
WHERE
子句添加到语句
WHERE
使用条款
AND
结缔组织(并添加括号以确保条款的部分用正确的优先顺序执行)。
生成的语句将变为:
SELECT c1,c2 FROM t WHERE(c3> 100)AND(c1 <100);
实际上,要执行的
WHERE
语句具有以下形式
的
子句:
WHERE(选择WHERE)AND(查看WHERE)
如果
MERGE
无法使用
该
算法,则必须使用临时表。
阻止合并的构造与阻止在派生表和公用表表达式中合并的构造相同。
示例是
SELECT
DISTINCT
或
LIMIT
在子查询中。
有关详细信息,请参见
第8.2.2.4节“使用合并或实现优化派生表,视图引用和公用表表达式”
。
某些视图是可更新的,对它们的引用可用于指定要在数据更改语句中更新的表。
也就是说,你可以在语句,如使用它们
UPDATE
,
DELETE
或
INSERT
更新基础表的内容。
派生表和公用表表达式也可以在多表
UPDATE
和
DELETE
语句中
指定
,但只能用于读取数据以指定要更新或删除的行。
通常,视图引用必须是可更新的,这意味着它们可以合并而不是实现。
复合视图具有更复杂的规则。
要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。 还有一些其他构造使视图不可更新。 更具体地说,如果视图包含以下任何内容,则视图不可更新:
视图中生成的列被视为可更新,因为可以为其分配。
但是,如果明确更新此类列,则唯一允许的值为
DEFAULT
。
有关生成的列的信息,请参见
第13.1.20.9节“创建表和生成的列”
。
假设可以使用
MERGE
算法
处理多表视图,有时可以更新多表视图
。
为此,视图必须使用内部联接(不是外部联接或a
UNION
)。
此外,只能更新视图定义中的单个表,因此该
SET
子句必须只列出视图中其中一个表的列。
UNION ALL
即使它们在理论上可以更新
,
也不允许
使用
它们。
关于可插入性(可使用
INSERT
语句
进行更新
),如果可更新视图也满足视图列的这些附加要求,则可插入视图:
必须没有重复的视图列名称。
视图必须包含基表中没有默认值的所有列。
视图列必须是简单的列引用。 它们不能是表达式,例如:
3.14159
col1 + 3
UPPER(COL2)
col3 / col4
(subquery
)
MySQL在
CREATE VIEW
时间
设置了一个标志,称为视图可更新性标志
。
YES
如果
UPDATE
和
DELETE
(和类似操作)对视图合法,则
该标志设置为
(true)
。
否则,该标志设置为
NO
(false)。
表中
的
IS_UPDATABLE
列
INFORMATION_SCHEMA.VIEWS
显示此标志的状态。
这意味着服务器始终知道视图是否可更新。
如果视图是不可更新的,这样的语句
UPDATE
,
DELETE
以及
INSERT
是非法的,将被拒绝。
(即使视图是可更新的,也可能无法插入其中,如本节其他部分所述。)
视图的可更新性可能受
updatable_views_with_limit
系统变量
值的影响
。
请参见
第5.1.8节“服务器系统变量”
。
对于以下讨论,假设存在以下表和视图:
CREATE TABLE t1(x INTEGER); CREATE TABLE t2(c INTEGER); 创建视图vmat AS SELECT SUM(x)AS s FROM t1; 创建视图vup AS SELECT * FROM t2; CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup on vmat.s = vup.c;
INSERT
,
UPDATE
和
DELETE
语句允许如下:
INSERT
:
INSERT
语句
的插入表
可以是合并的视图引用。
如果视图是连接视图,则视图的所有组件都必须是可更新的(未实现)。
对于多表可更新视图,
INSERT
如果它插入到单个表中
,则
可以工作。
此语句无效,因为连接视图的一个组件是不可更新的:
插入vjoin(c)VALUES(1);
本声明有效; 视图不包含实体化组件:
插入vup(c)VALUES(1);
UPDATE
:要在
UPDATE
语句中
更新的一个或多个表
可能是合并的视图引用。
如果视图是连接视图,则视图的至少一个组件必须是可更新的(这与之不同
INSERT
)。
在多表
UPDATE
语句中,语句的更新表引用必须是基表或可更新的视图引用。
非更新表引用可以是物化视图或派生表。
本声明有效;
column
c
来自连接视图的可更新部分:
更新vjoin SET c = c + 1;
该声明无效;
列
x
来自nonupdatable部分:
更新vjoin SET x = x + 1;
本声明有效;
多表的更新表引用
UPDATE
是可更新的视图(
vup
):
UPDATE vup JOIN(SELECT SUM(x)AS s FROM t1)AS dt ON ... SET c = c + 1;
该声明无效; 它尝试更新具体化的派生表:
UPDATE vup JOIN(SELECT SUM(x)AS s FROM t1)AS dt ON ... SET s = s + 1;
DELETE
:要在
DELETE
语句中
删除的一个或多个表
必须是合并视图。
不允许加入视图(这与
INSERT
和
不同
UPDATE
)。
此语句无效,因为视图是连接视图:
删除vjoin WHERE ...;
此语句有效,因为视图是合并(可更新)视图:
删除vup WHERE ...;
此语句有效,因为它从合并(可更新)视图中删除:
删除vup FROM vup JOIN(SELECT SUM(x)AS s FROM t1)AS dt ON ...;
其他讨论和示例如下。
本节中的早期讨论指出,如果并非所有列都是简单的列引用,则视图不可插入(例如,如果它包含表达式或复合表达式的列)。 虽然这样的视图不可插入,但如果只更新非表达式的列,则可以更新。 考虑这个观点:
CREATE VIEW v AS SELECT col1,1 AS col2 FROM t;
此视图不可插入,因为它
col2
是一个表达式。
但是,如果更新没有尝试更新,则可以更新
col2
。
此更新是允许的:
UPDATE v SET col1 = 0;
此更新是不允许的,因为它尝试更新表达式列:
UPDATE v SET col2 = 0;
如果表包含
AUTO_INCREMENT
列,则插入表中不
包含
列的可插入视图
AUTO_INCREMENT
不会更改值
LAST_INSERT_ID()
,因为将默认值插入不属于视图的列的副作用不应该是可见的。
的
WITH CHECK OPTION
条款可以给出一个可更新视图来防止插入到行的量,
WHERE
在该条款
select_statement
是不正确的。
它还会阻止更新
WHERE
子句为true的
行,
但更新会导致它不为true(换句话说,它会阻止可见行更新为不可见的行)。
在
WITH CHECK OPTION
可更新视图
的
子句中,
LOCAL
和
CASCADED
关键字确定在根据另一个视图定义视图时检查测试的范围。
如果没有给出关键字,则默认为
CASCADED
。
WITH CHECK OPTION
测试符合标准:
使用
LOCAL
,查看view
WHERE
子句,然后检查recurses到基础视图并应用相同的规则。
使用
CASCADED
,查看view
WHERE
子句,然后检查recurses到底层视图,添加
WITH CASCADED
CHECK OPTION
它们(为了检查;它们的定义保持不变),并应用相同的规则。
如果没有检查选项,则不检查view
WHERE
子句,然后检查recurses到基础视图,并应用相同的规则。
考虑下表和视图集的定义:
CREATE TABLE t1(INT); CREATE VIEW v1 AS SELECT * FROM t1 WHERE a <2 WITH CHECK OPTION; CREATE VIEW v2 AS SELECT * FROM v1 WHERE a> 0 WITH LOCAL CHECK OPTION; CREATE VIEW v3 AS SELECT * FROM v1 WHERE a> 0 带有级联检查选项;
这里
v2
和
v3
视图是根据另一个视图定义的
v1
。
v2
根据其
LOCAL
检查选项
检查
插入
,然后检查递归到
v1
并再次应用规则。
v1
导致检查失败
的规则
。
检查
v3
也失败:
MySQL的>INSERT INTO v2 VALUES (2);
ERROR 1369(HY000):检查选项失败'test.v2' MySQL的>INSERT INTO v3 VALUES (2);
ERROR 1369(HY000):检查选项失败'test.v3'
有关视图的元数据可以获得如下:
查询
数据库
VIEWS
表
INFORMATION_SCHEMA
。
请参见
第25.36节“INFORMATION_SCHEMA VIEWS表”
。
使用该
SHOW CREATE VIEW
声明。
请参见
第13.7.6.13节“显示创建视图语法”
。
存储的程序(过程,函数,触发器和事件)和视图在使用之前定义,并且在引用时,在确定其特权的安全上下文中执行。
这些权限由其
DEFINER
属性和
SQL SECURITY
特征控制。
所有存储的对象定义都可以包含一个
DEFINER
命名MySQL帐户
的
属性。
如果定义省略了该
DEFINER
属性,则默认定义者是创建该对象的用户。
MySQL使用以下规则来控制用户可以在对象
DEFINER
属性中
指定的帐户
:
如果您具有
SET_USER_ID
或
SUPER
权限,则可以将任何帐户指定为
DEFINER
值,但如果该帐户不存在则会生成警告。
此外,从MySQL 8.0.16开始,要将
DEFINER
存储对象
的
属性
设置
为具有该
SYSTEM_USER
权限
的帐户
,您必须具有该
SYSTEM_USER
权限。
否则,只允许账户是你自己的,无论是字面规定或作为
CURRENT_USER
或
CURRENT_USER()
。
您无法将定义者设置为其他帐户。
使用不存在的
DEFINER
帐户
创建存储对象
可能会产生负面后果:
对于存储例程,如果
SQL SECURITY
值为
DEFINER
但定义者帐户不存在,
则在例程执行时发生错误
。
对于触发器,在帐户确实存在之前触发激活不是一个好主意。 否则,未定义与权限检查有关的行为。
对于事件,如果该帐户不存在,则在事件执行时发生错误。
对于视图,如果
SQL SECURITY
值为
DEFINER
但是定义者帐户不存在,
则在引用视图时会发生错误
。
存储例程(过程和函数)和视图的定义可以包括
SQL SECURITY
具有值的特征
DEFINER
或
INVOKER
指定对象是在定义者还是调用者上下文中执行的特征。
如果定义省略了
SQL SECURITY
特征,则默认为定义上下文。
触发器和事件没有
SQL SECURITY
特征,总是在definer上下文中执行。
服务器根据需要自动调用这些对象,因此没有调用用户。
定义者和调用者安全上下文的不同之处如下:
在定义安全上下文中执行的存储对象使用其
DEFINER
属性
命名的帐户的权限执行
。
这些权限可能与调用用户的权限完全不同。
调用者必须具有引用对象的适当权限(例如,
EXECUTE
调用存储过程或
SELECT
从视图中选择),但在对象执行期间,调用者的权限将被忽略,只有
DEFINER
帐户权限才重要。
如果该
DEFINER
帐户具有很少的权限,则该对象在其可以执行的操作中相应地受到限制。
如果
DEFINER
如果帐户具有高权限(例如
root
帐户),则
无论谁调用它
,该对象都可以执行强大的操作
。
在调用程序安全性上下文中执行的存储例程或视图只能执行调用者具有特权的操作。
DEFINER
在对象执行期间,
该
属性无效。
考虑以下存储过程,声明
SQL SECURITY DEFINER
为在definer安全上下文中执行:
CREATE DEFINER ='admin'@'localhost'PROCEDURE p1() SQL安全定义器 开始 UPDATE t1 SET counter = counter + 1; 结束;
拥有该
EXECUTE
权限的
任何用户都
p1
可以使用
CALL
语句
调用它
。
但是,
p1
执行时,它会在definer安全上下文中执行,因此
'admin'@'localhost'
将使用
DEFINER
属性中
指定的帐户
的权限执行
。
此帐户必须具有
EXECUTE
的权限
p1
,以及在
UPDATE
为表特权
t1
的对象身体内引用。
否则,该过程失败。
现在考虑这个存储过程,
p1
除了它的
SQL
SECURITY
特性是
INVOKER
:
CREATE DEFINER ='admin'@'localhost'PROCEDURE p2() SQL安全调查员 开始 UPDATE t1 SET counter = counter + 1; 结束;
与
调用安全上下文中的执行
不同
p1
,
p2
因此无论
DEFINER
属性值
如何,都可以使用调用用户的特权
。
p2
如果调用者缺少表的
EXECUTE
特权
p2
或
UPDATE
特权,则
失败
t1
。
要最大程度地降低存储对象创建和使用的潜在风险,请遵循以下准则:
对于存储的例程或视图,
SQL SECURITY
INVOKER
尽可能在对象定义中
使用
,以便只有具有适合对象执行的操作的权限的用户才能使用它。
如果在使用具有
SET_USER_ID
or或
SUPER
权限
的帐户时创建definer-context存储对象
,请指定一个显式
DEFINER
属性,该属性命名仅拥有对象执行的操作所需的权限的帐户。
DEFINER
仅在绝对必要时
指定高权限
帐户。
管理员可以
DEFINER
通过不授予用户
SET_USER_ID
或
SUPER
权限
来阻止用户创建指定高权限
帐户的
存储对象
。
应该写入定义者上下文对象,记住他们可能能够访问调用用户没有权限的数据。 在某些情况下,您可以通过不向未授权用户授予特定权限来阻止对这些对象的引用:
但是,触发器和事件不存在这样的控制,因为它们总是在definer上下文中执行。 服务器根据需要自动调用这些对象; 用户不直接引用它们:
触发器通过访问与之关联的表来激活,甚至是没有特殊权限的用户进行的普通表访问。
服务器按计划执行事件。
在这两种情况下,如果
DEFINER
帐户具有高权限,则该对象可能能够执行敏感或危险操作。
如果从创建对象的用户的帐户中撤消创建对象所需的权限,则仍然如此。
管理员应特别注意授予用户对象创建权限。
二进制日志包含有关修改数据库内容的SQL语句的信息。 该信息以 描述修改 的 “ 事件 ” 的形式存储 。 (二进制日志事件与预定事件存储对象不同。)二进制日志有两个重要目的:
对于复制,二进制日志在主复制服务器上用作要发送到从属服务器的语句的记录。 主服务器将其二进制日志中包含的事件发送到其从属服务器,这些服务器执行这些事件以对主服务器上的数据进行相同的更改。 请参见 第17.2节“复制实现” 。
某些数据恢复操作需要使用二进制日志。 还原备份文件后,将重新执行备份后记录的二进制日志中的事件。 这些事件使数据库从备份点更新。 请参见 第7.3.2节“使用备份进行恢复” 。
但是,如果在语句级别进行日志记录,则存储的程序(存储过程和函数,触发器和事件)存在某些二进制日志记录问题:
在某些情况下,语句可能会影响主服务器和从服务器上的不同行集。
在从属服务器上执行的复制语句由具有完全权限的从属SQL线程处理。 程序可以在主服务器和从服务器上遵循不同的执行路径,因此用户可以编写包含危险语句的例程,该例程仅在从服务器上执行,并由具有完全权限的线程处理。
如果修改数据的存储程序是不确定的,则它是不可重复的。 这可能导致主站和从站上的数据不同,或导致恢复的数据与原始数据不同。
本节介绍MySQL如何处理存储程序的二进制日志记录。 它说明了实现对使用存储程序的当前条件,以及您可以采取哪些措施来避免记录日志问题。 它还提供了有关这些条件的原因的其他信息。
通常,当在SQL语句级别(基于语句的二进制日志记录)发生二进制日志记录时,会导致此处描述的问题。
如果使用基于行的二进制日志记录,则日志包含由于执行SQL语句而对各个行所做的更改。
执行例程或触发器时,将记录行更改,而不是进行更改的语句。
对于存储过程,这意味着
CALL
不记录
该
语句。
对于存储的函数,将记录函数内的行更改,而不是函数调用。
对于触发器,将记录触发器所做的行更改。
在从属端,只能看到行更改,而不是存储的程序调用。
混合格式二进制日志记录(
binlog_format=MIXED
)使用基于语句的二进制日志记录,但只保证基于行的二进制日志记录能够产生正确结果的情况除外。
对于混合格式,当存储的函数,存储过程,触发器,事件或预准备语句包含对基于语句的二进制日志记录不安全的任何内容时,整个语句将标记为不安全并以行格式记录。
用于创建和删除过程,函数,触发器和事件的语句始终是安全的,并以语句格式记录。
有关基于行,混合和基于语句的日志记录以及如何确定安全和不安全语句的详细信息,请参见
第17.2.1节“复制格式”
。
除非另有说明,否则此处的备注假定在服务器上启用了二进制日志记录(请参见 第5.4.4节“二进制日志” 。)如果未启用二进制日志,则无法进行复制,二进制日志也不可用于数据恢复。
在MySQL中使用存储函数的条件可以总结如下。 这些条件不适用于存储过程或事件调度程序事件,除非启用了二进制日志记录,否则它们不适用。
要创建或更改存储的函数,
除了
通常需要的权限
或
权限
之外
,还必须具有
SET_USER_ID
或
SUPER
权限
。
(取决于
函数定义中
的
值,
或者
可能需要,无论是否启用二进制日志记录。请参见
第13.1.17节“创建过程和创建函数语法”
。)
CREATE ROUTINE
ALTER ROUTINE
DEFINER
SET_USER_ID
SUPER
创建存储函数时,必须声明它是确定性的或不修改数据。 否则,数据恢复或复制可能不安全。
默认情况下,一个
CREATE
FUNCTION
语句被接受,至少一个
DETERMINISTIC
,
NO SQL
或
READS SQL DATA
必须明确指定。
否则会发生错误:
ERROR 1418(HY000):此函数没有DETERMINISTIC,NO SQL, 或启用声明和二进制日志记录中的READS SQL DATA (你*可能*想要使用安全性较低的log_bin_trust_function_creators 变量)
此功能是确定性的(并且不会修改数据),因此它是安全的:
创建功能f1(i INT) 退货INT 确定性 读取SQL数据 开始 回归我; 结束;
此函数使用
UUID()
,这不是确定性的,因此该函数也不是确定性的并且不安全:
创建功能f2() 返回CHAR(36)CHARACTER SET utf8 开始 RETURN UUID(); 结束;
此功能修改数据,因此可能不安全:
创建功能f3(p_id INT) 退货INT 开始 UPDATE t SET modtime = NOW()WHERE id = p_id; RETURN ROW_COUNT(); 结束;
评估函数的性质是基于
创建者
的
“
诚实
”
。
MySQL不检查声明的函数
DETERMINISTIC
是否没有产生非确定性结果的语句。
当您尝试执行存储函数时,如果
binlog_format=STATEMENT
已设置,则
DETERMINISTIC
必须在函数定义中指定关键字。
如果不是这种情况,则会生成错误并且函数不会运行,除非
log_bin_trust_function_creators=1
指定覆盖此检查(参见下文)。
对于递归函数调用,
DETERMINISTIC
仅在最外层调用时需要
该
关键字。
如果正在使用基于行或混合二进制日志记录,则即使在没有
DETERMINISTIC
关键字的
情况下定义函数,也会接受并复制该语句
。
因为MySQL在创建时不检查函数是否确实是确定性的,所以使用
DETERMINISTIC
关键字
调用存储函数
可能会执行对基于语句的日志记录不安全的操作,或者调用包含不安全语句的函数或过程。
如果在
binlog_format=STATEMENT
设置
时发生这种情况
,则会发出警告消息。
如果正在使用基于行或混合二进制日志记录,则不会发出警告,并且将以基于行的格式复制语句。
要放松函数创建的前述条件(必须具有
SUPER
权限并且必须将函数声明为确定性或不修改数据),请将全局
log_bin_trust_function_creators
系统变量
设置
为1.默认情况下,此变量的值为0,但是你可以改变它:
MySQL的> SET GLOBAL log_bin_trust_function_creators = 1;
您还
--log-bin-trust-function-creators=1
可以在启动服务器时
使用该
选项
设置此变量
。
如果未启用二进制日志记录,
log_bin_trust_function_creators
则不适用。
SUPER
函数创建不需要,除非如前所述,
DEFINER
函数定义中
的
值需要它。
有关可能对复制不安全的内置函数的信息(从而导致使用它们的存储函数也不安全),请参见 第17.4.1节“复制功能和问题” 。
触发器与存储函数类似,因此前面关于函数的注释也适用于具有以下异常的触发器:
CREATE
TRIGGER
没有可选
DETERMINISTIC
特性,因此假定触发器始终是确定性的。
但是,在某些情况下,这种假设可能无效。
例如,该
UUID()
函数是不确定的(并且不会复制)。
在触发器中使用此类功能时要小心。
触发器可以更新表,因此
CREATE
TRIGGER
如果您没有所需的权限,
则会出现类似于存储函数的错误消息
。
在从属端,从属设备使用trigger
DEFINER
属性来确定哪个用户被认为是触发器的创建者。
本节的其余部分提供了有关日志记录实现及其含义的其他详细信息。
除非您对存储的常规使用中当前与日志记录相关的条件的基本原理感兴趣,否则无需阅读它。
此讨论仅适用于基于语句的日志记录,而不适用于基于行的日志记录,但第一项除外:
CREATE
并且
DROP
语句将记录为语句,而不管日志记录模式如何。
服务器写入
CREATE
EVENT
,
CREATE
PROCEDURE
,
CREATE
FUNCTION
,
ALTER
EVENT
,
ALTER
PROCEDURE
,
ALTER
FUNCTION
,
DROP
EVENT
,
DROP PROCEDURE
,和
DROP
FUNCTION
语句二进制日志。
SELECT
如果函数更改数据并且在不会记录的语句中发生,则将
存储的函数调用记录为
语句。
这可以防止因在非记录语句中使用存储函数而导致的数据更改不重复。
例如,
SELECT
语句不会写入二进制日志,但是
SELECT
可能会调用存储的函数进行更改。
要处理此问题,
当给定函数进行更改时,会将语句写入二进制日志。
假设在master上执行以下语句:
SELECT
func_name
()
CREATE FUNCTION f1(INT)RETURNS INT 开始 如果(a <3)那么 插入t2值(a); 万一; 返回0; 结束; CREATE TABLE t1(INT); 插入t1值(1),(2),(3); SELECT f1(a)FROM t1;
当
SELECT
语句执行,函数
f1()
被调用三次。
其中两个调用插入一行,MySQL
SELECT
为每个
调用记录一个
语句。
也就是说,MySQL将以下语句写入二进制日志:
SELECT f1(1); SELECT f1(2);
SELECT
当函数调用导致错误的存储过程时,
服务器还会记录
存储函数调用
的
语句。
在这种情况下,服务器将
SELECT
语句与预期的错误代码一起
写入
日志。
在从属设备上,如果发生相同的错误,那就是预期的结果并继续复制。
否则,复制将停止。
记录存储的函数调用而不是函数执行的语句对复制具有安全隐患,这可能源于两个因素:
函数可以遵循主服务器和从服务器上的不同执行路径。
在从属服务器上执行的语句由具有完全权限的从属SQL线程处理。
这意味着虽然用户必须具有
CREATE ROUTINE
创建函数
的
权限,但是用户可以编写包含危险语句的函数,该函数仅在具有完全特权的线程处理它的从服务器上执行。
例如,如果主服务器和从属服务器的服务器ID值分别为1和2,则主服务器上的用户可以创建和调用不安全的函数
unsafe_func()
,如下所示:
mysql>delimiter //
mysql>CREATE FUNCTION unsafe_func () RETURNS INT
- >BEGIN
- > - > - > - > mysql> mysql>IF @@server_id=2 THEN
dangerous_statement
; END IF;RETURN 1;
END;
//
delimiter ;
INSERT INTO t VALUES(unsafe_func());
该
CREATE
FUNCTION
和
INSERT
语句被写入二进制日志,以便从机将执行它们。
因为从属SQL线程具有完全权限,所以它将执行危险语句。
因此,函数调用对主服务器和从服务器具有不同的影响,并且不是复制安全的。
为了防止启用了二进制日志记录的服务器的这种危险
SUPER
,除了通常
CREATE ROUTINE
所需的权限
之外
,存储的函数创建者必须具有该
特权
。
同样,要使用
ALTER
FUNCTION
,您必须拥有该
SUPER
权限之外的
ALTER ROUTINE
权限。
没有该
SUPER
权限,将发生错误:
错误1419(HY000):您没有SUPER权限 启用二进制日志记录(您*可能*希望使用不太安全的日志 log_bin_trust_function_creators变量)
如果您不希望要求函数创建者拥有该
SUPER
特权(例如,如果
CREATE
ROUTINE
您系统上
具有该
特权的
所有用户
都是有经验的应用程序开发人员),请将全局
log_bin_trust_function_creators
系统变量
设置
为1.您还可以使用该
--log-bin-trust-function-creators=1
选项
设置此变量
启动服务器时。
如果未启用二进制日志记录,
log_bin_trust_function_creators
则不适用。
SUPER
函数创建不需要,除非如前所述,
DEFINER
函数定义中
的
值需要它。
如果执行更新的函数是不确定的,则它不可重复。 这可能会产生两种不良影响:
它会使奴隶与主人不同。
恢复的数据将与原始数据不同。
为了解决这些问题,MySQL强制执行以下要求:在主服务器上,除非您声明函数是确定性的或不修改数据,否则将拒绝创建和更改函数。 这里有两组功能特征:
的
DETERMINISTIC
和
NOT
DETERMINISTIC
特性指示的功能是否总是产生对于给定的输入相同的结果。
默认值是,
NOT DETERMINISTIC
如果没有给出特征。
要声明函数是确定性的,必须
DETERMINISTIC
明确
指定
。
的
CONTAINS SQL
,
NO
SQL
,
READS SQL DATA
,和
MODIFIES SQL DATA
特征提供有关该功能是否读取或写入数据信息。
无论是
NO SQL
或
READS SQL DATA
指示功能不会改变的数据,但由于默认情况下是必须指定的这些明确的一个
CONTAINS
SQL
,如果没有特性给予。
默认情况下,一个
CREATE
FUNCTION
语句被接受,至少一个
DETERMINISTIC
,
NO SQL
或
READS SQL DATA
必须明确指定。
否则会发生错误:
ERROR 1418(HY000):此函数没有DETERMINISTIC,NO SQL, 或启用声明和二进制日志记录中的READS SQL DATA (你*可能*想要使用安全性较低的log_bin_trust_function_creators 变量)
如果设置
log_bin_trust_function_creators
为1,则会删除功能为确定性或不修改数据的要求。
存储过程调用记录在语句级别而不是
CALL
级别。
也就是说,服务器不记录
CALL
语句,它会在实际执行的过程中记录这些语句。
因此,将在从属服务器上观察到主服务器上发生的相同更改。
这可以防止可能由于在不同计算机上具有不同执行路径的过程而导致的问题。
通常,在存储过程中执行的语句将使用与要以独立方式执行的语句相同的规则写入二进制日志。 记录过程语句时需要特别注意,因为过程中的语句执行与非过程上下文中的语句执行不完全相同:
要记录的语句可能包含对本地过程变量的引用。 这些变量不存在于存储过程上下文之外,因此无法从字面上记录引用此类变量的语句。 相反,每个对局部变量的引用都被此构造替换以用于记录目的:
NAME_CONST(var_name
,var_value
)
var_name
是局部变量名称,
var_value
是一个常量,指示变量在记录语句时的值。
NAME_CONST()
具有的价值
var_value
,以及
“
名
”
的
var_name
。
因此,如果直接调用此函数,则会得到如下结果:
MySQL的> SELECT NAME_CONST('myname', 14);
+ -------- +
| myname |
+ -------- +
| 14 |
+ -------- +
NAME_CONST()
允许在从属服务器上执行已记录的独立语句,其效果与在存储过程中在主服务器上执行的原始语句具有相同的效果。
当源列表达式引用局部变量时,
使用
NAME_CONST()
可能导致
CREATE
TABLE
... SELECT
语句出现
问题
。
将这些引用转换为
NAME_CONST()
表达式可能会导致主服务器和从属服务器上的列名不同,或者名称太长而不能成为合法的列标识符。
解决方法是为引用局部变量的列提供别名。
当
myvar
值为1
时,请考虑此语句
:
CREATE TABLE t1 SELECT myvar;
这将改写如下:
CREATE TABLE t1 SELECT NAME_CONST(myvar,1);
要确保主表和从表具有相同的列名,请按以下方式编写语句:
CREATE TABLE t1选择myvar AS myvar;
重写的声明变为:
CREATE TABLE t1 SELECT NAME_CONST(myvar,1)AS myvar;
要记录的语句可能包含对用户定义变量的引用。
为了解决这个问题,MySQL将一条
SET
语句
写入
二进制日志,以确保变量存在于从站上,其值与主站上的值相同。
例如,如果语句引用变量
@my_var
,则该语句将在二进制日志中由以下语句开头,其中
value
是
@my_var
master
的值
:
SET @my_var = value
;
过程调用可以在已提交或回滚的事务中进行。
考虑事务上下文,以便正确复制过程执行的事务方面。
也就是说,服务器日志中发现实际执行和修改数据的过程中这些语句,还记录
BEGIN
,
COMMIT
和
ROLLBACK
语句是必要的。
例如,如果过程仅更新事务表并在回滚的事务中执行,则不会记录这些更新。
如果过程发生在已提交的事务中,
BEGIN
并且
COMMIT
使用更新记录语句。
对于在回滚事务中执行的过程,如果语句以独立方式执行,则使用相同的规则记录其语句:
如果从存储函数中调用
过程,则
不会
在语句级别
将存储过程调用
写入二进制日志。
在这种情况下,唯一记录的是调用函数的语句(如果它发生在记录的语句中)或
DO
语句(如果它发生在未记录的语句中)。
因此,在使用调用过程的存储函数时应该小心,即使该过程本身是安全的。