跳转至内容

MySQL/存储程序

来自 Wikibooks,开放世界中的开放书籍

MySQL 支持一些对 SQL 的过程扩展。通过使用它们,您可以管理控制流、创建循环和使用游标。这些功能允许您创建存储程序,它们可以是三种类型:

  • 触发器 - 在某个事件涉及表之前/之后触发的程序(DELETE、INSERT、UPDATE);
  • 事件 - 定期在一段时间间隔后执行的程序;
  • 存储过程 - 可以通过 CALL SQL 命令调用的程序。

MySQL 的未来版本将支持用其他语言编写的存储程序,而不仅仅是 SQL。您将能够管理新的语言作为 PLUGIN。此外,存储过程将被编译成 C 代码,因此它们将更快。

触发器

[编辑 | 编辑源代码]

管理触发器

[编辑 | 编辑源代码]

触发器在 MySQL 5.0.2 中添加。它们在持久表上工作,但不能与 TEMPORARY 表相关联。

CREATE TRIGGER

[编辑 | 编辑源代码]

创建新的触发器

 CREATE TRIGGER `delete_old` AFTER INSERT ON `articles`
     FOR EACH ROW BEGIN
         DELETE FROM `articles` ORDER BY `id` ASC LIMIT 1
     END

这个示例触发器定义了一个名为 `delete_old` 的存储程序(它是一个简单的 DELETE 语句)。它在将新记录 INSERT 到 `articles` 时自动触发。它是在 INSERT 之后调用,而不是之前。如果单个 INSERT 将多行添加到表中,`delete_old` 将被调用多次。这个想法很简单:当创建新记录时,最旧的记录会被 DELETEd。

触发器可以在某个 SQL 语句之前或之后执行。这很重要,因为触发器可以执行一个或多个激活其他触发器的语句;因此,决定它们的执行顺序可能很重要,以确保数据库的完整性。

触发触发器的语句必须是基本的 DML 命令

  • INSERT,包括 LOAD DATA 和 REPLACE
  • DELETE,包括 REPLACE,但不包括 TRUNCATE
  • UPDATE

特殊情况是 INSERT ... ON DUPLICATE KEY UPDATE。如果执行了 INSERT,则 BEFORE INSERT 和 AFTER INSERT 都会执行。如果 INSERT 未执行,并且因此执行了 UPDATE,则事件顺序如下:BEFORE INSERT、BEFORE UPDATE、AFTER UPDATE。

您也可以使用以下语法指定表名

... ON `my_database`.`my_table` ...

触发器名称在数据库中必须是唯一的。同一个数据库中的两个表不能关联到具有相同名称的两个不同触发器。

与其他 DBMS 和标准 SQL 不同,所有触发器都针对每行触发,并且不能针对每个语句执行。

存储程序必须指定在 BEGIN 和 END 保留字之间。您不能在此处使用动态 SQL(PREPARE 语句);相反,可以使用调用存储过程。如果您只执行一条语句,可以省略 BEGIN 和 END 字。

您可以访问字段的旧值(它在语句执行之前的价值)以及新值(它在语句执行之后的值)。示例

 CREATE TRIGGER `use_values` AFTER INSERT ON `example_tab`
     FOR EACH ROW BEGIN
         UPDATE `changelog` SET `old_value`=OLD.`field1`, `new_value`=NEW.`field1` WHERE `backup_tab`.`id`=`example_tab`.`id`
     END

DROP TRIGGER

[编辑 | 编辑源代码]

要 DROP 触发器,您可以使用以下语法

 DROP TRIGGER `my_trigger`

或者

 DROP TRIGGER `my_database`.`my_trigger`

或者

 DROP TRIGGER IF EXISTS `my_trigger`

要更改现有触发器,您必须 DROP 并重新创建它。

元数据

[编辑 | 编辑源代码]

SHOW CREATE TRIGGER

