复合语句

标签

[begin_label:] BEGIN
    [statement_list]
END [end_label]

[begin_label:] LOOP
    statement_list
END LOOP [end_label]

[begin_label:] REPEAT
    statement_list
UNTIL search_condition
END REPEAT [end_label]

[begin_label:] WHILE search_condition DO
    statement_list
END WHILE [end_label]
  • begin ... endlooprepeatwhile允许使用标签
  • 标签的规则如下:
    • begin_label后面必须跟着:

    • begin_label可以不带end_label,如果end_label存在,则其必须与begin_label一样

      create procedure Fun0()
      begin
      end;
      
      create procedure Fun1()
      szn : begin
      end szn;
      
      create procedure Fun2()
      szn : begin
      end;
      
    • end_label若存在,必须匹配begin_label

    • 标签最多可以包含16个字符

begin ... end

[begin_label:] BEGIN
    [statement_list]
END [end_label]
  • begin ... end用于编写复合语句,其可以出现在
    • 存储过程stored procedures
    • 函数functions
    • 触发器triggers
    • 事件evnets
  • begin ... end是可以嵌套的

declare

  • declare语句可以用于定义:
    • 本地变量
    • conditions
    • handlers
    • 游标
  • declare只能在begin ... end中使用,且必须位于任何其他语句之前
  • declare的顺序:
    • 变量和conditions
    • 游标
    • handler

存储程序中的变量

  • 使用declare定义本地变量

    DECLARE var_name [, var_name] ... type [DEFAULT value]
    
    • 上述语句在存储程序中定义本地变量
    • 可以包含default赋予变量默认值,这个默认值可以是一个表达式
    • 若无default则变量的默认值是null
    • 本地变量的生命周期在定义其的begin ... end块内
    delimiter $$
    
    create procedure Fun()
    begin
      declare a int default 10;
      select a;
    
      begin 
          select a;
      end;
    
      begin 
          declare a int default 20;
          select a;
      end;
    
    end $$
    
    delimiter ;
    
    call Fun();
    /*
    a
    10
    
    a
    10
    
    a
    20
    */
    
  • 使用set设置本地变量

  • 将查询结果赋值给本地变量

    • select ... into var_list

    • 打开游标,使用fetch ... into var_list

流程控制语句

  • mysql支持存储程序中使用ifcaseiterateleaveloopwhilerepeat
  • 函数中支持return
  • 不支持for

case

#值匹配
CASE case_value
    WHEN when_value THEN statement_list
    [WHEN when_value THEN statement_list] ...
    [ELSE statement_list]
END CASE
#条件匹配
CASE
    WHEN search_condition THEN statement_list
    [WHEN search_condition THEN statement_list] ...
    [ELSE statement_list]
END CASE
  • case匹配失败,且不包含else子句,则会报错
  • 处理when没有匹配的情况,此时else可以包含一个空的begin ... end
delimiter $$

create procedure Fun(in v0 int, out v1 int, out v2 int)
begin
    case
        when v0 > 0 then
            set v1 = v0 + 1;
            set v2 = v0 + 2;
        when v0 < 0 then
            set v1 = v0 - 1;
            set v2 = v0 - 2;
        else
            set v1 = v0;
            set v2 = v0;
    end case;
end $$

delimiter ;

call Fun(-1, @a0, @a1);
call Fun(1, @b0, @b1);
call Fun(0, @c0, @c1);
select @a0, @a1, @b0, @b1, @c0, @c1;
/*
@a0     @a1     @b0     @b1     @c0     @c1
-2      -3      2       3       0       0
*/
delimiter $$

create procedure Fun(in v0 int, out v1 int, out v2 int)
begin
    case v0
        when 1 then
            set v1 = v0 + 1;
            set v2 = v0 + 2;
        when -1 then
            set v1 = v0 - 1;
            set v2 = v0 - 2;
        else
            set v1 = v0;
            set v2 = v0;
    end case;
end $$

delimiter ;

call Fun(-1, @a0, @a1);
call Fun(1, @b0, @b1);
call Fun(0, @c0, @c1);
select @a0, @a1, @b0, @b1, @c0, @c1;
/*
@a0     @a1     @b0     @b1     @c0     @c1
-2      -3      2       3       0       0
*/
delimiter $$

