この記事はQiita Tech Festa 2025の投稿記事です。
MySQL 8.0の新機能
MySQL 8.0 から REGEXP_LIKE
と REGEXP_REPLACE
による正規表現クエリが使えるようになりました。
これまでも RLIKE
で正規表現によるSELECTなどはできていたようですが、MySQL 8.0で大幅に機能強化されたそうです。
正規表現によるクエリはインデックスが効かないとのことで、あまり使い道はないと思われますがいつか使う時があるかもしれないので軽く検証してみたいと思います。
「京都」と「東京都」を見分けたい
テキスト検索時のあるあるとして、「京都」を検索すると「東京都」もひっかかってしまう問題がありますよね。
正規表現が使えればこの問題もある程度緩和できるかもしれません。
検証
データベースとテーブルの準備
まずはテスト用のDBとテーブルを作成します。
CREATE DATABASE IF NOT EXISTS regex_test_simple
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;
USE regex_test_simple;
DROP TABLE IF EXISTS sample_texts;
CREATE TABLE sample_texts (
id INT AUTO_INCREMENT PRIMARY KEY,
content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci
);
サンプルデータ
「京都」のみ、「東京」のみ、その両方を含むデータをいくつか投入します。
INSERT INTO sample_texts (content) VALUES
-- 京都のみ
('昨日、京都府に旅行しました。'),
('京都は寺が多くて落ち着く。'),
('京都府庁前で待ち合わせしました。'),
('京都府の南部は奈良にも近い。'),
('京都府警が会見を開きました。'),
-- 東京のみ
('私は東京都に住んでいます。'),
('東京都庁は新宿にあります。'),
('東京都下の小さな町で育ちました。'),
('東京都知事の発言が話題に。'),
-- 両方含む
('京都と東京都を比較するのは難しい。'),
('東京都から京都への出張は新幹線が便利です。'),
('私は京都に住んでいますが、以前は東京都にいました。'),
('東京都と京都府の文化はかなり異なります。'),
('旅行先として京都も東京都も人気があります。');
正規表現でクエリしてみる(REGEXP_LIKE)
正規表現の先読みを使用し、 「東」が先行しない「京都」を含む
というパターンでクエリしてみます。
SELECT * FROM sample_texts
WHERE REGEXP_LIKE(content, '(?<!東)京都');
MySQL [regex_test_simple]> SELECT * FROM sample_texts
-> WHERE REGEXP_LIKE(content, '(?<!東)京都');
+----+-----------------------------------------------------------------------------+
| id | content |
+----+-----------------------------------------------------------------------------+
| 1 | 昨日、京都府に旅行しました。 |
| 2 | 京都は寺が多くて落ち着く。 |
| 3 | 京都府庁前で待ち合わせしました。 |
| 4 | 京都府の南部は奈良にも近い。 |
| 5 | 京都府警が会見を開きました。 |
| 10 | 京都と東京都を比較するのは難しい。 |
| 11 | 東京都から京都への出張は新幹線が便利です。 |
| 12 | 私は京都に住んでいますが、以前は東京都にいました。 |
| 13 | 東京都と京都府の文化はかなり異なります。 |
| 14 | 旅行先として京都も東京都も人気があります。 |
+----+-----------------------------------------------------------------------------+
10 rows in set (0.009 sec)
良い感じですね。
京都に言及しているテキストのみヒットしました。
10~14は東京も含まれていますが、京都に言及しているテキストなのでヒットして欲しいかったのでこれで理想通りです。
マッチした結果を表示してみる(REGEXP_SUBSTR)
REGEXP_SUBSTR
を使ってマッチ文字列を取得することもできるようなので、試してみます。
SELECT
id,
REGEXP_SUBSTR(content, '(?<!東)京都') AS matched_part
FROM sample_texts
WHERE REGEXP_LIKE(content, '(?<!東)京都');
MySQL [regex_test_simple]> SELECT
-> id,
-> REGEXP_SUBSTR(content, '(?<!東)京都') AS matched_part
-> FROM sample_texts
-> WHERE REGEXP_LIKE(content, '(?<!東)京都');
+----+--------------+
| id | matched_part |
+----+--------------+
| 1 | 京都 |
| 2 | 京都 |
| 3 | 京都 |
| 4 | 京都 |
| 5 | 京都 |
| 10 | 京都 |
| 11 | 京都 |
| 12 | 京都 |
| 13 | 京都 |
| 14 | 京都 |
+----+--------------+
10 rows in set (0.038 sec)
結果は当然全部同じですね。想定通りです。
置換を試してみる(REGEXP_REPLACE)
置換も試してみます。
UPDATE sample_texts
SET content = REGEXP_REPLACE(
REGEXP_REPLACE(
REGEXP_REPLACE(content, '東京都', 'TOKYO'),
'(?<!東)京都', 'KYOTO'
),
'東京', 'TOKYO'
);
MySQL [regex_test_simple]> UPDATE sample_texts
-> SET content = REGEXP_REPLACE(
-> REGEXP_REPLACE(
-> REGEXP_REPLACE(content, '東京都', 'TOKYO'),
-> '(?<!東)京都', 'KYOTO'
-> ),
-> '東京', 'TOKYO'
-> );
Query OK, 14 rows affected (0.005 sec)
Rows matched: 14 Changed: 14 Warnings: 0
MySQL [regex_test_simple]> select * from sample_texts;
+----+------------------------------------------------------------------------+
| id | content |
+----+------------------------------------------------------------------------+
| 1 | 昨日、KYOTO府に旅行しました。 |
| 2 | KYOTOは寺が多くて落ち着く。 |
| 3 | KYOTO府庁前で待ち合わせしました。 |
| 4 | KYOTO府の南部は奈良にも近い。 |
| 5 | KYOTO府警が会見を開きました。 |
| 6 | 私はTOKYOに住んでいます。 |
| 7 | TOKYO庁は新宿にあります。 |
| 8 | TOKYO下の小さな町で育ちました。 |
| 9 | TOKYO知事の発言が話題に。 |
| 10 | KYOTOとTOKYOを比較するのは難しい。 |
| 11 | TOKYOからKYOTOへの出張は新幹線が便利です。 |
| 12 | 私はKYOTOに住んでいますが、以前はTOKYOにいました。 |
| 13 | TOKYOとKYOTO府の文化はかなり異なります。 |
| 14 | 旅行先としてKYOTOもTOKYOも人気があります。 |
+----+------------------------------------------------------------------------+
14 rows in set (0.001 sec)
置換できました。便利ですね。
でも何に使えるんでしょうね・・・?
本番データのメールアドレスをテスト用に置換するとか?
おわり
以上、MySQL 8.0の正規表現を軽く検証してみました。
他にもまだ新機能があるようなのでまた機会があれば検証してみたいと思います。