二つのMySQLのユーザー定義変数の設定方法
@
をつけて@変数
で定義できるユーザー変数には、MySQL 8.0(現時点の最新)までで二つの設定方法がある。
SETで設定する
ひとつはSET
を使う方法。
mysql> SET @var_name = 'expr';
mysql> SELECT @var_name;
+-----------+
| @var_name |
+-----------+
| expr |
+-----------+
1 row in set (0.00 sec)
SET以外のステートメント内で設定する
もうひとつはSELECT
などのステートメント内で設定する方法。
mysql> SELECT @var_name := 'expr';
+---------------------+
| @var_name := 'expr' |
+---------------------+
| expr |
+---------------------+
1 row in set, 1 warning (0.00 sec)
「SET以外のステートメント内で設定する」はもうやってはいけない
SET以外のステートメント内で設定する方法は、MySQL 5.6の時点でドキュメントに注意事項が記載されていて、「同じステートメント内で、ユーザー変数に値を割り当ててその値を読み取る」ことが禁止されていた。
一般的なルールとして、SET ステートメント以外では、同じステートメント内で、ユーザー変数に値を割り当ててその値を読み取ることは決してしないでください。
SELECT などのほかのステートメントでは、予想した結果が得られることもありますが、これは保証されません。
SET 以外の同じステートメント内で変数に値を割り当てて、その値を読み取る場合に生じるもう 1 つの問題は、変数のデフォルトの結果型がステートメントの開始時の型に基づくということです。
MySQL 8.0のドキュメントでは、下位互換性のために残されているだけで、もはやSET以外のステートメント内で設定する方法は推奨されていない。
以前のリリースの MySQL では、SET 以外のステートメントでユーザー変数に値を割り当てることができました。 この機能は、下位互換性のために MySQL 8.0 でサポートされていますが、MySQL の将来のリリースで削除される予定です。
ステートメント内でユーザー変数に値を割り当ててその値を読み取るとどうなるか
「SET以外のステートメント内で設定する」はもうやってはいけないということに気づいたのは、Stack Overflowの質問「Mysql. Function ROUND doesn't give right value」を読んで実際に試してみたところ、確かにうまくいかず、ユーザー定義変数についてのドキュメントを読んでみたから。
質問では、select @a := 992.7500, ROUND(@a * (1 + 18 / 100), 2) AS Total;
の結果が1回目と2回目以降で異なると記載されていた。
回答した通り、根本的な原因は、SQLを一番はじめに実行したタイミングでは変数が真値の数値(exact-value)になっておらず、近似値(approximate-value)になっていたため、ROUND
の丸め動作が期待したものにならなかったということである。
MySQL 5.6のドキュメントの注意事項で書かれていたこと(「SET 以外の同じステートメント内で変数に値を割り当てて、その値を読み取る場合に生じるもう 1 つの問題は、変数のデフォルトの結果型がステートメントの開始時の型に基づくということです。」)にぴったり当てはまる。
※ROUND
の丸め動作についてはこちらを参照
回答↓
If you use
SET
statements, you can obtain the right value with the first run.
SET @a = 992.7500;
select ROUND(@a * (1 + 18 / 100), 2) AS Total;
https://dev.mysql.com/doc/refman/5.6/en/user-variables.html
As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement.
Another issue with assigning a value to a variable and reading the value within the same non-SET statement is that the default result type of a variable is based on its type at the start of the statement.
@a
is exact-value number when you useSET
.@a
assigned inSELECT
statements seems not to be exact-value but approximate-value numbers.MySQL 8.0(current) still supports the syntax you wrote, but the syntax is subject to removal in a future release of MySQL.
Previous releases of MySQL made it possible to assign a value to a user variable in statements other than SET. This functionality is supported in MySQL 8.0 for backward compatibility but is subject to removal in a future release of MySQL.