create procedure Fun(in v0 int)
begin
    case
        when v0 > 0 then
            begin 
            end;
        else
            begin 
            end;
    end case;
end $$

delimiter ;

call Fun(1);

if

IF search_condition THEN statement_list
    [ELSEIF search_condition THEN statement_list] ...
    [ELSE statement_list]
END IF
delimiter $$

create procedure Fun(in v0 int, out v1 int, out v2 int)
begin
    if v0 > 0 then 
        set v1 = v0 + 1;
        set v2 = v0 + 2;
    elseif v0 < 0 then 
        set v1 = v0 - 1;
        set v2 = v0 - 2;
    else 
        set v1 = v0;
        set v2 = v0;
    end if;
end $$

delimiter ;

call Fun(-1, @a0, @a1);
call Fun(1, @b0, @b1);
call Fun(0, @c0, @c1);
select @a0, @a1, @b0, @b1, @c0, @c1;
/*
@a0     @a1     @b0     @b1     @c0     @c1
-2      -3      2       3       0       0
*/

iterate

ITERATE label
  • 只能出现在looprepeatwhile中,表示再次开始循环

leave

LEAVE label
  • 退出具有指定标签的控制
  • 可以在begin ... endlooprepeatwhile中使用

loop

[begin_label:] LOOP
    statement_list
END LOOP [end_label]
  • loop实现循环,通常通过leave来退出循环,在函数中也可以使用return来退出
delimiter $$

create procedure Fun()
begin
    declare v int default 3;
    
    szn:loop
        select v;
        set v = v - 1;
        
        if v > 0 then 
            iterate szn;
        end if;
        
    leave szn;
    end loop;
    
end $$

delimiter ;

call Fun();
/*
v
3

v
2

v
1
*/
drop procedure if exists Fun;

delimiter $$

create procedure Fun()
begin
    declare v int default 3;

    szn:loop
        select v;
        set v = v - 1;
    end loop;

end $$

delimiter ;

call Fun();
#死循环,将一直进行输出

repeat

[begin_label:] REPEAT
    statement_list
UNTIL search_condition
END REPEAT [end_label]
delimiter $$

create procedure Fun()
begin
    declare v int default 3;
    
    repeat 
        select v;
        set v = v - 1;
    until v < 0 #注意,此处不能有;
    end repeat;
    
end $$

delimiter ;

call Fun();
/*
v
3

v
2

v
1
*/

return

RETURN expr
  • 用于终止函数的执行,并将返回值返回给函数调用者
  • 函数中至少要有一个return语句
  • 存储过程、触发器、事件中不应该使用return

while

[begin_label:] WHILE search_condition DO
    statement_list
END WHILE [end_label]
delimiter $$

create procedure Fun()
begin
    declare v int default 3;
 
    while v > 0 do 
        select v;
        set v = v - 1;
    end while;
    
end $$

delimiter ;

call Fun();
/*
v
3

v
2

v
1
*/

游标

#声明一个游标,并将其与一个select关联
DECLARE cursor_name CURSOR FOR select_statement

#打开已声明的游标
OPEN cursor_name

#获取下一行数据
FETCH [[NEXT] FROM] cursor_name INTO var_name [, var_name] ...

#关闭游标
CLOSE cursor_name
  • mysql支持在stored programs中使用游标,游标具有以下属性

    • Asensitive: The server may or may not make a copy of its result table
    • Read only: Not updatable
    • Nonscrollable: Can be traversed only in one direction and cannot skip rows
  • 已打开的游标,使用fetch来获取关联的select的下一行数据

    • If no more rows are available, a No Data condition occurs with SQLSTATE value '02000'. To detect this condition, you can set up a handler for it (or for a NOT FOUND condition)
  • 只有已经被打开的游标,才能被关闭。如果游标没有显式关闭,则会在begin ... end块末尾自动关闭

  • 服务器端游标允许在服务器端生成结果集,并只传输客户端请求的行。服务器端游标被实现为内部的临时表,当其大小小于max_heap_table_sizetmp_table_size的最小值时,此临时表为memory table,否则是myisam table,所以对于大型结果集,使用游标可能会很慢

    show variables like 'max_heap_table_size'
    #16MB
    
    show variables like 'tmp_table_size'
    #39MB
    
    show variables like 'version'
    #8.0.17
    

