23章存储程序和视图

目录

23.1定义的存储程序
23使用存储程序(程序和功能)
23.2.1存储程序的语法
23.2.2存储程序和MySQL的特权
23.2.3存储程序的元数据
23.2.4存储过程,函数,触发器,和last_insert_id()
23.3使用触发器
23.3.1 Trigger Syntax和例子
23.3.2触发元数据
23.4使用事件调度器
23.4.1事件调度程序概述
23.4.2事件调度器配置
Event Sootax
23.4.4事件元数据
23.4.5状态事件调度器
23.4.6事件调度器和MySQL的特权
23.5使用视图
23.5.1景语法
23.5.2视图处理算法
23.5.3更新和插入的观点
23.5.4观检查选项条款
23.5.5查看元数据
23.6访问控制用于存储程序和视图
23.7二进制日志存储程序

本章讨论了存储程序和视图,这是数据库对象的SQL代码来定义,存储在稍后执行服务器。

存储程序包括这些对象:

视图是存储在引用产生一个结果集的查询。一个视图作为一个虚拟表。

本章介绍如何使用存储程序和视图。以下部分提供了有关这些对象的相关语句的SQL语法的更多信息:

在MySQL中,元数据更改对象被存储的程序进行检测,使受影响的报表程序时自动重新下执行。有关更多信息,参见8.10.3节,“缓存的准备好的语句和存储的程序”

23.1定义的存储程序

每个存储的程序包含体,由一个SQL语句。这句话可能是一个由若干语句用分号分开复合语句(;)字符。例如,下面的存储过程有一个由一个身体BEGIN ... END块包含SET声明和REPEAT环本身所包含的另一个SET声明:

CREATE PROCEDURE dorepeat(p1 INT)BEGIN  SET @x = 0;  REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT;END;

如果你使用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
    -> //
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;

mysql> CALL dorepeat(1000);
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @x;
+------+
| @x   |
+------+
| 1001 |
+------+
1 row in set (0.00 sec)

你可以重新定义分隔符以外的字符串//,和分隔符可以包含单个字符或多个字符。你应该避免使用反斜杠(\因为这是MySQL)字符转义字符。

以下是一个函数接受一个参数的一个例子,使用一个SQL函数执行一个操作,并返回结果。在这种情况下,没有必要使用delimiter因为函数定义不包含内部语句分隔符:

mysql> CREATE FUNCTION hello (s CHAR(20))
mysql> RETURNS CHAR(50) DETERMINISTIC
    -> RETURN CONCAT('Hello, ',s,'!');
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT hello('world');
+----------------+
| hello('world') |
+----------------+
| Hello, world!  |
+----------------+
1 row in set (0.00 sec)

23使用存储程序(程序和功能)

MySQL支持存储子程序(过程和函数)。存储过程是一组SQL语句可以存储在服务器上。一旦这样做,客户不需要补发个人陈述但可以参考存储程序相反。

存储过程可以在某些情况下特别有用:

  • 当多个客户端应用程序都写在不同的语言或工作在不同的平台,但需要执行相同的数据库操作。

  • 当安全是最重要的。银行,例如,使用存储过程和函数的所有常见的操作。这提供了一个一致的和安全的环境,程序可以确保每个操作正确登录。在这样的设置中,应用程序和用户没有直接访问数据库表,但只能执行特定的存储过程。

存储程序可以提供更好的性能,更少的信息需要在服务器和客户端之间发送。权衡的是,这会增加数据库服务器上的负载,因为更多的工作是在服务器端完成,更是在客户端完成的(应用)的一面。考虑如果许多客户端(如Web服务器)只由一个或几个数据库服务器服务。

存储程序也允许你在数据库服务器上有函数库。这是一个功能的现代应用语言,使设计的内部共享(例如,通过使用类)。使用这些客户端应用程序语言的特点有利于程序员甚至外部数据库使用范围。

MySQL的SQL语法如下:2003年存储例程,并通过IBM的DB2应用。这里描述的所有语法的支持,任何限制和扩展都记录在适当的地方。

额外资源

23.2.1存储程序的语法

存储过程是一个过程或函数。存储程序的创建与CREATE PROCEDURECREATE FUNCTION报表(见第13.1.15,创建过程和创建函数的语法”)。一个程序被调用时使用CALL声明(见第13.2.1,“调用语法”),只能通过返回值使用输出变量。一个函数可以在里面声明,就像任何其他的功能(即通过调用函数的名字),并返回一个标量值。一个存储程序的主体可以使用复合语句(见13.6节“复合语句语法”

存储过程可以减少与DROP PROCEDUREDROP FUNCTION报表(见第13.1.26,“下降过程和降功能语法”蚀变),and with theALTER PROCEDUREALTER FUNCTION报表(见第13.1.6,“改变程序的语法”

存储过程或函数与特定数据库相关的。这有几方面的含义:

  • 当程序被调用时,一个隐含的USE db_name执行(了当程序终止)。USE在存储程序语句是不允许的。

  • 你可以胜任常规名称数据库名称。这可以用来指一个常规,不在当前的数据库。例如,调用一个存储过程p或功能F这是与test数据库,你可以说p()呼叫测试test.f()

  • 当一个数据库删除,所有存储的程序以及与它相关的下降。

存储函数不能是递归的。

递归在存储过程中是允许的但默认情况下禁用。使递归集max_sp_recursion_depth服务器系统变量的值大于零。存储过程递归增加线程的堆栈空间的需求。如果你增加价值max_sp_recursion_depth,可能需要通过增加值增加线程的堆栈大小thread_stack在服务器启动。看到第5.1.7,服务器“系统变量”为更多的信息

MySQL支持非常有用的扩展,使常规使用SELECT报表(即不使用游标或局部变量)在存储过程。建立这样一个查询的结果是直接发送到客户端。多SELECT报表生成多个结果集,所以客户端必须使用支持多个结果集的MySQL客户端库。这意味着客户必须使用一个版本至少MySQL客户端库最近4.1。客户还应当指定client_multi_results当它连接选项。C程序,这是可以做到的mysql_real_connect()C API函数。看到第27.7.7.54,mysql connect()_皇家_”,和第27.7.19,“C API多语句执行的支持”

23.2.2存储程序和MySQL的特权

MySQL授权系统以存储程序考虑如下:

  • 这个CREATE ROUTINE特权是需要创建存储过程。

  • 这个ALTER ROUTINE特权是需要更改或删除存储过程。授予该权限自动例行的创造者如果必要,来自造物主下降时,常规的下降。

  • 这个EXECUTE特权是需要执行存储过程。然而,这种特权是自动授予的一个例程的创造者必要时(在从造物主在常规滴)。另外,默认SQL安全对于一个常规的特性DEFINER,使得用户具有访问数据库的程序来执行日常关联。

  • 如果automatic_sp_privileges系统变为零,这EXECUTEALTER ROUTINE特权不会自动授予和常规的创造者下降。

  • 一个常规的创造者是用来执行帐户CREATE语句,它。这可能不是作为账户命名为相同的定义者在常规的定义

23.2.3存储程序的元数据

关于存储程序元数据可以得到如下:

23.2.4存储过程,函数,触发器,和last_insert_id()

在一个存储程序体(过程或函数)或触发器的值LAST_INSERT_ID()变化同外面执行这些对象身体的语句(见12.14节,“信息功能”)。一个存储过程或触发器对价值的影响LAST_INSERT_ID()这是被下面的语句取决于常规的那种:

  • 如果存储过程执行的语句,变化的价值LAST_INSERT_ID(),改变的值是由陈述,遵循程序调用过。

  • 存储函数和触发器改变的价值,价值恢复时的函数或触发器结束,所以下面看不到改变的价值。

23.3使用触发器

触发器是命名与表相关的数据库对象,并激活时,一个特定的事件发生时的表。触发器的一些用途是执行检查的值插入到表中或在参与更新值进行计算。

一个定义触发器激活当一个表中插入、更新或删除操作,在相关的表中的行。这些行操作触发事件。例如,可以插入的行INSERTLOAD DATA报表,并插入触发器激活每个插入的行。一个触发器可以被设置为激活之前或之后触发事件。例如,你可以有一个触发激活后,每行插入到表的每一行,或者是更新后。

重要

激活仅用SQL语句表的更改MySQL触发器。这包括可更新视图的基表所依据的变化。触发器不激活表由API不发送SQL语句到MySQL服务器的变化。这意味着,触发器不被更新使用NDB应用程序接口

触发器是不被改变的活性INFORMATION_SCHEMAperformance_schema表那些表实际上是视图和触发器是不允许在视图。

以下各节描述创建和删除触发器的语法,展示如何使用它们的例子,并说明如何获取触发元数据。

额外资源

23.3.1 Trigger Syntax和例子

创建一个触发器和删除触发器,使用CREATE TRIGGERDROP TRIGGER声明,描述第13.1.20,创建触发器的语法”,和第13.1.31,删除触发器的语法”

这里是一个简单的例子,将触发一个表,激活INSERT运营触发器作为累加器,总结插入一个表中的列的值。

MySQL的>CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));查询行,0行受影响(0.03秒)MySQL >CREATE TRIGGER ins_sum BEFORE INSERT ON accountFOR EACH ROW SET @sum = @sum + NEW.amount;查询行,0行受影响(0.01秒)