[编辑 | 编辑源代码]

此命令返回用于创建触发器的 CREATE TRIGGER 语句,以及有关可能影响语句的设置的一些信息。

SHOW CREATE TRIGGER delete_old;
  • 触发器 - 触发器名称
  • sql_mode - 语句执行时的 SQL_MODE 值
  • SQL 原始语句
  • character_set_client
  • collation_connection
  • 数据库排序规则

此语句在 MySQL 5.1 中添加。


SHOW TRIGGERS

[编辑 | 编辑源代码]

如果您想获取当前数据库中所有触发器的列表,可以键入以下内容

 SHOW TRIGGERS

如果您想获取另一个数据库中包含的触发器的列表,可以使用

 SHOW TRIGGERS IN `my_db`
 SHOW TRIGGERS FROM `my_db` -- synonym

如果您想列出名称与 LIKE 表达式匹配的触发器

 SHOW TRIGGERS FROM `my_db` LIKE 'my_%'

更复杂的过滤器

 SHOW TRIGGERS WHERE table='users'

您不能将 LIKE 和 WHERE 同时使用。

此语句返回的列为

  • 触发器 - 触发器的名称
  • 事件 - 触发触发器的 SQL 命令
  • - 与触发器关联的表
  • 语句 - 触发器执行的语句
  • 时间 - BEFORE 或 AFTER
  • 创建 - 它始终为 NULL
  • sql_mode - 创建触发器时设置的 SQL_MODE
  • 定义者 - 创建触发器的用户
  • character_set_client - 创建触发器时的 `character_set_client` 变量的值
  • collation_connection - 创建触发器时的 `collation_connection` 变量的值
  • 数据库排序规则 - 数据库(和触发器)使用的排序规则

INFORMATION_SCHEMA.TRIGGERS

[编辑 | 编辑源代码]

INFORMATION_SCHEMA 虚拟数据库有一个 `TRIGGERS` 表。它具有以下字段

  • TRIGGER_CATALOG - 哪个目录包含触发器(尚未实现)
  • TRIGGER_SCHEMA - 哪个 SCHEMA(DATABASE)包含触发器
  • TRIGGER_NAME - 触发器的名称
  • EVENT_MANIPULATION - INSERT / UPDATE /DELETE
  • EVENT_OBJECT_CATALOG - 尚未实现
  • EVENT_OBJECT_SCHEMA - 包含与触发器关联的表的 SCHEMA
  • EVENT_OBJECT_NAME - 与触发器关联的表的名称
  • ACTION_ORDER - 尚未实现
  • ACTION_CONDITION - 尚未实现
  • ACTION_STATEMENT - 触发器激活时要执行的语句
  • ACTION_ORIENTATION - 尚未实现
  • ACTION_TIMING - BEFORE / AFTER
  • ACTION_REFERENCE_OLD_TABLE - 未实现
  • ACTION_REFERENCE_NEW_TABLE - 未实现
  • ACTION_REFERENCE_OLD_ROW - 未实现
  • ACTION_REFERENCE_NEW_ROW - 未实现
  • 创建 - 创建时间(尚未实现)
  • SQL_MODE - 此触发器执行的有效 SQL_MODE
  • 定义者 - 创建触发器的用户,格式为 'user@host'
  • CHARACTER_SET_CLIENT - 创建触发器时的 `character_set_client` 变量的值
  • COLLATION_CONNECTION - 创建触发器时的 `collation_connection` 变量的值
  • DATABASE_COLLATION - 数据库(和触发器)使用的排序规则

事件也称为计划事件或时间触发器。它们是计划的事件,在特定时间或特定时间间隔内执行。它们类似于 UNIX crontab

一旦事件启动,就必须完全执行。如果在事件执行结束之前重新激活它,则会创建同一个事件的新实例。如果可能发生这种情况,使用锁来确保数据一致性可能是一个好主意。