游标使用示例

create table t(v0 int, name varchar(100));
insert into t(v0, name) values(0, "0"), (1, "1"), (2, "2"), (3, "3");
drop procedure if exists Fun;

delimiter $$

create procedure Fun(out vOut int)
begin 
    declare d int default false;
    declare v int;
    declare n varchar(100);
    
    declare c cursor for select v0, name from t;
    declare continue handler for not found set d = true;
    
    open c;

    create table if not exists tOut (name varchar(100));
    truncate tOut;

    set vOut = 0;
    
    szn:while true do
        fetch c into v, n;
        
        if d then 
            leave szn;
        end if;

        if v % 2 then 
            set vOut = vOut + v;
            insert into tOut(name) values(n);
        end if;
    end while;
    
    close c;
end $$

delimiter ;

set @v = 0;
call Fun(@v);
select @v;
/*
@v
4
*/
select * from tOut;
/*
name
1
3
*/

条件(Condition) 处理(Handling)

  • stored program执行中可能会出现需要特殊处理(Handle)的条件(Condition),例如退出当前程序块或继续执行。handler可以应付如下Condition
    • 普通条件,比如警告和异常
    • 指定的条件,比如一个具体的错误代号。指定的条件可以命名,并且在handler中进行引用

条件声明

DECLARE condition_name CONDITION FOR condition_value

condition_value: {
    mysql_error_code
  | SQLSTATE [VALUE] sqlstate_value
}
  • declare ... condition语句声明了一个错误情况,并进行命名,这个名字可以在declare ... handler中进行引用

  • 上述condition_value具有以下形式:

    • mysql_error_code:整数数字,指示mysql错误码。不应该为0,因为0代表正确

    • SQLSTATE [VALUE] sqlstate_value:5个字符组成的字符串,指示mysql错误,不要使用00开头的值,因为其代表正确

      • mysql_error_codeSQLSTATE [VALUE] sqlstate_value 可参考 https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html

        select * from tNotExists
        #error 1146 (42S02):table 'd0.tNotExists' does not exist
        /*
        则:
        mysql_error_code = 1146
        SQLSTATE [VALUE] sqlstate_value = 42S02
        */
        
  • 使用命名的条件,使得程序更加的清晰可读。两种条件声明示例:

    drop procedure if exists Fun;
    
    delimiter $$
    
    create procedure Fun()
    begin 
    
      declare table_is_not_exist_0 condition for 1146;
      declare table_is_not_exist_1 condition for sqlstate '42S02';
    
    end $$
    
    delimiter ;
    

声明处理

DECLARE handler_action HANDLER
    FOR condition_value [, condition_value] ...
    statement

handler_action: {
    CONTINUE
  | EXIT
  | UNDO
}

