MySQLのJSON型に含まれる値でソートをかける方法 (数値・文字列・Goのtime.Time型)
MySQLのJSON型に含まれている中の値で、ソートをかける方法についてまとめたいと思います。 JSON型に変換したGoのtime.Time型をソートする方法も後半でご紹介します。
準備
まずはMySQLのコンテナとサンプルデータを準備していきます。
DockerでMySQLのコンテナをたてます。(imageはを以下のものを使用します。)
$ docker pull mysql
$ docker run -it --name test -e MYSQL_ROOT_PASSWORD=mysql -d mysql:latest
コンテナの準備ができたらコンテナでMySQLに接続します。
今回使用するMySQLのバージョンは以下の通りです。
$ mysql --version
mysql Ver 8.0.25 for Linux on x86_64 (MySQL Community Server - GPL)
ソート対象のサンプルデータを用意します。
timeに指定されている時間は、Goのtime.Time型をMarshalして算出した値です。
また、0001-01-01T00:00:00Zの値はtime.Time型のゼロ値です。
mysql> CREATE TABLE data (content JSON);
mysql> INSERT INTO data VALUES
('{"type": 1, "name": "aaa", "available": true, "time": "2021-03-25T00:00:00+09:00"}'),
('{"type": 2, "name": "bbb", "available": true, "time": "2021-04-25T00:00:00+09:00"}'),
('{"type": 3, "name": "ccc", "available": true, "time": "2021-05-25T00:00:00+09:00"}'),
('{"type": 4, "name": "ddd", "available": true, "time": "2021-03-01T00:00:00+09:00"}'),
('{"type": 1, "name": "ccc", "available": false, "time": "2021-03-02T00:00:00+09:00"}'),
('{"type": 2, "name": "ddd", "available": false, "time": "2021-03-02T09:25:00+09:00"}'),
('{"type": 3, "name": "bbb", "available": false, "time": "2021-03-02T09:25:01+09:00"}'),
('{"type": 4, "name": "aaa", "available": false, "time": "0001-01-01T00:00:00Z"}'),
('{"type": 1, "name": "eee", "available": true, "time": "0001-01-01T00:00:00Z"}'),
('{"type": 2, "name": "eee", "available": false, "time": "2021-06-25T00:00:00+09:00"}');
永続化した値を確認してみます。
mysql> SELECT * FROM data;
+-------------------------------------------------------------------------------------+
| content |
+-------------------------------------------------------------------------------------+
| {"name": "aaa", "time": "2021-03-25T00:00:00+09:00", "type": 1, "available": true} |
| {"name": "bbb", "time": "2021-04-25T00:00:00+09:00", "type": 2, "available": true} |
| {"name": "ccc", "time": "2021-05-25T00:00:00+09:00", "type": 3, "available": true} |
| {"name": "ddd", "time": "2021-03-01T00:00:00+09:00", "type": 4, "available": true} |
| {"name": "ccc", "time": "2021-03-02T00:00:00+09:00", "type": 1, "available": false} |
| {"name": "ddd", "time": "2021-03-02T09:25:00+09:00", "type": 2, "available": false} |
| {"name": "bbb", "time": "2021-03-02T09:25:01+09:00", "type": 3, "available": false} |
| {"name": "aaa", "time": "0001-01-01T00:00:00Z", "type": 4, "available": false} |
| {"name": "eee", "time": "0001-01-01T00:00:00Z", "type": 1, "available": true} |
| {"name": "eee", "time": "2021-06-25T00:00:00+09:00", "type": 2, "available": false} |
+-------------------------------------------------------------------------------------+
JSON_EXTRACTを使用する方法
JSON_EXTRACTを使用してソートをかけてみたいと思います。
使い方の詳細についてはMySQLリファレンスマニュアルを参考にしてください。
“type”を昇順に並べ替えてみます。
mysql> SELECT * FROM data ORDER BY JSON_EXTRACT(content, "$.type") ASC;
+-------------------------------------------------------------------------------------+
| content |
+-------------------------------------------------------------------------------------+
| {"name": "aaa", "time": "2021-03-25T00:00:00+09:00", "type": 1, "available": true} |
| {"name": "ccc", "time": "2021-03-02T00:00:00+09:00", "type": 1, "available": false} |
| {"name": "eee", "time": "0001-01-01T00:00:00Z", "type": 1, "available": true} |
| {"name": "bbb", "time": "2021-04-25T00:00:00+09:00", "type": 2, "available": true} |
| {"name": "ddd", "time": "2021-03-02T09:25:00+09:00", "type": 2, "available": false} |
| {"name": "eee", "time": "2021-06-25T00:00:00+09:00", "type": 2, "available": false} |
| {"name": "ccc", "time": "2021-05-25T00:00:00+09:00", "type": 3, "available": true} |
| {"name": "bbb", "time": "2021-03-02T09:25:01+09:00", "type": 3, "available": false} |
| {"name": "ddd", "time": "2021-03-01T00:00:00+09:00", "type": 4, "available": true} |
| {"name": "aaa", "time": "0001-01-01T00:00:00Z", "type": 4, "available": false} |
+-------------------------------------------------------------------------------------+
“type”が1〜4で昇順にソートされたことが確認できました。
あわせて”available”にもソートをかけてみます。
mysql> SELECT * FROM data ORDER BY JSON_EXTRACT(content, "$.type") ASC, JSON_EXTRACT(content, "$.available") DESC;
+-------------------------------------------------------------------------------------+
| content |
+-------------------------------------------------------------------------------------+
| {"name": "aaa", "time": "2021-03-25T00:00:00+09:00", "type": 1, "available": true} |
| {"name": "eee", "time": "0001-01-01T00:00:00Z", "type": 1, "available": true} |
| {"name": "ccc", "time": "2021-03-02T00:00:00+09:00", "type": 1, "available": false} |
| {"name": "bbb", "time": "2021-04-25T00:00:00+09:00", "type": 2, "available": true} |
| {"name": "ddd", "time": "2021-03-02T09:25:00+09:00", "type": 2, "available": false} |
| {"name": "eee", "time": "2021-06-25T00:00:00+09:00", "type": 2, "available": false} |
| {"name": "ccc", "time": "2021-05-25T00:00:00+09:00", "type": 3, "available": true} |
| {"name": "bbb", "time": "2021-03-02T09:25:01+09:00", "type": 3, "available": false} |
| {"name": "ddd", "time": "2021-03-01T00:00:00+09:00", "type": 4, "available": true} |
| {"name": "aaa", "time": "0001-01-01T00:00:00Z", "type": 4, "available": false} |
+-------------------------------------------------------------------------------------+
同じ”type”の中で、true>falseの順番になっているので、”available”にもソートがかけられたことが確認できました。
「->」演算子を使用する方法
先ほどはJSON_EXTRACTを使用しましたが、「->」演算子を使用することもできます。 JSON_EXTRACTの省略記法なので、同様の機能を果たします。
リファレンスにも以下のような記述があります。
mysql> SELECT * FROM data ORDER BY content->"$.type" ASC, content->"$.available" DESC;
+-------------------------------------------------------------------------------------+
| content |
+-------------------------------------------------------------------------------------+
| {"name": "aaa", "time": "2021-03-25T00:00:00+09:00", "type": 1, "available": true} |
| {"name": "eee", "time": "0001-01-01T00:00:00Z", "type": 1, "available": true} |
| {"name": "ccc", "time": "2021-03-02T00:00:00+09:00", "type": 1, "available": false} |
| {"name": "bbb", "time": "2021-04-25T00:00:00+09:00", "type": 2, "available": true} |
| {"name": "ddd", "time": "2021-03-02T09:25:00+09:00", "type": 2, "available": false} |
| {"name": "eee", "time": "2021-06-25T00:00:00+09:00", "type": 2, "available": false} |
| {"name": "ccc", "time": "2021-05-25T00:00:00+09:00", "type": 3, "available": true} |
| {"name": "bbb", "time": "2021-03-02T09:25:01+09:00", "type": 3, "available": false} |
| {"name": "ddd", "time": "2021-03-01T00:00:00+09:00", "type": 4, "available": true} |
| {"name": "aaa", "time": "0001-01-01T00:00:00Z", "type": 4, "available": false} |
+-------------------------------------------------------------------------------------+
先程の、JSON_EXTRACTと同様の結果を得ることができました。 JSON_EXTRACTに比べて、「->」演算子の方がSQL文の記述量が少なくなるので、可読性が上がると思います。
Goのtime.Time型をMySQLのDATETIME型にキャストしてソートする方法
timeも先ほど同様に記述すればソートはできますが、あくまでもJSON内の”文字列”での比較となってしまいます。
※正確には、公式リファレンス に以下のように記述されています。
正しく比較するにはMySQLのDATETIME型にキャストすることが安全かと思いますので、実装してみます。
-> は値を抽出するだけの演算子ですが、 ->> は抽出された結果を引用符で囲まない演算子です。
mysql> SELECT * FROM data ORDER BY CAST(content->>"$.time" AS DATETIME) ASC;
+-------------------------------------------------------------------------------------+
| content |
+-------------------------------------------------------------------------------------+
| {"name": "aaa", "time": "0001-01-01T00:00:00Z", "type": 4, "available": false} |
| {"name": "eee", "time": "0001-01-01T00:00:00Z", "type": 1, "available": true} |
| {"name": "ddd", "time": "2021-03-01T00:00:00+09:00", "type": 4, "available": true} |
| {"name": "ccc", "time": "2021-03-02T00:00:00+09:00", "type": 1, "available": false} |
| {"name": "ddd", "time": "2021-03-02T09:25:00+09:00", "type": 2, "available": false} |
| {"name": "bbb", "time": "2021-03-02T09:25:01+09:00", "type": 3, "available": false} |
| {"name": "aaa", "time": "2021-03-25T00:00:00+09:00", "type": 1, "available": true} |
| {"name": "bbb", "time": "2021-04-25T00:00:00+09:00", "type": 2, "available": true} |
| {"name": "ccc", "time": "2021-05-25T00:00:00+09:00", "type": 3, "available": true} |
| {"name": "eee", "time": "2021-06-25T00:00:00+09:00", "type": 2, "available": false} |
+-------------------------------------------------------------------------------------+
日付でソートして出力することができました。
まとめ
MySQLにJSON型で永続化している場合は、指定した値の取得であっても一手間必要になります。 値の抽出やソートについて、公式のリファレンスには様々なケースが記載されているので、より良い書き方があれば取り入れていきたいと思います。
この記事を書いた人
- 創造性を最大限に発揮するとともに、インターネットに代表されるITを活用し、みんなの生活が便利で、豊かで、楽しいものになるようなサービスやコンテンツを考え、創り出し提供しています。
この執筆者の最新記事
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー