一、到底MySQL的变量分哪几类?

MySQL变量一共分为两大类:用户自定义变量和系统变量。如下:

  • 用户自定义变量

    • 局部变量
    • 会话变量
  • 系统变量
    • 会话变量
    • 全局变量

本文涉及的内容为用户自定义会话变量,若对其他分类无感,请点击这里

PS:用户定义的会话变量和系统定义的会话变量有什么区别?

局部变量

局部变量一般用于SQL的语句块中,比如存储过程中的begin和end语句块。其作用域仅限于该语句块内。生命周期也仅限于该存储过程的调用期间。

1
2
3
4
5
6
7
8
9
10
11
drop procedure if exists add;
create procedure add
(
    in a int,
    in b int
)
begin
    declare c int default 0;
    set c = a + b;
    select c as c;
end;

上述存储过程中定义的变量c就是局部变量。

会话变量

会话变量即为服务器为每个客户端连接维护的变量。在客户端连接时,使用相应全局变量的当前值对客户端的回话变量进行初始化。设置会话变量不需要特殊权限,但客户端只能更改自己的会话变量。其作用域与生命周期均限于当前客户端连接。

会话变量的赋值:

1
2
3
set session var_name = value;
set @@session.var_name = value;
set var_name = value;

会话变量的查询:

1
2
3
select @@var_name;
select @@session.var_name;
show session variables like "%var%";

全局变量

全局变量影响服务器整体操作。当服务器启动时,它将所有全局变量初始化为默认值。这些默认值可以在选项文件中或在命令行中指定的选项进行更改。要想更改全局变量,必须具有SUPER权限。全局变量作用于server的整个生命周期,但是不能跨重启。即重启后所有设置的全局变量均失效。要想让全局变量重启后继续生效,需要更改相应的配置文件。

全局变量的设置:

1
2
set global var_name = value; //注意:此处的global不能省略。根据手册,set命令设置变量时若不指定GLOBAL、SESSION或者LOCAL,默认使用SESSION
set @@global.var_name = value; //同上

全局变量的查询:

1
2
select @@global.var_name;
show global variables like "%var%";

二、MySQL用户自定义变量详解

你可以利用SQL语句将值存储在用户自定义变量中,然后再利用另一条SQL语句来查询用户自定义变量。这样以来,可以再不同的SQL间传递值。

用户自定义变量的声明方法形如:@var_name,其中变量名称由字母、数字、“.”、“_”和“$”组成。当然,在以字符串或者标识符引用时也可以包含其他字符(例如:@'my-var',@"my-var",或者@`my-var`)。

用户自定义变量是会话级别的变量。其变量的作用域仅限于声明其的客户端链接。当这个客户端断开时,其所有的会话变量将会被释放。

用户自定义变量是不区分大小写的。

使用SET语句来声明用户自定义变量:

1
SET @var_name = expr[, @var_name = expr] ...

在使用SET设置变量时,可以使用“=”或者“:=”操作符进行赋值。

当然,除了SET语句还有其他赋值的方式。比如下面这个例子,但是赋值操作符只能使用“:=”。因为“=”操作符将会被认为是比较操作符。

mysql> SET @t1=1, @t2=2, @t3:=4;
mysql> SELECT @t1, @t2, @t3, @t4 := @t1+@t2+@t3;
+------+------+------+--------------------+
| @t1 | @t2 | @t3 | @t4 := @t1+@t2+@t3 |
+------+------+------+--------------------+
| 1 | 2 | 4 | 7 |
+------+------+------+--------------------+

用户变量的类型仅限于:整形、浮点型、二进制与非二进制串和NULL。在赋值浮点数时,系统不会保留精度。其他类型的值将会被转成相应的上述类型。比如:一个包含时间或者空间数据类型(temporal or spatial data type)的值将会转换成一个二进制串。

如果用户自定义变量的值以结果集形式返回,系统会将其转换成字符串形式。

如果查询一个没有初始化的变量,将会以字符串类型返回NULL。

不要在同一个非SET语句中同时赋值并使用同一个用户自定义变量

用户自定义变量可以用于很多上下文中。但是目前并不包括那些显式使用常量的表达式中,比如SELECT中的LIMIT子句,或者LOAD DATA中的IGNORE N LINES的字句中。

通常来说,除了在SET语句中,不要再同一个SQL语句中同时赋值并使用同一个用户自定义变量。举个变量自增的例子,下面的是没问题的:

1
SET @a = @a + 1;

对于其他语句,比如SELECT,也许会得到期望的效果,但这真心不靠谱。比如下面的语句,也许你自然地会认为MySQL会先执行@a的值,然后再进行赋值操作:

1
SELECT @a, @a:=@a+1, ...;

然而,用户自定义变量表达式的计算顺序还没有定义呢。