condition_value: {
    mysql_error_code
  | SQLSTATE [VALUE] sqlstate_value
  | condition_name
  | SQLWARNING
  | NOT FOUND
  | SQLEXCEPTION
}
  • declare ... handle语句声明了一个handle,用于指定处理一个或多个情况。如果其中的一个情况发生,那么指定的statement将被执行,其中statement可以是set var_name = value这样的简单语句,也可以是begin ... end这样的复合语句

  • handler_action表示statement执行后的动作,如下:

    • condition:继续执行当前程序
    • exit:表示退出声明handle时所处的begin ... end语句块
    • undo:不支持
  • condition_value表示用于激活handle的指定条件或者是一类条件:

    • mysql_error_codemysql错误码
    • SQLSTATE [VALUE] sqlstate_value:5个字符组成的字符串,指示mysql错误
    • condition_name:声明的条件名称
    • SQLWARNING:以01开头的sqlstate的简写
    • NOT FOUND:以02开头的sqlstate的简写。在游标上下文中,如果没有跟多行可用,会触发此错误
    • SQLEXCEPTION:不以000102开头的sqlstate的简写
  • Condition被触发,且没有对应的handle与之关联,则

    • 对于SQLEXCEPTION

      • stored program在触发对应条件处退出,就像在此处有定义了exithandler一样
      • 如果程序是在另一个stored program中调用,那么由调用者的handle的规则去处理
      drop procedure if exists Fun;
      delimiter $$
      
      create procedure Fun()
      begin 
      
          signal sqlstate '03001';
          select "szn";
      
      end $$
      
      delimiter ;
      call Fun();
      #error 1644(03001): unhandled user-defined exception condition
      
      drop procedure if exists Fun0;
      drop procedure if exists Fun1;
      delimiter $$
      
      create procedure Fun0()
      begin 
      
          signal sqlstate '03001';
          select "Fun0";
      
      end $$
      
      create procedure Fun1()
      begin 
      
          declare continue handler for sqlexception begin end;
          call Fun0();
          select "szn";
      
      end $$
      
      delimiter ;
      call Fun1();
      /*
      szn
      ---
      szn
      */
      
      call Fun0();
      #error 1644(03001): unhandled user-defined exception condition
      
    • 对于SQLWARNING,程序将继续执行,就像此处有定义了continuehandler一样

      drop procedure if exists Fun;
      delimiter $$
      
      create procedure Fun()
      begin 
      
          signal sqlstate '01001';
          select "szn";
      
      end $$
      
      delimiter ;
      call Fun();
      /*
      szn
      ---
      szn
      */
      
    • 对于NOT FOUND

      • 如果条件是被正常触发的,则表现为continue
      • 如果条件是被signal或者resignal触发的,则表现为exit
      drop procedure if exists Fun;
      delimiter $$
      
      create procedure Fun()
      begin 
      
          declare v0 int;
          declare c cursor for select v from t;
      
          drop table if exists t;
          create table t(v int);
          insert into t(v) values(1);
      
          open c;
          fetch c into v0;
          select v0;
      
          fetch c into v0;
          select "szn";
          
          close c;
      
      end $$
      
      delimiter ;
      call Fun();
      /*
      v0
      1
      
      error 1329 (02000): no data - zero rows fetched, selectd, or processed
      */
      
      drop procedure if exists Fun;
      delimiter $$
      
      create procedure Fun()
      begin 
      
          signal sqlstate '02000';
          select "szn";
      
      end $$
      
      delimiter ;
      call Fun();
      #error1643 (02000): unhandled user-defined not found condition
      
  • 外部label不能在handler中使用。所以statement中,不允许使用leave或者iterate,来引用外层的label

    drop procedure if exists Fun;
    
    delimiter $$
    
    create procedure Fun()
    szn : begin 
    
      declare continue handler for not found 
      begin
      #   leave szn;      非法
      #   iterate szn;    非法
      end;
    
    end $$
    
    delimiter ;
    
  • 示例:

    drop procedure if exists Fun;
    
    delimiter $$
    
    create procedure Fun()
    begin 
    
      declare table_is_not_exist_0 condition for 1146;
    
      declare continue handler for table_is_not_exist_0
      begin
          select "Error : Table Not Found";
      end;
    
      select * from t;
    
    end $$
    
    delimiter ;
    
    call Fun();
    /*
    若表t存在,则输出表t的内容,否则输出Error : Table Not Found
    */
    
    

Signal

SIGNAL condition_value
    [SET signal_information_item
    [, signal_information_item] ...]

condition_value: {
    SQLSTATE [VALUE] sqlstate_value
  | condition_name
}

signal_information_item:
    condition_information_item_name = simple_value_specification