事件调度器是一个线程,它永久地处于执行状态。它在事件必须启动时启动事件。如果您不需要事件,您可以禁用事件调度器。您可以使用以下选项启动 MySQL 来执行此操作

mysqld --event-scheduler=DISABLED

或者,您可以在 my.cnf 配置文件中添加一行

event_scheduler=DISABLED

如果事件调度器没有被禁用,您将能够在运行时将其打开/关闭。它由一个全局系统变量控制

 SELECT event_scheduler -- values: ON / OFF / DISABLED
 SET GLOBAL event_scheduler = ON
 SET GLOBAL event_scheduler = OFF

如果事件调度器已打开,您可以使用 SHOW PROCESSLIST 检查其状态。它与所有其他线程一样显示。它的 `User` 是 'event_scheduler'。当它处于休眠状态时,`State` 的值为 'Waiting for next activation'。

管理事件

[edit | edit source]

您可以使用 SQL 命令 CREATE EVENT、ALTER EVENT 和 DROP EVENT。

CREATE EVENT

[edit | edit source]

最简单的情况。我们希望在明天执行一个 SQL 命令

 CREATE EVENT `newevent`
   ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY
   DO
     INSERT INTO `mydatabase`.`news` (`title`, `text`) VALUES ('Example!', 'This is not a reale news')

事件名称必须在 "EVENT" 后面指定。

如果您想创建一个仅在特定时间执行一次的任务,则需要使用 AT 子句。如果您不想指定一个绝对时间,而是希望在经过一段时间间隔后执行任务,则 "AT CURRENT_TIMESTAMP + INTERVAL ..." 是一种有用的语法。

如果您想创建一个定期任务(将在固定时间间隔内执行),则需要使用 EVERY 子句

 CREATE EVENT `newevent2`
   ON SCHEDULE EVERY 2 DAY
   DO
     OPTIMIZE TABLE `mydatabase`.`news`

您还可以指定开始时间和/或结束时间。任务将在固定时间间隔内从开始时间执行到结束时间

 CREATE EVENT `newevent2`
   ON SCHEDULE EVERY INTERVAL 1 DAY
   DO
     OPTIMIZE TABLE `mydatabase`.`news`
       STARTS CURRENT_TIMESTAMP + 1 MONTH
       ENDS CURRENT_TIMESTAMP + 3 MONTH

允许的时间单位是

 YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND, YEAR_MONTH, DAY_HOUR, DAY_MINUTE, DAY_SECOND, HOUR_MINUTE, HOUR_SECOND, MINUTE_SECOND

DO 子句指定必须执行哪个语句。

如果一个任务由多个语句组成,则必须使用 BEGIN ... END 语法

  delimiter |
  CREATE EVENT `newevent`
    ON SCHEDULE
      EVERY 1 DAY
    DO
      BEGIN
        DELETE FROM `logs`.`user` WHERE `deletion_time` < CURRENT_TIMESTAMP - 1 YEAR;
        DELETE FROM `logs`.`messages` WHERE `deletion_time` < CURRENT_TIMESTAMP - 1 YEAR;
        UPDATE `logs`.`activity` SET `last_cleanup` = CURRENT_TIMESTAMP;
    END |
  delimiter ;

如果具有相同名称的 EVENT 已经存在,您将收到来自服务器的错误。要抑制错误,您可以使用 IF NOT EXISTS 子句

 CREATE EVENT `newevent2`
   IF NOT EXISTS
   ON SCHEDULE EVERY 2 DAY
   DO
     OPTIMIZE TABLE `mydatabase`.`news`

在 EVENT 过期后(当 AT 子句或 ENDS 子句中指定的的时间戳过期时),MySQL 会默认删除事件,因为它不再有用。您可能希望将其保留下来,以便将来进行 ALTER 并重新激活它,或者只是将其代码存储在某个地方。您可以使用 ON COMPLETION 子句来执行此操作

 CREATE EVENT `newevent2`
   ON SCHEDULE EVERY 2 DAY
   ON COMPLETION PRESERVE
   DO
     OPTIMIZE TABLE `mydatabase`.`news`

