MySQL道普請便り

第170回MySQLのキーワードと予約語をテーブルに使用したい場合

皆さんは普段テーブルを作成する時にどのように作っているでしょうか? SQLでテーブルを作成している場合もあれば、マイグレーション用のツールを使っている場合もあるかと思います。

今回は、マイグレーション用のツールやORMを使って普段アクセスしているとあまり気が付かないけれども、いざ直接データベースを操作しようとしてSQLを書いた時に困ってしまうキーワードや、予約語と衝突してしまった時にどういったことが起こるのか。その回避方法と、MySQLのキーワードと予約語の確認方法について紹介していきます。

検証環境

今回はdockerで建てたMySQLを使用します。以下のコマンドでdockerを建ててローカルからアクセスをします。

% docker run --platform linux/x86_64 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:latest
% % mysql -uroot -pmy-secret-pw

執筆時点では、以下の通りMySQL 8.0.28を使用しております。

mysql>  select version();
+-----------+
| version() |
+-----------+
| 8.0.28    |
+-----------+
1 row in set (0.01 sec)

今回のテスト用に、testデータベースを以下のクエリで作っておきます。

mysql> create database test;
mysql> use test

キーワードや予約語と衝突するテーブルを作ってみる

まずはキーワードや予約語と衝突するテーブルを作ってみましょう。今回は、キーワードとして普段みなさんが一番叩くことが多く、馴染みが深いselectという名前のテーブルを作ってみようと思います。

create文は以下のものになります。一応、それ以外を変えて問題なく作れるか確認するためにtestテーブルを作成するクエリを、テーブル名だけ変えて実行をしておきます。

mysql> create table test(id int);
Query OK, 0 rows affected (0.09 sec)

mysql> create table select(id int);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select(id int)' at line 1

testテーブルは作成できましたが、selectテーブルはcreate文を実行した時点でERRORが出てしまいました。エラー文を読むと、SQLとして間違っていると言われています。

でも、こうやって作れないなら、どうして私は普通にクエリを書くとエラーになってしまっているのだろう、と思う方もいるかも知れません。そこで今度は少しクエリを変更して実行してみましょう。

mysql> create table `select`(id int);
Query OK, 0 rows affected (0.10 sec)
mysql> show tables ;
+----------------+
| Tables_in_test |
+----------------+
| select         |
| test           |
+----------------+
2 rows in set (0.03 sec)

今度はクエリが実行されてselectテーブルが作成されていることがわかります。では、先ほどの失敗したクエリと今成功したクエリは、一体何が違うのでしょうか?

答は簡単で、selectが「`」⁠バッククオート)記号で囲まれているか囲まれていないかの違いになります。キーワードや予約語と衝突が発生している場合は、このようにキーワードや予約語をバッククオート記号で囲んであげると問題なく実行することができます。 今回はDDLで実験しましたが、DMLでも同様に行えます。

mysql> select * from select;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select' at line 1

DMLで先ほど作成したテーブルに対してselect文を実行しようとしたら、構文エラーが発生してしまいました。落ち着いて、先ほどのDDLと同様にfrom句のselectを「`」で囲います。

mysql> select * from `select`;
Empty set (0.07 sec)

今度は実行できました。このように、もし万が一キーワードや予約語と衝突したテーブルやカラムと遭遇した場合には落ち着いて見つけ出してバッククオートで囲ってあげましょう。

selectテーブルは、あまり作ることは無いかなと思った方も多いかもしれませんが、groupやlogsなど一般的な名称もMySQLのキーワードと予約後には含まれているため、衝突することがよくあります。

部のクエリビルダやマイグレーションツールでは、デフォルトでそのような挙動になっているので、気が付かないで使っている場合も多くあると思います。実際にSQLを打って操作しようとしたタイミングで気がついて慌てる、ということもあるかと思うので、頭の片隅に入れておいても損はないと思います。

キーワードと予約語を調べる

キーワードと予約語の調べ方ですが、大きく分けて二通りありまして、1つはMySQL公式のドキュメントの9.3 キーワードと予約語を見る方法です。

もう1つ、information_schema.KEYWORDSを見る方法があります。見る方法は簡単で、以下のクエリを実行すると確認することができます。

mysql> select * from information_schema.KEYWORDS;

実行すると、以下のように結果がズラッと表示されます。

+----------------------------------------+----------+
| WORD                                   | RESERVED |
+----------------------------------------+----------+
| ACCESSIBLE                             |        1 |
| ACCOUNT                                |        0 |

            ~省略~

| XOR                                    |        1 |
| YEAR                                   |        0 |
| YEAR_MONTH                             |        1 |
| ZEROFILL                               |        1 |
| ZONE                                   |        0 |
+----------------------------------------+----------+
747 rows in set (0.04 sec)

WORDがキーワードと予約語の文字列になります。RESERVEDが1のものが予約語を表していて、0のものがキーワードとなります。

簡単にgroup byを取って数えてみましたが、キーワードに対して予約語は半分となっているようです。

mysql> select RESERVED, count(*) from information_schema.KEYWORDS group by RESERVED;
+----------+----------+
| RESERVED | count(*) |
+----------+----------+
|        1 |      261 |
|        0 |      486 |
+----------+----------+
2 rows in set (0.05 sec)

RESERVEDに入ってる単語から、将来どんな機能が追加されるのか、楽しみにするのも良いかもしれません。

まとめ

今回は、MySQLのキーワードや予約語とカラムやテーブルが衝突してしまった時にどういったことが起こるのかを紹介しました。

自分がSQLを使って作成する場合は、SQLの実行が失敗した時点で類似語を調べて対応という事もできますが、たとえば一部のクエリビルダやマイグレーション用ツールでは、気を効かせて「`」⁠バッククオート)で囲ってくれて作り込んでしまう場合があります。

そしてツールから作ったは良いものの、SQLで扱うことができない、と慌ててしまうかもしれませんが、落ち着いてキーワードや予約後と衝突が発生している箇所が無いか確認し、もし存在した場合はテーブルやカラム名を「`」で囲ってあげましょう。

そういったことが続くようであれば、カラム名の変更を検討しても良いかもしれません。また、SQLを書く際に、⁠`」で囲う習慣を付けても良いのかもしれません。

おすすめ記事

記事・ニュース一覧