condition_information_item_name: {
    CLASS_ORIGIN
  | SUBCLASS_ORIGIN
  | MESSAGE_TEXT
  | MYSQL_ERRNO
  | CONSTRAINT_CATALOG
  | CONSTRAINT_SCHEMA
  | CONSTRAINT_NAME
  | CATALOG_NAME
  | SCHEMA_NAME
  | TABLE_NAME
  | COLUMN_NAME
  | CURSOR_NAME
}
  • signal用于主动引发一个错误,并提供给handler、外部程序、客户端

  • signal中的condition_value指明了返回的错误,condition_value可以是以下值:

    • sqlstate值,包含5个字符
    • 使用declare ... condition声明的condition的名字。此condition必须以sqlstate定义,而不能以mysql_error_code定义(参见前面的condition的两种定义方式)
  • signal声明语句中包含可选的多个set语句

    • condition_information_item_name列表中包含了可以被set的值

      Item Name             Definition
      ---------             ----------
      CLASS_ORIGIN          VARCHAR(64)
      SUBCLASS_ORIGIN       VARCHAR(64)
      CONSTRAINT_CATALOG    VARCHAR(64)
      CONSTRAINT_SCHEMA     VARCHAR(64)
      CONSTRAINT_NAME       VARCHAR(64)
      CATALOG_NAME          VARCHAR(64)
      SCHEMA_NAME           VARCHAR(64)
      TABLE_NAME            VARCHAR(64)
      COLUMN_NAME           VARCHAR(64)
      CURSOR_NAME           VARCHAR(64)
      MESSAGE_TEXT          VARCHAR(128)
      MYSQL_ERRNO           SMALLINT UNSIGNED
      
      • 以上使用的字符集是utf8
      • 以上值不允许设为null
    • 同一个变量不允许set多次

    • simple_value_specification即被设置的变量的值,可以是

      • stored procedure的参数
      • 函数的参数
      • stored procedure中定义的本地变量
      • 用户定义的变量
      • 系统变量
      • 字面量
      drop procedure if exists Fun;
      
      delimiter $$
      
      create procedure Fun(in v int)
      begin 
          
          declare table_is_not_exist condition for sqlstate '42S02';
      
          if v = 1 then 
              signal sqlstate '42S03';
              
          elseif v = 2 then 
              signal sqlstate '42S04'
                  set column_name = '1',
                      table_name = '2';
                          
          else 
              signal table_is_not_exist;
              
          end if;
      
      end $$
      
      delimiter ;
      
      call Fun(0);
      #error 1644 (42S02):Unhandled user-defined exception condition
      
      call Fun(1);
      #error 1644 (42S03):Unhandled user-defined exception condition
      
      call Fun(2);
      #error 1644 (42S04):Unhandled user-defined exception condition
      
  • signal一般在stored program中使用,但他是mysql扩展,也可以在处理程序上下文中使用

    signal sqlstate '42S03';
    ##error 1644 (42S03):Unhandled user-defined exception condition
    
  • signal在存储过程中使用的例子:

    drop procedure if exists Fun0;
    drop procedure if exists Fun1;
    drop procedure if exists Fun2;
    
    
    delimiter $$
    
    
    create procedure Fun0()
    begin 
      signal sqlstate value '42S02';
    end $$
    
    
    create procedure Fun1()
    begin 
    
      declare exit handler for sqlexception 
      begin 
      end;
      
      signal sqlstate value '42S02';
    
    end $$
    
    
    create procedure Fun2()
    begin 
    
      declare exit handler for sqlexception 
      begin 
          signal sqlstate value '99999'
              set message_text = 'szn';
      end;
      
      signal sqlstate value '42S02';
    
    end $$
    
    
    delimiter ;
    
    
    call Fun0();
    #error 1644(42S02): unhandled user-defined exception condition
    
    call Fun1();
    #无错误输出
    
    call Fun2();
    #error 1644(99999): szn
    
    drop procedure if exists Fun;
    
    delimiter $$
    
    create procedure Fun()
    begin 
    
      declare exit handler for sqlstate '99999'
      begin 
          select "szn";
      end; 
    
      declare continue handler for sqlstate '42S02' 
      begin 
          signal sqlstate value '99999'
              set message_text = 'szn';
      end;
      
      signal sqlstate value '42S02';
    
    end $$
    
    
    delimiter ;
    
    call fun();
    #error 1644(99999):szn
    
  • condition_value总是一个sqlstate值,要么是直接指定,要么是通过condition间接指定。sqlstate的前两个字符指明了其类别:

    • '00':表示成功,不允许指定此值

    • '01'

      • 表示警告
      • show warnings可显示此signal
      • sqlwarning handler可以捕捉这个信号
      #此类 condition_information_item_name 中值的默认值
      MESSAGE_TEXT = 'Unhandled user-defined warning condition';
      MYSQL_ERRNO = ER_SIGNAL_WARN
      
    • '02':表示not found

      • not found handler可以捕捉此信号

      • 对游标无影响

        drop table if exists t;
        create table t(v0 int);
        insert into t(v0) values(1), (2), (3);
        
        
        drop procedure if exists Fun;
        
        delimiter $$
        
        create procedure Fun(out vOut int)
        begin 
          declare v int;
          declare c cursor for select v0 from t;
        
          declare continue handler for not found 
          begin 
              select "szn";
          end;
          
          
          open c;
          set vOut = 0;
          
          fetch c into v;
          set vOut = vOut + v;
          signal sqlstate '02000';
              
          fetch c into v;
          set vOut = vOut + v;    
          
          close c;
        end $$
        
        delimiter ;
        
        set @v = 0;
        call Fun(@v);
        #输出szn
        
        select @v;
        #3
        
      • 若此类型信号未进行捕获,则立刻结束语句块

      #此类 condition_information_item_name 中值的默认值
      MESSAGE_TEXT = 'Unhandled user-defined not found condition';
      MYSQL_ERRNO = ER_SIGNAL_NOT_FOUND
      
    • 大于'02':表示异常

      • sqlexception可以捕获此信号
      • 若此类型信号未进行捕获,则立刻结束语句块
      #此类 condition_information_item_name 中值的默认值
      MESSAGE_TEXT = 'Unhandled user-defined exception condition';
      MYSQL_ERRNO = ER_SIGNAL_EXCEPTION
      
  • 在调用了signal

    • C API
      • mysql_sqlstate():返回sqlstate
      • mysql_errno():返回MYSQL_ERRORMESSAGE_TEXT
    • Sql语句:show warningsshow errors指明了MYSQL_ERRORMESSAGE_TEXT

