MySQL 8.4 Reference Manual  /  Language Structure  /  User-Defined Variables

11.4 用户定义的变量

您可以在一个语句中存储一个值,并在另一个语句中引用它。这使您可以将值从一个语句传递到另一个语句。

用户变量写作 @var_name,其中变量名 var_name 由字母数字、._$组成。用户变量名可以包含其他字符,如果您将其作为字符串或标识符引用(例如 @'my-var'@"my-var"@`my-var`)。

用户定义的变量是会话特定的。一个客户端定义的变量不能被其他客户端看到或使用(除非您有访问性能.schema user_variables_by_thread 表的权限,可以看到所有会话的变量)。所有变量对于给定的客户端会话都是自动释放的,当该客户端退出时。

用户变量名不区分大小写。名称的最大长度为64个字符。

设置用户定义变量的一种方法是使用SET语句:

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

对于SET,可以使用=:=作为赋值操作符。

用户变量可以分配以下类型的值:整数、十进制、浮点数、非二进制字符串或NULL值。将十进制和浮点数的值分配给用户变量时,不会保留值的精度或比例。其他类型的值将被转换为可接受的类型。例如,具有时间或空间数据类型的值将被转换为二进制字符串。具有JSON数据类型的值将被转换为字符串,字符集为utf8mb4,排序规则为utf8mb4_bin

如果将用户变量分配给非二进制字符串值,它将具有相同的字符集和排序规则。用户变量的隐式强制性是相同的(与表列值相同)。

将十六进制或位值分配给用户变量时,会将其视为二进制字符串。要将十六进制或位值作为数字分配给用户变量,请将其用于数值上下文。例如,添加0或使用CAST(... AS UNSIGNED)

mysql> SET @v1 = X'41';
mysql> SET @v2 = X'41'+0;
mysql> SET @v3 = CAST(X'41' AS UNSIGNED);
mysql> SELECT @v1, @v2, @v3;
+------+------+------+
| @v1  | @v2  | @v3  |
+------+------+------+
| A    |   65 |   65 |
+------+------+------+
mysql> SET @v1 = b'1000001';
mysql> SET @v2 = b'1000001'+0;
mysql> SET @v3 = CAST(b'1000001' AS UNSIGNED);
mysql> SELECT @v1, @v2, @v3;
+------+------+------+
| @v1  | @v2  | @v3  |
+------+------+------+
| A    |   65 |   65 |
+------+------+------+

如果用户变量的值在结果集中选择,它将被传回客户端作为字符串。

如果您引用未初始化的变量,它将具有NULL值和字符串类型。

在预备语句中引用用户变量时,它的类型在语句首次准备时确定,并且在后续执行时保持该类型。类似地,在存储过程中使用用户变量时,它的类型在首次调用时确定,并且在后续调用时保持该类型。

用户变量可以在大多数允许表达式的上下文中使用。这不包括当前不允许使用字面值的上下文,例如SELECT语句的LIMIT子句或LOAD DATA语句的IGNORE N LINES子句。

MySQL 8.4中恢复了将用户变量赋值的功能,这是为了向后兼容,但是在将来的MySQL版本中可能会被删除。

在使用这种方式赋值时,您必须使用:=作为赋值操作符;在语句中使用=将被视为比较操作符,而不是赋值操作符。

表达式中用户变量的评估顺序是未定义的。例如,SELECT @a, @a:=@a+1不保证先评估@a然后执行赋值操作。

此外,变量的默认结果类型基于语句开始时的类型。这可能会导致意外的效果,如果变量在语句开始时持有一个类型,然后在语句中被赋值为另一个类型。

为了避免这种行为的影响,可以不要在同一个语句中将变量赋值和读取变量的值,或者将变量设置为00.0''以定义变量的类型。

HAVINGGROUP BYORDER BY在引用被赋值的变量时,不会像预期那样工作,因为表达式是在客户端评估的,因此可以使用之前行的列值。

用户变量旨在提供数据值。它们不能直接在SQL语句中使用作为标识符或作为标识符的一部分,例如在表或数据库名称的上下文中,或者作为保留字,如SELECT。这也适用于使用引号的变量,例如以下示例:

mysql> SELECT c1 FROM t;
+----+
| c1 |
+----+
|  0 |
+----+
|  1 |
+----+
2 rows in set (0.00 sec)

mysql> SET @col = "c1";
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @col FROM t;
+------+
| @col |
+------+
| c1   |
+------+
1 row in set (0.00 sec)

mysql> SELECT `@col` FROM t;
ERROR 1054 (42S22): Unknown column '@col' in 'field list'

mysql> SET @col = "`c1`";
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @col FROM t;
+------+
| @col |
+------+
| `c1` |
+------+
1 row in set (0.00 sec)

在构建用于后续执行的预备语句时,用户变量可以用来提供任何部分的语句。以下示例演示了如何实现这一点:

mysql> SET @c = "c1";
Query OK, 0 rows affected (0.00 sec)

mysql> SET @s = CONCAT("SELECT ", @c, " FROM t");
Query OK, 0 rows affected (0.00 sec)

mysql> PREPARE stmt FROM @s;
Query OK, 0 rows affected (0.04 sec)
Statement prepared

mysql> EXECUTE stmt;
+----+
| c1 |
+----+
|  0 |
+----+
|  1 |
+----+
2 rows in set (0.00 sec)

mysql> DEALLOCATE PREPARE stmt;
Query OK, 0 rows affected (0.00 sec)

查看第15.5节,“预编译语句”,了解更多信息。

可以在应用程序中使用类似的技术来使用程序变量构建SQL语句,如下所示使用PHP 5:

<?php
  $mysqli = new mysqli("localhost", "user", "pass", "test");

  if( mysqli_connect_errno() )
    die("Connection failed: %s\n", mysqli_connect_error());

  $col = "c1";

  $query = "SELECT $col FROM t";

  $result = $mysqli->query($query);

  while($row = $result->fetch_assoc())
  {
    echo "<p>" . $row["$col"] . "</p>\n";
  }

  $result->close();

  $mysqli->close();
?>

使用这种方式组装SQL语句有时被称为动态SQL