## 9.4 用户定义的变量

[](<>)[](<>)[](<>)[](<>)[](<>)[](<>)

您可以在一个语句中将值存储在用户定义的变量中，稍后在另一个语句中引用它。这使您能够将值从一个语句传递到另一个语句。

用户变量写为`@*`变量*姓名`*`, 其中变量名*`变量名`*由字母数字字符组成，`.`, \`*`， 和`$`.如果将用户变量名称作为字符串或标识符引用，则它可以包含其他字符（例如，`@'我的变量'`,`@“我的变种”`， 要么`@\`我的变量``）。

用户定义的变量是特定于会话的。一个客户端定义的用户变量不能被其他客户端看到或使用。（例外：有权访问性能模式的用户[`user_variables_by_thread`](performance-schema-user-variable-tables.html)table 可以看到所有会话的所有用户变量。）给定客户端会话的所有变量在该客户端退出时自动释放。

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

设置用户定义变量的一种方法是发出[`放`](set-variable.html)陈述：

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

为了[`放`](set-variable.html)， 任何一个[`=`](assignment-operators.html#operator_assign-equal)要么[`:=`](assignment-operators.html#operator_assign-value)可以用作赋值运算符。

可以从一组有限的数据类型中为用户变量分配一个值：整数、十进制、浮点、二进制或非二进制字符串，或者`空值`价值。十进制和实数值的分配不会保留值的精度或小数位数。将除允许类型之一之外的类型的值转换为允许类型。例如，具有时间或空间数据类型的值被转换为二进制字符串。具有的值[`JSON`](json.html)数据类型被转换为具有字符集的字符串`utf8mb4`和一个整理`utf8mb4_bin`.

如果为用户变量分配了非二进制（字符）字符串值，则它具有与字符串相同的字符集和排序规则。用户变量的强制性是隐含的。（这与表列值的强制力相同。）

分配给用户变量的十六进制或位值被视为二进制字符串。要将十六进制或位值作为数字分配给用户变量，请在数字上下文中使用它。例如，添加 0 或使用[`CAST（...未签名）`](cast-functions.html#function_cast)：

```
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 |
+------+------+------+
```

如果在结果集中选择了用户变量的值，则将其作为字符串返回给客户端。

如果你引用一个没有被初始化的变量，它的值是`空值`和一种字符串。

从 MySQL 8.0.22 开始，在准备好的语句中对用户变量的引用在第一次准备语句时确定其类型，并在此后每次执行该语句时保留此类型。类似地，在存储过程中的语句中使用的用户变量的类型是在第一次调用存储过程时确定的，并且在每次后续调用时都保留该类型。

用户变量可以在大多数允许表达式的情况下使用。这目前不包括明确需要文字值的上下文，例如在`限制`a的从句[`选择`](select.html)声明，或`忽视 *`ñ`* 线`a的从句[`加载数据`](load-data.html)陈述。

以前的 MySQL 版本可以在语句中为用户变量分配值，而不是[`放`](set-variable.html).MySQL 8.0 支持此功能以实现向后兼容性，但在 MySQL 的未来版本中可能会被删除。

以这种方式进行分配时，您必须使用[`:=`](assignment-operators.html#operator_assign-value)作为赋值运算符；`=`在语句中被视为比较运算符[`放`](set-variable.html).

涉及用户变量的表达式的求值顺序未定义。例如，不能保证`选择@a，@a:=@a+1`评估`@一种`首先，然后执行分配。

此外，变量的默认结果类型基于语句开头的类型。如果一个变量在语句的开头保存了一个类型的值，并且在该语句的开头还为它分配了一个不同类型的新值，这可能会产生意想不到的影响。

为避免此行为出现问题，请不要在单个语句中为同一变量赋值并读取其值，或者将变量设置为`0`,`0.0`， 要么`''`在使用它之前定义它的类型。

`拥有`,`通过...分组`， 和`订购方式`，当引用在选择表达式列表中分配了值的变量时，不会按预期工作，因为表达式是在客户端上计算的，因此可以使用前一行中的陈旧列值。

用户变量旨在提供数据值。它们不能直接在 SQL 语句中用作标识符或标识符的一部分，例如在需要表或数据库名称的上下文中，或作为保留字，例如[`选择`](select.html).即使引用了变量也是如此，如下例所示：

```
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)
```

看[第 13.5 节，“准备好的陈述”](sql-prepared-statements.html)， 了解更多信息。

可以在应用程序中使用类似的技术来使用程序变量构造 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”。