Resignal

RESIGNAL [condition_value]
    [SET signal_information_item
    [, signal_information_item] ...]

condition_value: {
    SQLSTATE [VALUE] sqlstate_value
  | condition_name
}

signal_information_item:
    condition_information_item_name = simple_value_specification

condition_information_item_name: {
    CLASS_ORIGIN
  | SUBCLASS_ORIGIN
  | MESSAGE_TEXT
  | MYSQL_ERRNO
  | CONSTRAINT_CATALOG
  | CONSTRAINT_SCHEMA
  | CONSTRAINT_NAME
  | CATALOG_NAME
  | SCHEMA_NAME
  | TABLE_NAME
  | COLUMN_NAME
  | CURSOR_NAME
}
  • condition handler中,resignal传递可更改的error condition

    • signal是创建error condition,而resignal是依赖已有的error condition并能进行更改
  • resignalsignalcondition_valuesignal_information_item的规则是相同的

  • resignalcondition_valueset子句都是可选的

单独的Resignal

  • 单独的resignal意味着仅传递错误,不进行更改
drop procedure if exists Fun;
drop table t if exists;


delimiter $$

create procedure Fun()
begin 
    
    declare continue handler for sqlexception 
    begin 
        select "szn0";
        resignal;
        select "szn1";
    end;
    
    drop table t;
    
end $$

delimiter ;

call Fun();
/*
szn0
----
szn0

error 1051 (42S02):unknow table 'd0.t'
*/

show errors;
/*
Level   Code    Message
----    ----    -------------------
Error   1051    unknow table 'd0.t'
*/

drop table t;
#error 1051 (42S02):unknow table 'd0.t'

仅更改signal_information_item的Resignal

drop procedure if exists Fun;
drop table t if exists;


delimiter $$

create procedure Fun()
begin 

    declare continue handler for sqlexception 
    begin 
        select "szn";
        resignal
            set mysql_errno = 5,
                message_text = "hello";
    end;
    
    drop table t;
    
end $$

delimiter ;

call Fun();
/*
szn
---
szn

error 5 (42S02):hello
*/

show errors;
/*
Level   Code    Message
----    ----    -------------------
Error   5       hello
*/

drop table t;
#error 1051 (42S02):unknow table 'd0.t'

更改condition_value及可选的signal_information_item的Resignal

drop procedure if exists Fun;
drop table t if exists;


delimiter $$

create procedure Fun()
begin 

    declare continue handler for sqlexception 
    begin 
        select "szn";
        resignal sqlstate '04123'
            set mysql_errno = 5,            #可选
                message_text = "hello";     #可选
    end;
    
    drop table t;
    
end $$

delimiter ;

call Fun();
/*
szn
---
szn

error 5 (04123):hello
*/