或者,您可以明确告诉 MySQL 删除它,即使没有必要

 CREATE EVENT `newevent2`
   ON SCHEDULE EVERY 2 DAY
   ON COMPLETION NOT PRESERVE
   DO
     OPTIMIZE TABLE `mydatabase`.`news`

如果您没有告诉 MySQL 在 EVENT 过期后保留它,但它在创建后立即过期(如果在 AT / ENDS 子句中指定了过去的 TIMESTAMP,则会发生这种情况),服务器会按照您的要求创建并删除它。但是,在这种情况下,它会返回 1588 警告以通知您。

您还可以指定 EVENT 是否必须启用。这可以通过指定 ENABLE、DISABLE 或 DISABLE ON SLAVES(用于在主服务器上执行事件,而不是在从服务器上复制它)来完成。默认情况下,EVENT 已启用。

 CREATE EVENT `newevent2`
   ON SCHEDULE EVERY 2 DAY
   ON COMPLETION NOT PRESERVE
   DISABLE
   DO
     OPTIMIZE TABLE `mydatabase`.`news`

要修改此行为,您将使用 ALTER EVENT。

您可以为 EVENT 指定一个注释。注释的字符限制为 64 个字符。注释必须是文字,而不是表达式。例如

 CREATE EVENT `newevent2`
   ON SCHEDULE EVERY 2 DAY
   ON COMPLETION NOT PRESERVE
   DISABLE
   COMMENT 'let\'s optimize some tables!'
   DO
     OPTIMIZE TABLE `mydatabase`.`news`

您还可以指定在执行 EVENT 时必须使用哪个用户来检查权限。默认情况下,使用 CURRENT_USER。您可以明确指定它

 CREATE DEFINER = CURRENT_USER
   EVENT `newevent2`
   ON SCHEDULE EVERY 2 DAY
   DO
     OPTIMIZE TABLE `mydatabase`.`news`

要指定不同的用户,您必须具有 SUPER 权限。在这种情况下,您必须同时指定用户名和主机

 CREATE DEFINER = 'allen@localhost'
   EVENT `newevent2`
   ON SCHEDULE EVERY 2 DAY
   DO
     OPTIMIZE TABLE `mydatabase`.`news`

ALTER EVENT

[edit | edit source]

ALTER EVENT 语句可用于修改现有 EVENT。

 CREATE EVENT `newevent2`
   ON SCHEDULE EVERY 2 DAY
   ON COMPLETION NOT PRESERVE
   RENAME TO `example_event`
   DISABLE
   COMMENT 'let\'s optimize some tables!'
   DO
     OPTIMIZE TABLE `mydatabase`.`news`

RENAME TO 用于重命名 EVENT。

您只需要指定要更改的子句

 CREATE EVENT `newevent2` ENABLE;

DROP EVENT

[edit | edit source]

您需要 EVENT 权限才能删除事件。要删除事件,您可以输入

 DROP EVENT `event_name`

如果 EVENT 不存在,您会收到 1517 错误。要避免这种情况,您可以使用 IF EXISTS 子句

 DROP EVENT IF EXISTS `event_name`

如果 EVENT 只需执行一次或只是在已知的时间段内执行,MySQL 默认会在 EVENT 过期时自动删除它(请参阅 CREATE EVENT 中的 ON COMPLETE 子句)。

元数据

[edit | edit source]

SHOW CREATE EVENT

[edit | edit source]

此命令返回用于创建触发器的 CREATE EVENT 语句,以及有关可能影响语句的设置的一些信息。