这个CREATE TRIGGER语句创建一个触发器的命名ins_sum这是与account表它还包括条款指定的触发时间、触发事件,和什么时候做触发激活:

  • 关键词BEFORE指示触发动作时间。在这种情况下,触发激活之前的每一行插入表。这里的关键词是允许的其他

  • 关键词INSERT指示触发事件;即操作激活触发器的类型。在示例,INSERT手术导致触发激活。你也可以创建触发器DELETEUPDATE运营

  • the following statementFOR EACH ROW定义触发器主体;即语句每次执行触发激活,其中发生一次为每一行的触发事件的影响。在这个例子中,触发体是一个简单的SET积累到用户变量的值插入到数量专栏声明指柱NEW.amount这意味着的价值amount柱插入新的行

使用触发器,设置蓄能器变为零,执行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 | -----------------------

在这种情况下,价值@sumINSERT语句执行14.98 1937.50 -100,或1852.48

摧毁触发,使用DROP TRIGGER声明。你必须如果触发器默认架构中未指定架构名称:

MySQL的>DROP TRIGGER test.ins_sum;

如果你把一个表,表的任何触发器也下降。

在架构命名空间的名字意味着所有存在的触发器,触发器必须在一个模式都有唯一的名字。在不同架构的触发器可以有相同的名字。

可以定义多个触发器的一个给定的表具有相同的触发事件和动作时间。例如,你可以有两BEFORE UPDATE一个表的触发器。默认情况下,触发器具有相同的触发事件和动作时间激活它们的创建顺序。影响触发命令,指定一个条款后每一行这表明FOLLOWS之前的名称和现有的触发器,也有相同的触发事件和动作时间。与FOLLOWS,新的触发激活现有触发后。与之前,新的触发激活之前已有触发器。

例如,下面的触发器定义中定义了另一个BEFORE INSERT触发的账户

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);
Query OK, 0 rows affected (0.01 sec)

这个触发,ins_transaction,类似于ins_sum但所积累的存款和取款分开。它有一个PRECEDES条款,使其激活之前ins_sum;没有条款,这将激活后ins_sum因为它是创造了后ins_sum

在触发身体的OLD关键词使您能够访问由触发影响列。OLD正在MySQL扩展触发器;他们是不区分大小写。

在一个INSERT触发,只col_name可以使用;没有老排。在一个删除触发,只OLD.col_name可以使用;没有新的行。在一个更新触发,你可以使用OLD.col_name它是指在更新一行的列col_name指的是更新后的行列

一列命名OLD是只读的。你可以把它(如果你有SELECT特权),但不能修改它。你可以参考一个栏目命名如果你有SELECT特权的信息。在一个之前触发,你可以改变它的值SET NEW.col_name = value如果你有UPDATE特权吧。这意味着你可以使用触发器来修改的值插入到一个新的行或用于更新行。(这样的配置声明中没有影响AFTER触发因为行变化已经发生了。)

在一个BEFORE触发器的值的一个AUTO_INCREMENT柱为零,没有序列号,自动生成新的行插入时,实际上。

通过使用BEGIN ... END构建,你可以定义一个触发执行多个语句。在开始块,你也可以使用其他的语法,在存储程序如条件和循环的允许。然而,正如存储程序,如果你使用MySQL程序定义一个触发执行多个语句,有必要重新定义MySQL语句分隔符,你可以使用;在触发器定义语句分隔符。下面的例子说明了这一点。它定义了一个更新触发检查新值被用于更新的每一行,并修改的值是从0到100的范围内。这必须是一个BEFORE触发因为价值在使用前必须检查更新的行:

MySQL的&#62;delimiter //MySQL的&#62;CREATE TRIGGER upd_check BEFORE UPDATE ON accountFOR EACH ROWBEGINIF NEW.amount < 0 THENSET NEW.amount = 0;ELSEIF NEW.amount > 100 THENSET NEW.amount = 100;END IF;END;//MySQL的&#62;delimiter ;

它可以容易地定义存储过程分开,然后调用它的触发使用简单CALL声明。如果你想执行同样的代码在几个触发这也是有利的。

有什么可以出现在语句触发器执行时的局限性:

  • 触发器不能使用CALL语句来调用存储过程,返回数据到客户端或使用动态SQL。(存储过程可以返回数据的触发INOUT参数。)

  • 触发器不能使用语句显式或隐式地开始或结束一个事务,如START TRANSACTIONCOMMIT,或ROLLBACK。(ROLLBACK to SAVEPOINT是允许的因为它不结束交易。)。

参见第1,“限制存储的程序”

MySQL处理错误时触发执行如下:

  • 如果一个BEFORE触发失败,不进行相应的行的操作。

  • BEFORE触发激活的尝试插入或修改的行,不管后来成功的尝试。

  • 一个AFTER如果任何执行触发器之前触发和行操作成功执行。

  • 一个错误在一个BEFORE触发器在触发调用导致整个语句失败的结果。

  • 事务表,一个表失败造成的语句执行的所有更改回滚。一个触发故障导致语句失败,那么触发失败也导致回滚。对于非事务表,如回滚不能做,所以虽然语句失败,任何变化对误差的影响仍在前点。

触发器可以包含直接引用的表的名称,如“触发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);delimiter |CREATE TRIGGER testref BEFORE INSERT ON test1  FOR EACH ROW  BEGIN    INSERT INTO test2 SET a2 = NEW.a1;    DELETE FROM test3 WHERE a3 = NEW.a1;    UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;  END;|delimiter ;INSERT INTO test3 (a3) VALUES  (NULL), (NULL), (NULL), (NULL), (NULL),  (NULL), (NULL), (NULL), (NULL), (NULL);INSERT INTO test4 (a4) VALUES  (0), (0), (0), (0), (0), (0), (0), (0), (0), (0);

假设你插入以下值表test1如图所示:

MySQL的&#62;INSERT INTO test1 VALUES (1), (3), (1), (7), (1), (8), (4), (4);查询行,8行受影响(0.01秒)记录:8份:0警告:0

因此,四个表包含以下数据:

mysql> SELECT * FROM test1;
+------+
| a1   |
+------+
|    1 |
|    3 |
|    1 |
|    7 |
|    1 |
|    8 |
|    4 |
|    4 |
+------+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test2;
+------+
| a2   |
+------+
|    1 |
|    3 |
|    1 |
|    7 |
|    1 |
|    8 |
|    4 |
|    4 |
+------+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test3;
+----+
| a3 |
+----+
|  2 |
|  5 |
|  6 |
|  9 |
| 10 |
+----+
5 rows in set (0.00 sec)

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 rows in set (0.00 sec)

23.3.2触发元数据

元数据的触发器可以得到如下:

23.4使用事件调度器

这个MySQL的事件调度器管理调度和执行的事件,即,根据计划任务。下面的讨论涉及事件调度程序,分为以下几个部分:

存储程序要求events在数据字典表MySQL系统数据库。这张桌子是MySQL 8安装过程中创建的。如果你升级到MySQL 8从较早版本,一定要跑mysql_upgrade要确保你的系统是最新的。看到4.4.5“,”mysql_upgrade检查升级MySQL表”

额外资源

23.4.1事件调度程序概述

MySQL的事件,根据运行计划任务。因此,我们有时称之为预定事件当你创建一个事件,你正在创建一个命名的数据库对象包含一个或多个SQL语句在执行定期或更多,开始和结束在一个特定的日期和时间。从概念上讲,这是类似于Unix的思想crontab(也被称为定时任务)或Windows任务计划程序。

计划任务这类有时也被称为临时触发器,这意味着这些是由时间触发的对象。虽然这基本上是正确的,我们更喜欢使用的术语事件避免与讨论式引发混乱23.3节,“使用触发器”。事件应该更具体地说,不能混淆临时的触发器。而一个触发器是一个数据库对象的语句被执行在响应一个特定类型的事件,发生在一个给定的表,一个(预定)事件是一个对象的陈述是根据指定的时间间隔执行的通道。

虽然没有规定在SQL标准的事件调度,在其他数据库系统中已有先例,你可能会注意到这些实现之间的一些相似性,发现在MySQL服务器。

MySQL的事件有以下主要特点和性能:

  • 在MySQL中,事件是由其名称唯一标识的模式分配的。

  • 一个事件,根据计划执行的具体行动。这次行动由一个SQL语句,它可以在一个复合语句BEGIN ... END如果需要的话(见块13.6节“复合语句语法”)。一个事件的时间可以是一次性复发性。一次性事件执行一次。复发性事件的重复动作在有规律的时间间隔,以及经常性事件的时间表可以被分配一个特定的开始日期和时间,结束日期和时间,同时,或不。(默认情况下,一个经常性事件的时间表就开始了它的创建,并继续下去,直到它被禁用或删除。)

    如果一个重复的事件不会终止在其调度间隔,结果可能是事件的同时运行多个实例。如果这是不可取的,你应该学会一种机制来防止同步实例。例如,您可以使用GET_LOCK()功能,或行或表锁定

  • 用户可以创建、修改、和降预定事件使用SQL语句用于这些目的。语法无效的事件创建和修改报表不适当的错误消息。用户可以在一个事件的行动需要的权限,用户实际上没有陈述。该事件创建或修改语句成功,但事件的行动失败。看到第23.4.6,“事件调度器和MySQL的特权”详情

  • 许多事件的属性可以设置或使用SQL语句修改。这些属性包括事件的名称、时间、持续性(即是否保留其计划期满后),状态(启用或禁用),要执行的动作和模式,它被分配到。看到第13.1.3,“改变的事件语法”

    一个事件的默认定义者是谁创造了事件的用户,除非事件已经改变,在这种情况下,定义是谁发出最后的用户ALTER EVENT声明影响事件。一个事件可以修改任何用户的EVENT在数据库中的事件定义的特权。看到第23.4.6,“事件调度器和MySQL的特权”

  • 一个事件的行动的声明可能包括大多数的SQL语句在存储过程允许。限制,看第1,“限制存储的程序”

23.4.2事件调度器配置

事件是由一个特殊的执行事件调度线程;当我们提及事件调度器,我们指的是这个线程。运行时,事件调度线程和当前的状态可以被用户看到的PROCESS输出中的特权SHOW PROCESSLIST,如讨论如下显示

全球event_scheduler系统变量决定启用事件调度器和服务器上运行。它有这3个值,这里描述的事件调度的影响。默认值是打开(放)

  • ON:事件调度器开始;事件调度线程运行并执行所有预定的事件。

    事件调度器时ON,事件调度线程是输出中的列SHOW PROCESSLIST作为一个守护进程,其状态被表示如下所示:

    MySQL的&#62;SHOW PROCESSLIST\G×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××1。××××××××××××××××××××××××一×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××:1根用户ID:本地主机:nullcommand DB查询时间:0::::processlist状态信息显示为×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××2。××××××××××××××××××××××××一×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××身份证号码:2:事件调度的用户:本地主机:_ DB:时间:3 nullcommand守护进程的状态:等待下一行信息:null2在激活集(0秒)

    事件调度可以通过设置值停止event_scheduler关闭

  • OFF事件调度器停止。事件调度线程不运行,不在输出显示SHOW PROCESSLIST,没有预定的事件被执行。

    当事件调度器停止(event_scheduler关闭),就可以开始通过设置值event_scheduler打开(放)。(见下一条。)

  • DISABLED:这个值呈现事件调度程序不再运行。事件调度器时禁用,事件调度线程不运行(因此不会出现在输出SHOW PROCESSLIST)。此外,事件调度器的状态无法在运行时改变。

如果事件调度器的状态没有设置DISABLEDevent_scheduler可以切换之间打开(放)OFF(使用SET)。也可以使用OFF,和ON设置这个变量的时候。因此,有下列4个语句可用于MySQL客户端打开事件调度器:

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关闭有数字等值的价值,显示event_schedulerSELECTSHOW VARIABLES一直是一个关闭ON,或禁用DISABLED没有数字等效。因为这个原因,打开(放)OFF通常是首选0When setting this变量.

注意,试图设置event_scheduler没有指定它作为一个全局变量导致错误:

mysql<SET @@event_scheduler = OFF;错误1229(hy000):变“event_scheduler &#39;是一个全局变量,应集全球
重要

可以设置事件调度器DISABLED只有在服务器启动。如果event_scheduler打开(放)OFF,你不能把它禁用在运行时。另外,如果事件调度程序设置DISABLED在启动时,你不能改变的价值event_scheduler在运行时

禁用事件调度,使用下列两种方法:

  • 作为一个命令行选项启动服务器时:

    --event-scheduler=DISABLED
    
  • 在服务器配置文件(my.cnf,或my.ini在Windows系统中),包括线,它将由服务器读取(例如,在一个[mysqld]部分):

    event_scheduler=DISABLED

为了使事件调度器,重启服务器不--event-scheduler=DISABLED命令行选项,或删除或注释掉后的行event-scheduler=DISABLED在服务器配置文件,适当的。或者,您可以使用打开(放)(或1)或关闭(或0)的地方禁用值在启动服务器时

笔记

你可以发布事件处理报表时event_scheduler是集禁用。没有警告或错误,在这种情况下产生的(假设语句本身有效)。然而,预定的事件无法执行到该变量设置为ON(或)。一旦这样做,事件调度线程执行所有的事件都满足条件的调度。