除此之外,还有另一个问题。变量的默认返回类型由语句开始时的类型决定的,正如下面的例子:

1
2
mysql> SET @a='test';
mysql> SELECT @a,(@a:=20) FROM tbl_name;

上述的SELECT语句中,MySQL会报告给客户端第一列的字段类型为字符串,同时将所有对@a变量的使用均转换为字符串处理,尽管在SELECT语句中将@a变量设置为数字类型。在SELECT语句执行后,@a变量才会在下一个语句中识别为数字类型。

为了避免上述问题的发生,要么不在同一个语句中同时赋值并使用变量,要么在使用之前,将变量设置为0,0.0,或者'',以确定它的数据类型。

变量的值是在SQL发送到客户端后才计算的

在SELECT语句中,在每一个select表达式被发送给客户端后,才会进行计算。这就意味着,在形如HAVING,GROUP BY和ORDER BY只句中有使用在当前select表达式定义的变量的情况下,该语句将不会得到如期的效果。

1
mysql> SELECT (@aa:=id) AS a, (@aa+3) AS b FROM tbl_name HAVING b=5;

上述在HAVING只句中使用了在当前的select列表中定义的别名b,其使用了变量@aa。这条语句并不会得到如期的效果:@aa变量为上一次SQL语句执行的结果集中的ID值,并非当前的。

三、MySQL用户自定义变量的实际应用举例

项目

超级话题积分系统

术语

积分行为:如转发、评论超级话题下的帖子、签到某超级话题或者帖子被其他人回复等行为。

积分行为次数:产生积分行为的累计次数。

业务场景

用户在某超级话题下,第N次产生累计积分的行为,如转发微博,会增加该用户在该超级话题下的积分总数。具体的积分规则见长文章

问题

曾有用户反馈说超级话题积分有漏记的情况:为什么我评论了却没有加分;为什么转发了超级话题帖子没有加分等等。随后,我们立即通过查询后台的积分记录发现,会看到转发行为在第5次时,积分的增加却为0。这显然是不正常的。

首先,排除了根据积分行为的次数来计算积分值的问题。比如第5次转发微博应增加6分。这块的规则,利用二分法写死在程序里面,也做过单元测试,不会有问题。那么,问题就锁定在这个积分行为的次数。

首先来看看积分次数的获取:

1
2
3
4
public static function find($uid, $aid, $status) { 
    $sql = 'SELECT * FROM '.self::table($aid).' WHERE uid = ? AND aid = ? AND status = ?';
    return Comm_Db::d(Comm_Db::DB_BASIC)->fetchRow($sql, array($uid, $aid, $status));
}  

然后,利用上述find()方法来取得该用户在某超级话题下的某积分行为的累计次数。这是有问题的,在于读于从库,但并不保证从库的值是最新的,所以导致当前获取的积分行为次数并不一定是正确的(小于等于实际的值)。

随后,程序会根据当前的次数计算积分值,并分别更新积分值和该行为的积分行为次数值。

所以,这次利用MySQL的用户自定义会话变量的方式,来解决上述问题。

1
2
3
4
5
6
7
8
public static function incCounter($uid, $aid, $status) {
    $db = Comm_Db::d(Comm_Db::DB_BASIC);
    $sql = "UPDATE ". self::table($aid) ." SET `ctn_counter`=@ctn_counter:=`ctn_counter`+1 WHERE `uid` = ? AND `aid` = ? AND `status` = ?";
    $db->execute($sql, array($uid, $aid, $status));
    $sql = "SELECT @ctn_counter";
    $rs = $db->fetchOne($sql, null, true);
    return $rs;
}

改进后,如上述函数,程序将先进行调用incCounter()函数,将当前的积分行为次数自增,并将值存入当前变量中。随后,立即将其读取并返回给PHP进行积分处理。这样一来,就保证了积分行为次数的正确性。

四、关于MySQL用户自定义变量的结束语

在这次的“填坑”过程中,使用了MySQL变量解决了MySQL主从服务同步延迟的问题。这篇文章也算是对于MySQL用户自定义变量深入学习的记载。

除此之外,仍有个问题,用户自定义的会话变量是存在进程内存中的。但是,是存在客户端进程中还是服务端进程中的呢?


参考文章:

文章来源:胡旭博客 => 深入MySQL用户自定义变量:使用详解及其使用场景案例

