MySQL には@変数名
という形で表記でき、セッションごとにスコープを持つため、一度代入した値は接続が続く限り利用できますが、他のセッションから参照することはできません。小さな計算やちょっとした結果の再利用に役立ち、古くから多くのユーザーに親しまれてきました。
今回はこのユーザー定義変数の利用について見ていきましょう。
ユーザー定義変数
ユーザー定義変数は以下のように@変数名 = 値
または@変数名 := 値
の形式で代入します。 定義したユーザー変数はSELECT @変数名
で確認できますし、WHERE句の条件としてWHERE id >= @変数名
のように利用することも可能です。
変数名は大文字小文字を区別せず、英数字に加えて_
、.
、$
を利用できます。それ以外の文字を含めたい場合は、文字列や識別子としてクオートすれば使用可能です。
なお、変数はあくまで
mysql> SET @x = 10; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @x + 5; +--------+ | @x + 5 | +--------+ | 15 | +--------+ 1 row in set (0.00 sec)
これだけで単純な計算を実行でき、セッション内では同じ値を使い回せます。外部スクリプトやアプリケーションに持ち出さずとも、SQLの中で軽い処理を済ませられるのが便利な点です。
代入に使う演算子については注意が必要です。SET文では=
も:=
も代入として使えますが、SELECT文など式の中では:=
を利用すべきです。=
は比較演算子として解釈されるため、次のように意図しない結果になることがあります。
mysql> SET @n = 1; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @n = @n + 1; +-------------+ | @n = @n + 1 | +-------------+ | 0 | +-------------+ 1 row in set (0.00 sec)
ユーザー変数への代入はSET
以外にもSELECT ... INTO
を使う方法があります。クエリの結果をそのまま変数に代入できるので便利です。
SELECT id, name INTO @uid, @uname FROM users WHERE id = 1001;
ただし、1行しか返さないことが前提です。複数行が返ると以下のエラーになってしまうので注意してください。
ERROR 1172 (42000): Result consisted of more than one row
ユーザー定義変数でできること
ユーザー定義変数はちょっとした工夫に役立ちます。代表的な利用例を挙げてみます。
SELECT COUNT(*) INTO @cnt FROM t_sales;
SELECT CONCAT('全件数は', @cnt, '件です');
集計結果を一時的に保存して使い回す方法です。以下ではt_
カウントの結果を@cnt
に保存して表示しています。クエリで取得した件数を変数に入れておけば、以降のクエリで何度も参照できます。複雑な集計を何度も繰り返す必要がなく、SQLを簡潔に保てるという利点があります。
次に、動的SQLの生成です。テーブル名を変数として保持し、それを使ってSQL文を組み立てられます。
SET @tbl := 't_sales';
SET @sql := CONCAT('SELECT COUNT(*) FROM ', @tbl);
PREPARE stmt FROM @sql;
EXECUTE stmt;
このようにすれば、テーブル名や条件を外部から渡して実行することも可能になります。
また、ユーザー定義変数を使って
SET @rn := 0;
SELECT id, @rn := @rn + 1 AS rownum FROM t_sales ORDER BY id;
累積合計を計算する場合も同様の書き方で実現できます。
SET @sum := 0;
SELECT id, amount,@sum := @sum + amount AS running_total FROM (SELECT * FROM t_sales ORDER BY id) s;
このように、ユーザー定義変数を利用すれば、ちょっとした工夫でアプリケーションに頼らずSQLの中だけで処理を完結させることができます。
とはいえSELECT句内での代入はDeprecatedに
ユーザー定義変数の利用には注意すべき点があります。上記のように SELECT句の中で@var := ...
とする書き方は、MySQL 8.
実際にこの書き方をすると、次のような警告が表示されます。
mysql> select @var := 5; +-----------+ | @var := 5 | +-----------+ | 5 | +-----------+ 1 row in set, 1 warning (0.00 sec) mysql> show warnings\G *************************** 1. row *************************** Level: Warning Code: 1287 Message: Setting user variables within expressions is deprecated and will be removed in a future release. Consider alternatives: 'SET variable=expression, ...', or 'SELECT expression(s) INTO variables(s)'. 1 row in set (0.00 sec)
では、SELECT句で値を入れたい場合はどのように記述すれば良いでしょうか。単純な代入にはSET
やSELECT ... INTO
を使えば問題ありません。これらは今後も利用可能です。一方で、行番号や累積合計といった順序を伴う処理には、MySQL 8.
performance_schemaからユーザー定義変数を覗く
ユーザー定義変数は通常はセッションごとに閉じており、他の接続からは参照できません。しかし、performance_
を利用すると、どのスレッド
mysql> SELECT *, CONVERT(VARIABLE_VALUE USING utf8mb4) AS var_value FROM performance_schema.user_variables_by_thread;
このテーブルには次のような情報が含まれています。
- THREAD_
ID:セッションを識別するスレッドID - VARIABLE_
NAME:ユーザー変数名 - VARIABLE_
VALUE:現在の値
これを使えば、たとえば
まとめ
ユーザー定義変数自体は依然として便利な仕組みであり、集計値の一時保存や動的SQLの生成など、実用的な場面で利用し続けられます。しかし、SELECT句内の代入による小技は非推奨であり、すでに警告が出る状態になっています。今後も使い続けると、将来のバージョンで動かなくなるリスクがある点に注意してください。
ユーザー定義変数は、MySQLの歴史の中で多くの人に使われてきた便利な道具です。しかし今は標準SQLとしてより整ったウィンドウ関数が存在し、互換性や信頼性の観点からもそちらに移行すべき時代になっています。古いコードを読み解くために知識として押さえつつ、新しい開発では正しい機能を積極的に活用していきましょう。
より詳細については公式ドキュメントを参考にしてください。