03.变量与作用域

297 阅读4分钟

变量与作用域

1.变量的分类

MySQL中的变量是容易让人混淆的部分

  • MySQL本身的变量分为系统变量和自定义变量(用户变量)
  • 系统变量主要是影响MySQL服务器运行的配置,又分为全局变量和会话(连接)变量,当MySQL启动时,会从配置文件读取系统变量到全局变量,当一个连接建立时,会从全局变量复制到会话变量
  • 自定义变量(用户变量)都是会话变量,生效于每次连接内
  • 在存储过程中又专门有存储过程变量,是需要声明使用的

在存储过程中我们使用的主要是存储过程变量和用户变量,极少数的情况可能需要去更改一下系统配置的会话变量

2.存储过程变量

存储过程变量需要声明,作用域是一个begin ... end代码块中

  BEGIN
    declare v_student_name varchar(255) default 'Tony';
  END

存储过程变量是正统的变量使用方式,有明确可控的作用域,有准确声明的类型,推荐在存储过程中使用。

  BEGIN
    declare v_student_name varchar(255) default 'Tony';
    BEGIN
      declare v_teacher_name varchar(255);
    END;
    select v_teacher_name; #ERROE! 这里不能访问teacher_name
  END

声明变量的类型的时候请根据需要选择合适的类型,注意大小精度限制

存储过程变量可以使用default初始化默认值,没有初始化的就是null

声明的变量都以v_开头,传入的变量都以p_开头,表里的字段不带前缀,这是我们的内部规定,对于阅读者友好,而且也不容易写错,是一种约定优于配置的体现

3.用户变量

用户变量是以@开头的自定义变量,作用域是当前会话(连接)

用户变量无需声明,直接赋值使用即可,而且不严格区分类型,可以混用

  set @temp = 'abc';
  set @temp = 123;

一般只有临时用一下的变量才会用到用户变量,由于该变量在会话内都不会被自动销毁,存储过程运行完后还存在,因此最好最好不要用,一不小心就掉坑里

4.更改MySQL配置的会话变量

有时候可能不确定MySQL的一些配置是否满足运行条件,可以在会话内更改一下配置的会话变量

  set @@max_sp_recursion_depth = 255;

5.变量的赋值

变量的赋值主要有set, select into 两种方式

有表teacher_table

teacher_id name age
1 Jessy 25
2 Mark 40
  BEGIN
    declare v_student_name varchar(255);
    declare v_teacher_name varchar(255);
    declare v_teacher_age int;

    set v_student_name = 'Tony';

    select name, age into v_teacher_name, v_teacher_age
    from teacher_table where teacher_id = 1 limit 1;
  END

上述代码中声明了两个变量,没有初始化赋值因此声明后它们都是null

使用set赋值一般是等号右侧能明确写出来值的情况,当数据需要从表里面取出来赋值的情况下,就需要用select into来赋值了

由于赋值必须显式的使用set或者select into, 因此存储过程中不需要使用两个等号来表示相等

需要注意的是select into的时候不能出多行结果,只能有一行或者没有结果,不然运行时会报错,加上limit 1以防万一

如果这里没有select出结果,那么v_teacher_name还是之前的值,也就是null

6.严格模式

MySQL的配置sql_mode中有个选项是严格模式STRICT_TRANS_TABLES,在严格模式下不同类型的赋值会报错

  BEGIN
    declare v_student_id int;
    declare v_student_age int;

    set v_student_id = 'abc'  #报错!

    select age into v_student_age
    from student_table where student_id = 1 limit 1;
  END

理论上应该开启严格模式以便防止错误的赋值,但实际上有些错误是运行的时候才会报出来的。例如上面示例中对于v_student_age的赋值,如果age字段是个null,那运行的时候这里就崩了,这些在写代码的时候是很难防范的,因此我们在实际使用的过程中是不开严格模式的。后文的示例都是在不开严格模式的情况下运行的