开始与MySQL服务器--skip-grant-tables选择的原因event_scheduler要设置禁用,压倒一切的任何其他值设置在命令行或在my.cnfmy.ini(# 26807文件错误)。

SQL语句用于创建,修改,和拖放事件,看第23.4.3”事件语法”

MySQL提供了一个EVENTS表中information_schema数据库这张桌子可以查询以获得预定的事件已在服务器上定义的信息。看到第23.4.4,事件“元数据”,和24.8节,“information_schema事件表”为更多的信息

对于事件调度和MySQL系统的信息,参见第23.4.6,“事件调度器和MySQL的特权”

Event Sootax

MySQL提供与预定的事件几个SQL语句:

23.4.4事件元数据

元数据的事件可以得到如下:

事件调度器时表示

在MySQL,每一届都有一个会话的时区(STZ)。这是会议time_zone值初始化服务器的全球time_zone价值会话开始时可在会话过程中改变。

会话的时区是电流时CREATE EVENTALTER EVENT语句执行的是用来解释事件定义指定的时间。这成为事件的时间区(ETZ);即是用于事件调度,实际上是在事件执行时的时区。

在事件信息的表示mysql.event表的execute_atstarts,和末端时间转换为UTC并随着事件时间区存储。这使得活动执行进行定义无论对服务器的时区和夏令时的任何后续变化的影响。这个last_executed时间也存储在UTC

如果你选择的信息mysql.event,刚才提到的时间为UTC值检索。这些时间也可以选择从中获得INFORMATION_SCHEMA.EVENTS表或SHOW EVENTS,但他们报告为埃茨值。其他时间可以从这些来源显示当一个事件被创建或最后修改;这些都显示为STZ值。下表总结了事件的时间表示法。

价值mysql.eventINFORMATION_SCHEMA.EVENTSSHOW EVENTS
执行UTC埃茨埃茨
开始UTC埃茨埃茨
末端UTC埃茨埃茨
最后执行UTC埃茨N / A
创建STZSTZN / A
最后的改变STZSTZN / A

23.4.5状态事件调度器

事件调度器写关于活动的信息,以执行一个错误或警告的MySQL服务器的错误日志。看到第23.4.6,“事件调度器和MySQL的特权”为例

获得了用于调试和故障排除的目的事件调度器的状态信息,运行mysqladmin DEBUG(见4.5.2“,”mysqladmin客户管理MySQL服务器”);运行此命令后,服务器的错误日志包含有关事件调度器输出,类似于如下所示:

Events status:
LLA = Last Locked At  LUA = Last Unlocked At
WOC = Waiting On Condition  DL = Data Locked

Event scheduler status:
State      : INITIALIZED
Thread id  : 0
LLA        : init_scheduler:313
LUA        : init_scheduler:318
WOC        : NO
Workers    : 0
Executed   : 0
Data locked: NO

Event queue status:
Element count   : 1
Data locked     : NO
Attempting lock : NO
LLA             : init_queue:148
LUA             : init_queue:168
WOC             : NO
Next activation : 0000-00-00 00:00:00

在由事件调度器执行部分事件发生的陈述,诊断信息(不仅错误,而且警告)写入错误日志,并在Windows应用程序事件日志,以。为频繁执行的事件,这导致许多记录的信息是可能的。例如,对于SELECT ... INTO var_list语句,如果查询不返回行,一个错误代码1329警告出现(没有数据),和变量的值保持不变。如果查询返回多行,发生错误1172(Result consisted of more than one row)。任何条件,你可以避免将宣布一个条件处理记录;看第13.6.7.2,”声明…异常处理的语法”。对于可能检索多行语句,另一个策略是使用LIMIT 1限制设置为单排的结果。

23.4.6事件调度器和MySQL的特权

启用或禁用预定活动的执行,有必要建立的全球价值event_scheduler系统变量。这就要求SYSTEM_VARIABLES_ADMINSUPER特权

这个EVENT特权管理创建,修改,删除事件。这种特权可以授予使用GRANT。例如,这GRANT告白EVENT命名模式的特权myschema在用户jon@ghidora

格兰特事件对myschema。*乔恩@姬度拉;

(我们假设这个用户帐户已经存在,而且我们希望它保持不变的话。)

给予相同的用户EVENT在所有模式的特权,使用以下语句:

格兰特事件我们*。* ghidora乔恩”;

这个EVENT特权具有全局或图式水平范围。因此,试图给它一个错误一个表结果如图所示:

MySQL的&#62;GRANT EVENT ON myschema.mytable TO jon@ghidora;错误1144(42000):非法授予/撤销命令;请咨询手册看到这特权可以用

这是理解执行事件的定义者权限的重要,而且它不能执行任何操作,其定义没有所需的权限。例如,假设jon@ghidoraEVENT特权myschema。假设该用户拥有的SELECT特权myschema,但没有其他的特权这一图式。这是可能的jon@ghidora创建一个新的事件,比如这一个:

创建事件e_store_ts进度每10秒做插入myschema.mytable值(unix_timestamp());

用户等待一分钟左右,然后执行SELECT * FROM mytable;查询,期待看到一些新的表行。相反,该表是空的。由于用户没有INSERT问题中的表的特权,事件没有影响。

如果您检查MySQL错误日志(hostname.err),你可以看到事件被执行,但它试图执行行动失败:

2013-09-24t12:41: 31.261992z 25 [错误]事件调度器:[乔恩@姬度拉] [菜谱。e_store_ts ]插入命令拒绝user&#39;jon”@ &#39;ghidora”表“表&#39;2013-09-24t12:41: 31.262022z 25 [注]事件调度器:[乔恩@姬度拉]。[ myschema。e_store_ts ]事件执行失败。2013-09-24t12:41: 41.271796z 26 [错误]事件调度器:[乔恩@姬度拉] [菜谱。e_store_ts ]插入命令拒绝user&#39;jon”@ &#39;ghidora”表“表&#39;2013-09-24t12:41: 41.272761z 26 [注]事件调度器:[乔恩@姬度拉]。[ myschema e_store_ts ]活动执行失败。

因为用户很可能没有访问错误日志,可以验证是否通过执行直接的事件的行动的声明是有效的:

mysql> INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP());
ERROR 1142 (42000): INSERT command denied to user
'jon'@'ghidora' for table 'mytable'

检查的INFORMATION_SCHEMA.EVENTS表格显示_ TS和_商店存在和启用,但其LAST_EXECUTED无效的

mysql> SELECT * FROM INFORMATION_SCHEMA.EVENTS
     >     WHERE EVENT_NAME='e_store_ts'
     >     AND EVENT_SCHEMA='myschema'\G
*************************** 1. row ***************************
   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: SECOND
        SQL_MODE: NULL
          STARTS: 0000-00-00 00:00:00
            ENDS: 0000-00-00 00:00:00
          STATUS: ENABLED
   ON_COMPLETION: NOT PRESERVE
         CREATED: 2006-02-09 22:36:06
    LAST_ALTERED: 2006-02-09 22:36:06
   LAST_EXECUTED: NULL
   EVENT_COMMENT:
1 row in set (0.00 sec)

解除EVENT使用“特权”,REVOKE声明。在这个例子中,该EVENT在图式的特权myschema是从jon@ghidora用户帐户:

撤销事件myschema。*乔恩@姬度拉;
重要

撤销EVENT从一个用户的特权不删除或禁用,可能是由用户创建的任何事件。

一个事件是不会迁移或下降由于重命名或删除用户谁创造了它。

假设用户jon@ghidora已获EVENTINSERT上的特权myschema图式。这个用户就创建了以下事件:

CREATE EVENT e_insert
    ON SCHEDULE
      EVERY 7 SECOND
    DO
      INSERT INTO myschema.mytable;

这一事件后已创建,root撤销EVENT特权乔恩@ ghidora。然而,e_insert继续执行,插入新的一行到空白表Each Seven Seeds .同样的话root发布了上述声明:

  • DROP USER jon@ghidora;

  • RENAME USER jon@ghidora TO someotherguy@ghidora;

你可以证明这是真实的检查mysql.event表格(在本节稍后讨论)或INFORMATION_SCHEMA.EVENTS(这台24.8节,“information_schema事件表”)之前和之后的发行DROP USERRENAME USER声明

事件定义存储在mysql.event表到另一个用户帐户创建一个事件,MySQLroot用户(或其他有权限的用户可以从该表中删除行)。例如,删除事件e_insert先前所示,root可以使用以下语句:

DELETE FROM mysql.event
    WHERE db = 'myschema'
      AND definer = 'jon@ghidora'
      AND name = 'e_insert';

它是对事件的名称匹配非常重要,数据库名称和用户帐户时,删除行的mysql.event表这是因为同一个用户可以创建不同的模式名称相同的不同的事件。

用户EVENT权限存储在_私人事件列的mysql.usermysql.db表在这两种情况下,本栏认为其中的价值Y&#39;或&#39;N&#39;。”N“是默认的mysql.user.event _私人的设置为“Y“对于一个给定的用户只有用户拥有全球EVENT特权(即,如果特权被授予使用Grant Event on * *)。for a级模式EVENT特权,GRANT创建一个行mysql.db并将该行的Db列的架构的名称,该用户列的用户的姓名,和Event_priv柱”Y&#39;。不能有任何需要直接操纵这些表,因为GRANT EVENT撤销事件语句执行所需的操作上。