语法

 SHOW CREATE EVENT newevent2;
  • Event - 事件名称。
  • sql_mode - 执行 CREATE EVENT 语句时生效的 SQL 模式。
  • time_zone - 执行语句时使用的时区。
  • Create Event - 用于创建事件的语句。
  • character_set_client
  • collation_connection
  • 数据库排序规则

SHOW EVENTS

[edit | edit source]

该语句显示有关当前数据库或指定数据库中 EVENT 的信息

 SHOW EVENTS

 SHOW EVENTS FROM `my_nice_db`
 SHOW EVENTS IN `my_nice_db` -- synonym

 SHOW EVENTS LIKE 'my_%' -- name starts with 'my_'
 SHOW EVENTS WHERE definer LIKE 'admin@%' -- filters on any field
  • Db 数据库名称。
  • Name 事件名称。
  • Definer 创建 EVENT 的用户以及他使用的主机,格式为 user@host。
  • Time zone EVENT 使用的时区。如果从未更改,它应该为 'SYSTEM',这意味着:服务器的时区。
  • Type 'ONE TIME' 表示仅执行一次的 EVENT,'RECURRING' 表示定期执行的 EVENT。
  • Executed At EVENT 将执行的时刻的 TIMESTAMP。对于递归 EVENT,为 NULL。
  • Interval Value EVENT 执行之间的时间间隔数。请参阅下一个字段。对于仅执行一次的 EVENT,为 NULL。
  • Interval Field EVENT 执行之间等待的时间间隔类型。例如,如果 `Interval Field` 为 'SECOND' 且 `Interval Value` 为 30,则 EVENT 将每 30 秒执行一次。对于仅执行一次的 EVENT,为 NULL。
  • Starts 递归 EVENT 的第一次执行 DATETIME。对于仅执行一次的事件,为 NULL。
  • Ends 递归 EVENT 的最后一次执行 DATETIME。对于仅执行一次的事件,为 NULL。
  • Status ENABLED、DISABLED 或 SLAVESIDE_DISABLED。对于 ENABLED 和 DISABLED,请参阅以上内容。SLAVESIDE_DISABLED 是在 5.1 中添加的,表示 EVENT 在主服务器上已启用,但在从服务器上已禁用。
  • Originator 创建 EVENT 的服务器的 ID。如果它是在当前服务器上创建的,则该值为 0。在 5.1 中添加。
  • character_set_client
  • collation_connection
  • 数据库排序规则

INFORMATION_SCHEMA.EVENTS

[edit | edit source]

INFORMATION_SCHEMA 虚拟数据库有一个 `EVENTS` 表。它是非标准的,是在 5.1 中添加的。EVENTS 具有以下字段

  • EVENT_CATALOG 始终为 NULL(CATALOG 在 MySQL 中未实现)。
  • EVENT_SCHEMA 数据库名称。
  • EVENT_NAME 事件名称。
  • DEFINER 创建 EVENT 的用户以及他使用的主机,格式为 user@host。
  • TIME_ZONE EVENT 使用的时区。如果从未更改,它应该为 'SYSTEM',这意味着:服务器的时区。
  • EVENT_BODY 用于编写将执行的例程的语言。
  • EVENT_DEFINITION 将执行的例程。
  • EVENT_TYPE 'ONE TIME' 表示仅执行一次的 EVENT,'RECURRING' 表示定期执行的 EVENT。
  • EXECUTE_AT EVENT 将执行的时刻的 TIMESTAMP。对于递归 EVENT,为 NULL。
  • INTERVAL_VALUE EVENT 执行之间的时间间隔数。请参阅下一个字段。对于仅执行一次的 EVENT,为 NULL。
  • INTERVAL_FIELD EVENT 执行之间等待的时间间隔类型。例如,如果 `Interval Field` 为 'SECOND' 且 `Interval Value` 为 30,则 EVENT 将每 30 秒执行一次。对于仅执行一次的 EVENT,为 NULL。
  • SQL_MODE 创建 EVENT 时生效的 SQL 模式。
  • STARTS 递归 EVENT 的第一次执行 DATETIME。对于仅执行一次的事件,为 NULL。
  • ENDS 递归 EVENT 的最后一次执行 DATETIME。对于仅执行一次的事件,为 NULL。
  • STATUS ENABLED、DISABLED 或 SLAVESIDE_DISABLED。对于 ENABLED 和 DISABLED,请参阅以上内容。SLAVESIDE_DISABLED 是在 5.1 中添加的,表示 EVENT 在主服务器上已启用,但在从服务器上已禁用。
  • ON_COMPLETION 'NOT PRESERVE'(EVENT 将被删除)或 'PRESERVE'(EVENT 不会被删除)。
  • CREATED 创建时间 DATETIME。
  • LAST_ALTERED 最后一次编辑的 DATETIME。如果 EVENT 从未被修改,则 `LAST_ALTERED` 与 `CREATED` 具有相同的值。
  • LAST_EXECUTED 最后一次执行的 TIMESTAMP。如果 EVENT 从未被执行,则该值为 NULL。
  • EVENT_COMMENT 与 EVENT 关联的注释。如果没有注释,则该值为一个空字符串。
  • ORIGINATOR 创建 EVENT 的服务器的 ID。如果它是在当前服务器上创建的,则该值为 0。在 5.1 中添加。
  • character_set_client
  • collation_connection
  • 数据库排序规则

