美文网首页
复合语句

复合语句

作者: szn好色仙人 | 来源:发表于2020-01-07 20:41 被阅读0次

    标签

    [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开头的值,因为其代表正确

    • 使用命名的条件,使得程序更加的清晰可读。两种条件声明示例:

      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

    相关文章

      网友评论

          本文标题:复合语句

          本文链接:https://www.haomeiwen.com/subject/jupdactx.html