五状态变量提供事件相关的操作数(但执行的事件的陈述;看第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%';

23.5使用视图

MySQL支持的观点,包括可更新视图。视图是存储,在调用的时候产生一个结果集的查询。一个视图作为一个虚拟表。

下面的讨论描述创建和删除视图的语法,并举例说明如何使用它们。

额外资源

23.5.1景语法

这个CREATE VIEW语句创建新的视图(参见第13.1.21,”创建视图的语法”)。改变视图的定义或删除视图,使用ALTER VIEW(见第13.1.10,“改变视图语法”),或DROP VIEW(见第13.1.32,“删除视图的语法”

一个视图可以从多种创建SELECT声明.它可以指基表或其他视图。它可以使用联接,UNION和子查询。theSELECT甚至不需要参考任何表。下面的示例定义一个视图,选择从另一桌两列,以及从这些列的计算表达式:

MySQL的&#62;CREATE TABLE t (qty INT, price INT);MySQL的&#62;INSERT INTO t VALUES(3, 50), (5, 60);MySQL的&#62;CREATE VIEW v AS SELECT qty, price, qty*price AS value FROM t;MySQL的&#62;SELECT * FROM v;------ ------- ------- |数量|价格|价值| ------ ------- ------- |三|五十| 150 | |五|六十| 300 | ------ ------- ------- MySQL &#62;SELECT * FROM v WHERE qty = 5;------ ------- ------- |数量|价格|价值| ------ ------- ------- |五|六十| 300 | ------ ------- -------

23.5.2视图处理算法

可选ALGORITHM条款CREATE VIEWALTER VIEW是一个mysql扩展标准的SQL。它影响到MySQL的过程观。算法以三值:MERGE诱人的,或UNDEFINED

  • MERGE一个声明,指的是视图和视图定义的文本进行合并,使得零件的视图定义的语句替换相应部分。

  • TEMPTABLE从结果来看,检索到一个临时表,然后用于执行该语句。

  • UNDEFINED选择使用哪种算法,MySQL。它喜欢合并结束TEMPTABLE如果可能的话,因为合并通常是更有效的,因为如果一个临时表使用一个视图不可更新的。

  • 如果没有ALGORITHM子句,默认算法是由价值决定的derived_merge旗的optimizer_switch系统变量。For Additional discussion,see第8.2.2.3,“优化派生表、视图的引用,和公用表表达式”

一个原因说明TEMPTABLE明确的是,锁可以在临时表创建基础表发布和使用前处理完声明。这可能会导致更快的锁定释放比合并算法使其他客户使用视图不会被长。

一个视图的算法可UNDEFINED有三个原因:

  • ALGORITHM条款中存在的CREATE VIEW声明

  • 这个CREATE VIEW有一个显式声明ALGORITHM = UNDEFINED条款.

  • ALGORITHM = MERGE是一个视图可以只处理一个临时表规定。在这种情况下,MySQL会生成一条警告,并设置算法未定义

作为上述earlier,MERGE通过合并视图定义相应的零部件进入声明,指观处理。下面的例子简要说明合并算法。的例子,假设有一个视图v_merge有这样的定义:

CREATE ALGORITHM = MERGE VIEW v_merge (vc1, vc2) ASSELECT 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;

例:假设我们发布这个声明:

SELECT * FROM v_merge WHERE vc1 < 100;

这一声明是处理类似于前一个,除了vc1 < 100成为c1 < 100与视图WHERE子句添加到报表哪里从句的使用AND结缔组织(和括号添加以确保该条款的部分是执行正确的优先)。由此产生的语句是:

SELECT c1, c2 FROM t WHERE (c3 > 100) AND (c1 < 100);

有效的语句有WHERE形态:本条款

在(选择)和(视图)

如果MERGE算法不能使用,必须使用临时表。结构,防止合并相同防止派生表、公用表表达式合并。实例选择不同的LIMIT在子查询。详情见第8.2.2.3,“优化派生表、视图的引用,和公用表表达式”

23.5.3更新和插入的观点

一些视图是可更新的,对它们的引用可以用来指定要更新的表中数据变化报表。那就是,你可以使用它们的报表等UPDATEDELETE,或INSERT更新基础表的内容。派生表、公用表表达式也可以在多个表中指定的UPDATEDELETE报表,但只能用于读取数据指定要更新或删除的行。一般来说,视图的引用必须是可更新的,这意味着他们可以合并和不实。复合视图有更复杂的规则。

一个视图是可更新的,必须有一个一对一的关系排在视图和基础表中的行。也有一些其他的结构,使视图nonupdatable。更具体的,如果它包含下列视图是不可更新的:

  • 聚合函数和窗口函数(SUM()MIN()MAX()COUNT()我知道,和四)

  • DISTINCT

  • GROUP BY

  • HAVING

  • UNIONUNION ALL

  • 选择列表中的子查询

    独立的子查询选择列表中的失败INSERT,但都好UPDATEDELETE。相关子查询的选择列表中,没有数据变化报表允许。

  • 某些连接(见附加在本节稍后加入讨论)

  • 参考在nonupdatable观FROM条款

  • 子查询中的WHERE条款,是指一个表中条款

  • 仅指文字值(在这种情况下,没有基本表的更新)

  • ALGORITHM = TEMPTABLE(使用一个临时表总是认为nonupdatable)

  • 多个引用任何一个基表列(失败INSERT,好了UPDATEDELETE

一个生成的列在视图是可更新的因为它可以给它。但是,如果这样的更新列明确,唯一被允许的值DEFAULT。有关生成的列的信息,参见第13.1.18.8,“创建表和生成的列”

对于多表视图是可更新,它有时是可能的,假设它可以处理的MERGE算法.对于这个工作,视图必须使用内部联接(不是外部联接或UNION)。另外,在视图定义中只有一个表可以被更新,所以配置条款名称仅列必须从一个视图中的表。视图的使用UNION ALL不允许他们虽然在理论上可能更新。

关于嵌入性(被更新与INSERT语句),如果它也满足这些额外的要求可更新视图的视图列插入:

  • 不能有重复的视图列的名字。

  • 视图必须包含在基表中,没有默认值的所有列。

  • 视图的列必须是简单的列引用。他们不能表达,如这些:

    3.14159
    col1 + 3
    UPPER(col2)
    col3 / col4
    (subquery)
    

MySQL设置标志,称为可更新视图的旗帜,在CREATE VIEW时间标志设置为(真的)如果UPDATEDELETE(和类似的操作)是法律的观点。否则,标志设置为(假)。这个IS_UPDATABLE列在INFORMATION_SCHEMA.VIEWS表格显示此标志的状态。这意味着服务器总是知道一个视图是可更新的。

如果一个视图是不可更新的,这样的陈述UPDATEDELETE,和INSERT是非法的,被拒绝了。(注意:即使一个视图是可更新的,它可能无法插入,本节中描述的其他地方)

视图的更新可以通过值的影响updatable_views_with_limit系统变量。见第5.1.7,服务器“系统变量”

在下面的讨论中,假定这些表和视图的存在:

CREATE TABLE t1 (x INTEGER);
CREATE TABLE t2 (c INTEGER);
CREATE VIEW vmat AS SELECT SUM(x) AS s FROM t1;
CREATE VIEW vup AS SELECT * FROM t2;
CREATE VIEW vjoin AS SELECT * FROM vmat JOIN vup ON vmat.s=vup.c;

INSERTUPDATE,和DELETE语句允许如下:

  • INSERT:一个插入表格INSERT语句可以查看参考,合并。如果视图是一个连接视图,该视图的所有组件必须是可更新的(不实)。对于多表可更新视图,INSERT如果插入一个表的工作。

    由于连接视图组件nonupdatable这个声明是无效的:

    INSERT INTO vjoin (c) VALUES (1);
    

    这种说法是正确的;视图不包含物化成分:

    INSERT INTO vup (c) VALUES (1);
    
  • UPDATE:表或表被更新的UPDATE语句可以被合并视图引用。如果一个视图是一个连接视图,视图中的至少一个组件必须更新(这不同于INSERT

    在一个多表UPDATE声明,更新表引用的语句必须是可更新的视图引用基表或。nonupdated表引用可以物化视图或派生表。

    这种说法是正确的;柱c从连接查看更新的部分:

    UPDATE vjoin SET c=c+1;

    这一声明是无效的;柱x从nonupdatable部分:

    UPDATE vjoin SET x=x+1;

    这种说法是正确的;对多表更新的参考表UPDATE是一个可更新的视图(哥伦比亚):

    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声明必须合并的看法。加入的观点都是不允许的(这不同于INSERTUPDATE

    因为视图是一个连接视图这个声明是无效的:

    DELETE vjoin WHERE ...;
    

    这种说法是正确的因为视图合并(更新)的观点:

    DELETE vup WHERE ...;
    

    这一声明是有效的因为它会删除从合并的观点(更新):

    DELETE 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柱,插到桌子上的一个可插入的观点,不包括汽车列不变化的价值LAST_INSERT_ID(),因为插入默认值为列的视图部分的副作用应该是不可见的。

23.5.4观检查选项条款

这个WITH CHECK OPTION条款可以考虑可更新视图以防止插入行的哪里子句中select_statement是不是真的。它还可以防止更新行的哪里条款是真实的但更新会导致它不是真的(换句话说,它可以防止可见的行被更新为不可见行)。

在一个WITH CHECK OPTION可更新视图的条款,当地CASCADED关键词确定测试范围视图时,在另一个视图的定义。当没有关键字,默认是级联

WITH CHECK OPTION测试标准:

  • LOCAL,观哪里条款检查,然后检查递归相关观点和适用相同的规则。

  • CASCADED,观哪里条款检查,然后检查递归到视图,添加WITH CASCADED CHECK OPTION他们(对检查;目的定义保持不变),并应用相同的规则。

  • 带检查选项,视图WHERE条款没有被检查,然后检查递归到视图,并将相同的规则。

考虑下面的定义表和视图集:

CREATE TABLE t1 (a 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
WITH CASCADED CHECK OPTION;

这里的v2V3的观点是在另一个视图的定义,v1

插入v2检查其当地检查选项,然后检查递归到v1和规则适用了。规则V1导致检查失败。的检查v3也没有:

MySQL的&#62;INSERT INTO v2 VALUES (2);错误1369年(hy000):检查选项失败的试验。v2&#39;mysql &#62;INSERT INTO v3 VALUES (2);错误1369(hy000):检查选项失败的试验。V3”

23.5.5查看元数据

元数据视图可以得到如下:

23.6访问控制用于存储程序和视图

存储程序和视图定义之前使用,当引用,执行在一个安全的背景下,决定了他们的特权。这些特权是控制自己DEFINER属性,并且,如果有一个,他们SQL安全特征

所有存储的程序(程序,函数,触发器,视图和事件),可以有一个DEFINER属性名称MySQL账户。如果定义者属性是省略了存储程序或视图定义,默认账户是谁创建该对象的用户。

此外,存储程序(程序和功能)和视图可以有一个SQL SECURITY有价值的特性定义者INVOKER指定对象是否执行在定义或调用上下文。如果SQL安全特点是省略,默认是定义上下文。

触发器和事件没有SQL SECURITY在定义中执行的特点和总。服务器调用这些对象会自动为必要,所以没有调用用户。

Definer和卡尔安全上下文差异如下:

  • 一个存储程序或视图中定义的安全上下文中执行与执行的帐户的权限命名的DEFINER属性。这些权限可以在调用用户完全不同。调用方必须具有适当的权限对象的引用(例如,EXECUTE调用一个存储过程或SELECT从视图中选择),但对象执行时,调用者的权限被忽略,只有定义者帐户权限问题。如果该帐户没有权限,对象是相对有限的操作可以执行。如果DEFINER帐户是高特权(如root帐户),对象可以执行强大的业务不管是谁调用它

  • 一个存储程序或视图执行调用者的安全上下文可以执行操作的调用有特权。这个DEFINER属性可以指定,但没有效果,执行调用上下文对象。

考虑下面的存储过程:

CREATE DEFINER = 'admin'@'localhost' PROCEDURE p1()
SQL SECURITY DEFINER
BEGIN
  UPDATE t1 SET counter = counter + 1;
END;

任何用户谁有EXECUTE特权P1可以使用一个CALL声明。然而,当P1执行,它在定义的安全上下文中执行,这样的特权'admin'@'localhost',账户名为“在定义者属性。这个帐号必须have theEXECUTE特权P1以及UPDATE表的特权T1。。。。。。。否则,该程序fails。

现在考虑这个存储过程,这是相同的p1除了其SQL安全特点是INVOKER

CREATE DEFINER = 'admin'@'localhost' PROCEDURE p2()SQL SECURITY INVOKERBEGIN  UPDATE t1 SET counter = counter + 1;END;

p2,不像P1Expert in援引Security Context .的DEFINER属性是不相关的,P2执行与执行用户的特权。p2如果调用失败,缺乏EXECUTE特权P2UPDATE表的特权T1

MySQL使用下面的规则来控制账户,用户可以指定一个对象DEFINER属性:

  • 你可以指定一个DEFINER价值以外的自己的帐户,如果你有SET_USER_IDSUPER特权

  • 如果你没有SET_USER_IDSUPER特权,只有合法的用户价值的是你自己的帐户,指定从字面上或用CURRENT_USER。不能设置定义到其他帐户。

为了减少存储程序和视图的创建和使用的潜在风险,遵循这些指导方针:

  • 用于存储程序或视图,使用SQL SECURITY INVOKER在定义对象时,可能使它只能通过适当的用户权限的操作对象进行。

  • 如果你创建定义上下文存储程序或观点而使用一个帐户,有SET_USER_IDSUPER特权,指定一个明确的定义者属性名称的帐户只拥有特权的对象上执行所需的操作。指定一个高度特权DEFINER账户只有在绝对必要的时候。

  • 管理员可以阻止用户指定高度特权DEFINER账户不给予他们的SET_USER_IDSUPER特权

  • 定义上下文对象应写记住他们可能能够访问数据,调用用户没有权限。在某些情况下,你可以通过不允许未经授权的用户的特定权限防止引用这些对象:

    • 存储过程或函数不能引用的用户谁没有EXECUTE特权吧

    • 一种观点是不能由用户没有适当的特权,它引用(SELECT选择它,INSERT插入,等等)

    然而,不存在这样的控制触发器,因为用户不直接参考。在定义触发器总是执行上下文和被访问的表与它相关的激活,甚至普通的表没有特殊权限的用户访问。如果DEFINER帐户是高特权,触发器可以执行敏感或危险作业。这是真的如果SET_USER_ID(或SUPER)和TRIGGER需要创建触发器的权限是由用户创造它的人的账户撤销。管理者应特别注意授予用户权限的组合。

23.7二进制日志存储程序

二进制日志包含SQL语句修改数据库内容的信息。这种信息存储的形式事件描述的修改。二进制日志有两个重要的目的:

  • 为复制,二进制日志是用于主复制服务器作为一个被发送到从服务器的报表记录。主服务器发送包含在二进制日志的奴隶的事件,并执行这些事件作出同样的数据改变了主。看到第17.2节,“复制执行”

  • 某些数据恢复操作需要使用二进制日志。在备份文件还原,在二进制日志进行备份后进行记录的事件重新执行。这些事件带来的数据库目前的备份点。看到7.3.2节,“使用备份恢复”

然而,如果记录发生在语句层面,有一定的二进制日志问题方面的程序存储(存储过程和函数、触发器和事件):

  • 在某些情况下,声明可能会影响不同的主人和奴隶的行。

  • 复制语句执行一个奴隶的奴隶SQL线程处理,具有充分的特权。这是可能的一个程序,遵循不同的执行路径,在主从服务器,这样用户可以写一个程序含有危险的声明,将只执行的奴隶,它是由一个线程拥有完全权限处理。

  • 如果一个存储程序,修改数据是不确定的,它是不可重复的。这会导致主从不同的数据,或导致恢复的数据与原始数据。

本节介绍了MySQL如何处理二进制日志存储程序。指出当前的条件下,对存储的程序执行使用的地方,你可以做些什么来避免测井问题。它还提供了关于这些情况的原因的更多信息。

总的来说,这里所描述的问题,结果在SQL语句级的二进制日志时发生(基于二进制日志语句)。如果你使用基于行的二进制日志,日志包含了各行由于执行SQL语句的变化。当例程或触发器执行,行更改记录,不是陈述做出的改变。对于存储过程,这意味着CALL声明中没有记录。存储功能,在功能行更改登录,没有函数调用。触发器,触发了连续变化的记录。在奴隶的一面,看到的都是行的变化,不是存储程序调用。

混合格式的二进制日志(binlog_format=MIXED)采用基于二进制日志声明,除的情况下,只有基于行的二进制日志是保证产生正确的结果。混合格式,当一个存储功能,存储过程,触发器,事件,或事先准备好的声明中包含的任何不安全的基于二进制日志声明,整个声明标记为不安全的行格式和记录。语句用于创建和删除过程,函数,触发器和事件永远都是安全的,并记录在表格式。为更多的信息关于排为主,混合,和基于语句的日志,以及如何安全的和不安全的确定语句,看17.2.1,“复制格式”

除非另有说明,这里的言论认为二进制日志服务器上启用(见5.4.4节,“二进制日志”。)如果二进制日志未启用,复制是不可能的,也可用于数据恢复的二进制日志。

在MySQL中存储功能的使用条件如下。这些条件并不适用于存储过程或事件的调度事件,他们不除非启用应用二进制日志。

  • 创建或更改存储功能,你必须有SET_USER_IDSUPER特权,另外的CREATE ROUTINEALTER ROUTINE特权,通常需要。(根据定义者在函数定义中的价值,SET_USER_IDSUPER可能会被要求无论是否启用了二进制日志。看到第13.1.15,创建过程和创建函数的语法”。)

  • 当你创建一个存储功能,你必须声明,这是确定的,它不修改数据。否则,它可能不安全的数据恢复或复制。

    默认情况下,一个CREATE FUNCTION声明被接受,至少一个确定性NO SQL,或读取SQL数据必须显式指定。否则会发生错误:

    ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL,
    or READS SQL DATA in its declaration and binary logging is enabled
    (you *might* want to use the less safe log_bin_trust_function_creators
    variable)
    

    这个函数是确定性的(和不修改数据),所以它是安全的:

    CREATE FUNCTION f1(i INT)
    RETURNS INT
    DETERMINISTIC
    READS SQL DATA
    BEGIN
      RETURN i;
    END;
    

    这个函数使用UUID(),这是不确定的,所以功能也是不确定的,是不安全的:

    创建功能f2()返回char(36)字符集utf8begin返回uuid();结束;

    这个函数修改数据,所以它可能不安全:

    CREATE FUNCTION f3(p_id INT)
    RETURNS INT
    BEGIN
      UPDATE t SET modtime = NOW() WHERE id = p_id;
      RETURN ROW_COUNT();
    END;
    

    对一个函数的性质的评估是基于诚实造物主的。MySQL不检查一个函数声明DETERMINISTIC是免费的,产生不确定的结果的陈述。

  • 当您尝试执行存储功能,如果binlog_format=STATEMENT设置的确定性关键词必须在函数定义中指定的。如果不是这样的话,一个错误的产生和功能不运行,除非log_bin_trust_function_creators=1指定重写此检查(见下文)。递归函数调用,这确定性关键词是需要在外面的电话只有。如果基于行的或混合的二进制日志中,声明是接受,即使没有定义复制功能DETERMINISTIC关键词

  • 因为MySQL不检查如果一个函数在创建时真的是确定性的,与一个存储函数的调用DETERMINISTIC关键词可能进行一种基于语句的记录不安全行为,或调用一个函数或含有不安全的报表程序。如果这发生在binlog_format=STATEMENT是集,发出警告消息。如果基于行的或混合的二进制日志中,没有发出警告,并声明是基于行的格式复制。

  • 放松前条件函数创建的(你必须有SUPER特权和一个函数必须声明确定的或不修改数据),设置全局log_bin_trust_function_creators系统变量为1。默认情况下,该变量的值为0,但你可以改变它,像这样:

    MySQL的&#62;SET GLOBAL log_bin_trust_function_creators = 1;

    你也可以设置这个变量的使用--log-bin-trust-function-creators=1当服务器启动the option。

    如果没有启用二进制日志,log_bin_trust_function_creators不适用SUPER不需要函数创建的除非,如前所述,该定义者在函数定义的值需要它。

  • 关于内置函数,可以安全的用于复制(从而导致存储功能使用它们是不安全的为好),看17.4.1节,“复制的特点和问题”

触发器是类似的存储功能,所以上述言论的功能也适用于触发有以下例外:CREATE TRIGGER没有一个可选的确定性的特点,所以触发器是总是确定的。然而,这种假设可能会在某些情况下是无效的。例如,在UUID()函数是不确定的(不重复)。是关于触发等功能小心使用。

触发器可以更新表,所以错误消息类似于存储功能发生CREATE TRIGGER如果没有所需的权限。对奴隶,奴隶使用触发器定义者属性来确定哪些用户被认为是触发的创造者。

本节的其余部分提供了有关采伐实施及其影响的更多细节。你不需要读它,除非你是感兴趣的背景对当前测井相关条件对存储程序使用的基本原理。这个讨论仅适用于基于语句的记录,而不是基于行的记录,与第一项例外:CREATE报表记录报表的记录模式。

  • 服务器写CREATE EVENTCREATE PROCEDURECREATE FUNCTIONALTER EVENTALTER PROCEDUREALTER FUNCTIONDROP EVENTDROP PROCEDURE,和DROP FUNCTION报表的二进制日志

  • 存储函数调用记录为SELECT声明如果函数变化的数据和发生在一个声明,否则不会被记录。这可以防止数据的变化,导致使用的nonlogged语句在存储函数非重复性。例如,SELECT陈述并非写入二进制日志,但SELECT可能会调用一个存储功能使得变化。为了解决这个问题,一选择func_name()语句写入二进制日志时给定的函数进行修改。假设以下语句在主执行:

    CREATE FUNCTION f1(a INT) RETURNS INTBEGIN  IF (a < 3) THEN    INSERT INTO t2 VALUES (a);  END IF;  RETURN 0;END;CREATE TABLE t1 (a INT);INSERT INTO t1 VALUES (1),(2),(3);SELECT f1(a) FROM t1;

    SELECT语句执行的功能,f1()调用三次。其中两个调用中插入一行,和MySQL的日志SELECT他们每一个语句。那是,MySQL写下面的二进制日志:

    选择F1(1);(2)选择F1;

    服务器也记录SELECT声明一个存储函数调用时函数调用存储过程引起的误差。在这种情况下,服务器将SELECT语句日志随着预期的错误代码。对奴隶,如果同样的错误发生,这是预期的结果和复制下去。否则,复制停止。

  • 记录存储函数调用而不是语句的执行功能有复制安全的涵义,它产生于两个因素:

    • 这可能是一个函数遵循不同的执行路径,在主从服务器。

    • 在从执行的语句的SQL线程奴隶有完整的权限处理。

    其含义是,虽然用户必须有CREATE ROUTINE特权创建功能,用户可以编写一个函数含有危险的声明,将只执行的奴隶,它是由一个线程拥有完全权限处理。例如,如果主、从服务器1和2服务器ID值,分别为一个主服务器上的用户可以创建和调用一个不安全的功能不安全_ func()如下:

    mysql> delimiter //
    mysql> CREATE FUNCTION unsafe_func () RETURNS INT
        -> BEGIN
        ->   IF @@server_id=2 THEN dangerous_statement; END IF;
        ->   RETURN 1;
        -> END;
        -> //
    mysql> delimiter ;
    mysql> INSERT INTO t VALUES(unsafe_func());
    

    这个CREATE FUNCTIONINSERT语句写入二进制日志,所以奴隶将执行。因为从SQL线程有充分的特权,它将执行危险的声明。因此,函数调用对主人和奴隶的不同影响和复制是不安全的。

    为防止这种危险,二进制日志服务器启用,创作者必须有存储功能SUPER特权,除了通常的CREATE ROUTINE特权是必需的。同样,使用ALTER FUNCTION,你必须有SUPER除了特权ALTER ROUTINEprivilege。没有SUPER特权,将发生错误:

    错误1419(hy000):你没有超级特权和启用了日志记录(你可能想使用更少的safelog_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不需要函数创建的除非,如前所述,该定义者在函数定义的值需要它。

  • 如果一个函数的执行,更新是不确定的,它是不可重复的。这可以有两个不良影响:

    • 它将使一个奴隶不同于主。

    • 恢复数据将不同于原始数据。

    针对这些问题,MySQL执行以下要求:在主服务器上,一个函数创建和更改除非你声明一个函数是确定的或不修改数据拒绝。两套功能特点适用于这里:

    • 这个DETERMINISTIC不确定性特征表明一个函数是否会产生同样的结果对于给定的输入。默认值是NOT DETERMINISTIC如果没有特色了。声明一个函数是确定性的,你必须指定确定性明确地.

    • 这个CONTAINS SQL没有SQLREADS SQL DATA,和修改SQL数据特点提供函数是否读取或写入的数据信息。无论是NO SQL读取SQL数据表示一个函数不更改数据,但是您必须指定一个明确的违约是因为CONTAINS SQL如果没有特色了

    默认情况下,一个CREATE FUNCTION声明被接受,至少一个确定性NO SQL,或读取SQL数据必须显式指定。否则会发生错误:

    ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL,
    or READS SQL DATA in its declaration and binary logging is enabled
    (you *might* want to use the less safe log_bin_trust_function_creators
    variable)
    

    如果你设置log_bin_trust_function_creators1,功能是确定的或不修改数据的需求下降。

  • 存储过程调用记录在语句层面而不是在CALL水平。即,服务器不记录CALL语句,它记录这些语句在程序实际执行。因此,在主发生同样的变化将从服务器的观察。这可以防止可能导致具有不同的执行路径不同机器上的程序问题。

    一般来说,在存储过程执行的语句都使用相同的规则,将二进制日志是在独立的方式执行的语句。一些特殊的照顾,因为当测井程序语句语句执行过程中不太一样nonprocedure语境:

    • 一种说法是记录可能包含本地过程变量的引用。这些变量并不存在外部存储过程的背景下,这样的声明,指的是这样一个变量无法记录上。相反,每一个局部变量的引用替换为本构建测井的目的:

      NAME_CONST(var_name, var_value)
      

      var_name是局部变量的名字,和var_value是一个常数表明变量在时间记录表的价值。NAME_CONST()有一个值var_value,和一个姓名属于var_name。因此,如果你直接调用这个函数,你得到这样的结果:

      MySQL的&#62;SELECT NAME_CONST('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 SELECT myvar AS myvar;
      

      改写后的语句是:

      CREATE TABLE t1 SELECT NAME_CONST(myvar, 1) AS myvar;
      
    • 一种说法是记录可能包含用户定义的变量的引用。为了解决这个问题,MySQL写SET声明的二进制日志以确保变量的存在具有相同价值的奴隶对主人。例如,如果一个语句是指一个变量“我是_,声明之前将二进制日志中,用下面的语句,其中value是价值“我是_在主:

      SET @my_var = value;
      
    • 过程调用可以在提交或回滚事务发生。事务上下文是占使程序执行的事务方面是正确复制。即,服务器日志中那些语句在程序实际执行和修改数据,并记录BEGINCOMMIT,和ROLLBACK声明是必要的。例如,如果一个程序只更新事务表,是在一个事务回滚执行这些更新没有登录。如果过程发生在一个已提交的事务,BEGINCOMMIT报表的更新记录。一个程序执行在回滚事务,其报表登录使用相同的规则,如果声明是在独立的方式执行的申请:

      • 事务表更新不登录

      • 以非事务表的更新日志因为回滚不取消。

      • 一个混合的事务和非事务表的更新记录包围BEGINROLLBACK因此,奴隶也会做出相同的改变和回滚作为主人。

  • 存储过程调用写入二进制日志在语句级如果过程是在存储函数调用。在这种情况下,唯一的记录是语句调用函数(如果它发生在一个声明,或登录)DO声明(如果它发生在一个声明中没有记录)。为此,应在存储函数,调用程序的使用行使,即使程序本身是不安全的。