深入MySQL用户自定义变量的更多相关文章

  1. 深入MySQL用户自定义变量:使用详解及其使用场景案例

    一.前言 在前段工作中,曾几次收到超级话题积分漏记的用户反馈.通过源码的阅读分析后,发现问题出在高并发分布式场景下的计数器上.计数器的值会影响用户当前行为所获得积分的大小.比如,当用户在某超级话题下连 ...

  2. 如何在.Net的MySqlCommand里面使用MySql用户自定义变量?

    Mysql使用@符号代表变量,但C#也恰好使用@代表用户自定义变量,这样两者就会正好冲突了. SELECT () AS rowId, u.*, r.RoleName FROM userinfo u L ...

  3. mysql:用户自定义变量关联失效

    自定义变量的属性和限制 使用自定义变量的查询,无法使用查询缓存. 不能在使用常量或者标识列的地方使用自定义变量,例如表名.列明和LIMIT子句中. 用户自定义变量的生命周期是在一个连接中有效,所以不能 ...

  4. mysql 用户自定义变量

    SQL中可以用变量来操作值.那么问题就来了.mysql中怎么定义一个变量呢? 一.定义变量 1.定义变量的语法: set @var_name=expr [,@var_name=expr] ... 2. ...

  5. mysql用户自定义变量

    可以先在用户变量中保存值然后在以后引用它:这样可以将值从一个语句传递到另一个语句.用户变量与连接有关.也就是说,一个客户端定义的变量不能被其它客户端看到或使用.当客户端退出时,该客户端连接的所有变量将 ...

  6. MySQL的变量分类总结

    在MySQL中,my.cnf是参数文件(Option Files),类似于ORACLE数据库中的spfile.pfile参数文件,照理说,参数文件my.cnf中的都是系统参数(这种称呼比较符合思维习惯 ...

  7. jmeter参数化数据(_csvread函数、用户自定义变量等)

    以下是转载内容,仔细看过后,觉得用得最多的应该是csvread函数.用户自定义变量以及CSV DATA CONFIG控制器这几个,但是做练习之后,在结果树和聚合报告中怎么查看执行结果是个问题,没找到对 ...

  8. Mysql自定义变量的使用

    用户自定义变量是一个容易被遗忘的MySQL特性,但是如果能用的好,发挥其潜力,在某些场景可以写出非常高效的查询语句.在查询中混合使用过程化和关系化逻辑的时候,自定义变量可能会非常有用.单纯的关系查询将 ...

  9. mysql之变量

    本文内容: 系统变量 用户变量 局部变量 首发日期:2018-04-18 系统变量: 系统变量就是系统已经提前定义好了的变量 系统变量一般都有其特殊意义.比如某些变量代表字符集.某些变量代表某些mys ...

随机推荐

  1. freeswitch自定义模块的wiki地址

    http://wiki.freeswitch.org/wiki/Authoring_Freeswitch_Modules

  2. BASIC-15_蓝桥杯_字符串对比

    示例代码: #include <stdio.h>#define N 10 int main(void){ int i = 0 ; int len1 = 0 , len2 = 0 , fla ...

  3. jenkins 执行python脚本 断言失败就可以构建失败

    可以配合try: 那个语句去搭配

  4. 杂项-Java:jar 包与 war 包介绍与区别

    ylbtech-杂项-Java:jar 包与 war 包介绍与区别 1.返回顶部 1. 做Java开发,jar包和war包接触的挺多的,有必要对它们做一个深入的了解,特总结整理如下: 1.jar包的介 ...

  5. 用两条命令看出你买的H3C光模块是否是正品

    display transceiver manuinfo interfacedisplay transceiver interface从下文可以看出 1/0/26 1/0/27 2/0/26三个端口的 ...

  6. Redis 密码设置和查看密码

    Redis 密码设置和查看密码 redis没有实现访问控制这个功能,但是它提供了一个轻量级的认证方式,可以编辑redis.conf配置来启用认证. 1.初始化Redis密码: 在配置文件中有个参数: ...

  7. python判断变量是否为int、字符串、列表、元组、字典等方法

    在实际写程序中,经常要对变量类型进行判断,除了用type(变量)这种方法外,还可以用isinstance方法判断: #!/usr/bin/env pythona = 1b = [1,2,3,4]c = ...

  8. T1330 最少步数(#Ⅱ- 8)(广度优先搜索)

    [题目描述] 在各种棋中,棋子的走法总是一定的,如中国象棋中马走“日”.有一位小学生就想如果马能有两种走法将增加其趣味性,因此,他规定马既能按“日”走,也能如象一样走“田”字.他的同桌平时喜欢下围棋, ...

  9. dos批处理文件中的变量小结

    很多情况下我们需要用到一些批处理文件中的变量,方便我们执行一些操作,这里简单整理下,方便需要的朋友   批处理中的变量,我把他分为两类,分别为"系统变量"和"自定义变量& ...

  10. VB ListView罗列图片

      一开始听到job的要求,还真不知道如何下手,用Gridview?好像有做不到把图片show出来的.在网上搜索一遍,发现原来是ListView与Imglist的结合应用. 看设计: Private ...