show errors;
/*
Level   Code    Message
----    ----    -------------------
Error   1051    unknow table 'd0.t'
Error   5       hello
*/

drop table t;
#error 1051 (42S02):unknow table 'd0.t'

Handler的范围规则

stored program可以包含多个handler,当特定的条件在program中发生时,进行调用。hanlder的适用性取决于其自身定义的位置以及其关联的condition

  • begin ... enddeclarehandler,其范围只适用于begin ... end块剩余的sql语句。如果handler自身触发了condition,其自身无法处理这个condition,并且begin ... end块内的任何其他handler也无法处理

    BEGIN -- outer block
      DECLARE EXIT HANDLER FOR ...;  -- handler H1
      DECLARE EXIT HANDLER FOR ...;  -- handler H2
      stmt1;
      stmt2;
    END;
    #H1和H2适用于由stmt1和stmt2触发的condition,但是无论是H1还是H2都无法处理在H1和H2内部触发的condition
    
  • handler的仅适用于declare其本身的begin ... end块内,无法处理外部引发的condition

    BEGIN -- outer block
      BEGIN -- inner block
        DECLARE EXIT HANDLER FOR ...;  -- handler H1
        stmt1;
      END;
      stmt2;
    END;
    #在上述例子中,H1适用于由stmt1引发的condition,而不适用于stmt2引发的condition
    
  • handler是否被触发,不仅取决于其自身声明的位置和条件,还受当前块中的其他handler影响。当condition被触发,服务器将搜索当前begin ... end块中适用的handler,如果当前没有合适的handler,服务器将继续搜索外部块中的handler,当服务器在指定的块中找到了一个或者多个可用的handler,将按照如下优先级进行选择

    • 最局部的且符合conditionhandler优于外部的handler
    • 关联了mysql error codehandler
    • 关联了sqlstate valuehandler
    • 关联了sqlexceptionhandler
    • 关联了sqlwarningnot foundhandler
  • 同时具有多个可用的且优先级相同的handler是可能的。比如说一条语句可能同时触发多个general warning并且对应的handler都存在。此时无法保证哪个handler会被触发

Demo

drop procedure if exists Fun0;
drop procedure if exists Fun1;
drop procedure if exists Fun2;
drop procedure if exists Fun3;
drop table if exists t;


delimiter $$


#Fun0
create procedure Fun0()
begin 

    declare continue handler for sqlexception 
        select "sql exception";
        
    declare continue handler for sqlstate '42S02'
        select "sql state";

    drop table t;

end $$


#Fun1
create procedure Fun1()
begin 

    declare continue handler for sqlstate '42S02'
        select "sql state";
    
    begin 
        declare continue handler for sqlexception 
            select "sql exception";

        drop table t;
    end;

end $$


#Fun2
create procedure Fun2()
begin 

    declare continue handler for sqlstate '42S02'
        select "sql state";
    
    begin 
        declare continue handler for sqlexception 
            select "sql exception";
    end;

    drop table t;

end $$


#Fun3
create procedure Fun3()
begin 

    begin 
    declare continue handler for sqlstate '42S02'
        select "sql state";
    
    declare continue handler for sqlexception 
        select "sql exception";
    end;

    drop table t;

end $$


delimiter ;

call Fun0();
/*
sql state
---------
sql state
*/

call Fun1();
/*
sql exception
-------------
sql exception
*/

call Fun2();
/*
sql state
---------
sql state
*/

call Fun3();
#error 1051 (42S02): unknow table 'd0.t'

Condition Handling and OUT or INOUT Parameters

  • 如果存储过程因为一个没有处理的exception而退出,被更改的outinout参数不会被传递给调用者

    drop procedure if exists Fun;
    delimiter $$
    
    create procedure Fun(out v0 int, inout v1 int)
    begin 
    
      set v0 = 1;
      set v1 = 2;
    
      select "Inner", v0, v1;
      signal sqlstate '04001';
    
    end $$
    
    delimiter ;
    
    call Fun(@a, @b);
    /*
    Inner v0  v1
    -------------
    Inner 1   2
    */
    
    select @a, @b;
    /*
    @a        @b
    ------------
    null  null
    */
    

参考

https://dev.mysql.com/doc/refman/8.0/en/sql-compound-statements.html

你可能感兴趣的:(复合语句)