存储过程

[编辑 | 编辑源代码]

存储例程是使用 SQL(带有一些过程扩展)编写的模块,可以使用 CALL 命令在另一个语句中调用。

如果存储例程返回结果,则称为函数;如果它们不返回任何内容,则称为过程。存储过程不要与在 C 或 LUA 中编写的过程混淆,这些过程可以在 SELECT 语句中使用;存储函数不要与 UDF 混淆,即使它们都是使用 CREATE FUNCTION 语句创建的。

存储例程的优点

[编辑 | 编辑源代码]
  • 它们减少了网络流量:它们可能包含许多语句,但只需要发送一个语句来调用它们。
  • 能够将逻辑保留在数据库中。
  • 可重用模块,可以从外部程序调用,无论它们是用什么语言编写的。
  • 您可以修改存储例程,而无需更改程序。
  • 调用存储例程的用户无需访问它读取/写入的表。
  • 调用存储例程比执行单个语句更快。

管理存储例程

[编辑 | 编辑源代码]

CREATE PROCEDURE

[编辑 | 编辑源代码]
 CREATE DEFINER = `root`@`localhost` PROCEDURE `Module1` ( ) NOT DETERMINISTIC NO SQL SQL SECURITY DEFINER OPTIMIZE TABLE wiki1_page;
 CALL `Module1` ();

DROP PROCEDURE

[编辑 | 编辑源代码]
 DROP PROCEDURE `Module1` ;
 DROP PROCEDURE `Module1` ;
 CREATE DEFINER = `root`@`localhost` PROCEDURE `Module1` ( ) NOT DETERMINISTIC NO SQL SQL SECURITY DEFINER
 BEGIN
  OPTIMIZE TABLE wiki1_page;
  OPTIMIZE TABLE wiki1_user;
 END

元数据

[编辑 | 编辑源代码]

SHOW FUNCTION / PROCEDURE STATUS

[编辑 | 编辑源代码]
 SHOW PROCEDURE STATUS;

SHOW CREATE FUNCTION / PROCEDURE

[编辑 | 编辑源代码]
 SHOW CREATE PROCEDURE Module1;

INFORMATION_SCHEMA.ROUTINES

[编辑 | 编辑源代码]

虚拟数据库 INFORMATION_SCHEMA 包含一个名为 `ROUTINES` 的表,其中包含函数和过程信息。

INFORMATION_SCHEMA.PARAMETERS

[编辑 | 编辑源代码]

此表包含所有存储函数的值。


标准 SQL 的过程扩展

[编辑 | 编辑源代码]

分隔符

[编辑 | 编辑源代码]

MySQL 使用一个字符作为分隔符 - MySQL 知道该字符出现的位置,SQL 语句结束,可能另一个语句开始。默认情况下该字符为 ';'。当您创建一个包含多个语句的存储程序时,您只输入一个语句:CREATE 命令。但是,它在它的主体中包含多个语句,这些语句用 ';' 分隔。在这种情况下,您需要通知 MySQL ';' 不识别 CREATE 语句的结束:您需要另一个分隔符。

在下面的示例中,'|' 用作分隔符

 delimiter |
 CREATE EVENT myevent
    ON SCHEDULE EVERY 1 DAY
    DO
      BEGIN
        TRUNCATE `my_db`.`my_table`;
        TRUNCATE `my_db`.`another_table`;
      END
 delimiter ;

流程控制

[编辑 | 编辑源代码]

关键字是:IF, CASE, ITERATE, LEAVE LOOP, WHILE, REPEAT[1]

DELIMITER $$
CREATE PROCEDURE counter()
  BEGIN
    DECLARE x INT;
    SET x = 1;
    WHILE x  <= 5 DO
      SET  x = x + 1;
    END WHILE;
    SELECT x;  -- 6
  END$$
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE counter2()
  BEGIN
    DECLARE x INT;
    SET x = 1;
    boucle1: LOOP
      SET  x = x + 1;
        IF x > 5 THEN
          LEAVE boucle1;
      END IF;
    END LOOP boucle1;
    SELECT x;  -- 6
  END$$
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE counter3()
  BEGIN
    DECLARE x INT;
    SET x = 1;
    REPEAT
      SET x = x + 1; UNTIL x > 5
    END REPEAT;
    SELECT x;  -- 6
  END$$
DELIMITER ;

游标允许以不同的方式处理每一行,但它会大大降低查询速度。

DELIMITER $$
CREATE PROCEDURE cursor1()
BEGIN
    DECLARE result varchar(100) DEFAULT "";
    DECLARE c1 CURSOR FOR
    	SELECT page_title
    	FROM wiki1.wiki1_page
    	WHERE page_namespace = 0;
    OPEN c1;
    FETCH c1 INTO result;
    CLOSE c1;
    SELECT result;
END;$$
DELIMITER ;

它们应该在循环之前声明和打开,该循环应该以不同的方式处理每条记录。要了解表的结束,我们应该在游标之后创建一个处理程序

-- Concatenate all a table column values on a row
DELIMITER $$
CREATE PROCEDURE cursor2()
BEGIN
    DECLARE result varchar(100) DEFAULT "";
    DECLARE total text DEFAULT "";
    DECLARE done BOOLEAN DEFAULT 0;
    DECLARE c2 CURSOR FOR
    	SELECT page_title
    	FROM wiki1.wiki1_page
    	WHERE page_namespace = 0;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    OPEN c2;
    REPEAT
        FETCH c2 INTO result;
        set total = concat(total, result);
    UNTIL done END REPEAT;
    CLOSE c2;
    SELECT total;
END;$$
DELIMITER ;

错误处理

[编辑 | 编辑源代码]

处理程序声明允许在发生错误时指定处理方法[2]

DECLARE CONTINUE HANDLER FOR SQLEXCEPTION

此外,可以指示错误类型

DECLARE CONTINUE HANDLER FOR SQLSTATE [VALUE] sqlstate_value
DECLARE CONTINUE HANDLER FOR SQLWARNING
DECLARE CONTINUE HANDLER FOR NOT FOUND

参考资料

[编辑 | 编辑源代码]
  1. https://dev.mysqlserver.cn/doc/refman/5.0/en/flow-control-statements.html
  2. https://dev.mysqlserver.cn/doc/refman/5.7/en/declare-handler.html
华夏公益教科书