Generated_Column(生成列)とJSONデータを扱う場合に考慮しておく事
MySQL5.7.9とMySQL5.7.10以降で挙動が異なる為、念の為こちらにメモしておきます。
基本的には、MySQL5.7.10で改善されたという事になります。
http://bugs.mysql.com/bug.php?id=79552
>I think the behaviour you observed in 5.7.9 was actually a bug.
>The bug was fixed in 5.7.10, which is why you see different results now. There is some discussion about this in bug#78464 and bug#76834.
関連要望チケット from Morganさん
https://bugs.mysql.com/bug.php?id=78736
root@localhost [NEW57]> select @@version; +-------------------------------------------+ | @@version | +-------------------------------------------+ | 5.7.10-enterprise-commercial-advanced-log | +-------------------------------------------+ 1 row in set (0.00 sec)
確認に利用したテーブル定義
root@localhost [NEW57]> show create table features\G *************************** 1. row *************************** Table: features Create Table: CREATE TABLE `features` ( `id` int(11) NOT NULL AUTO_INCREMENT, `feature` json NOT NULL, `feature_type` varchar(30) GENERATED ALWAYS AS (json_extract(`feature`,'$.type')) VIRTUAL, `feature_street` varchar(30) GENERATED ALWAYS AS (json_extract(`feature`,'$.properties.STREET')) VIRTUAL, PRIMARY KEY (`id`), KEY `idx_feature_type` (`feature_type`), KEY `idx_feature_street` (`feature_street`) ) ENGINE=InnoDB AUTO_INCREMENT=206561 DEFAULT CHARSET=utf8mb4 1 row in set (0.00 sec)
上記で定義された生成列からJSONデータをSELECTする場合
生成列のデータは””を含む為、SELECTした場合には””を付けなければいけない。
root@localhost [NEW57]> select id,feature_street from NEW57.features where feature_street = 'MARKET' limit 1; Empty set (0.00 sec) root@localhost [NEW57]> select id,feature_street from NEW57.features where feature_street = '"MARKET"' limit 1; +-------+----------------+ | id | feature_street | +-------+----------------+ | 12250 | "MARKET" | +-------+----------------+ 1 row in set (0.00 sec)
上記の挙動としては、CASTした時に”(ダブルクオート)をそのまま付けるからという事のようです。
CASTしないでそのままjson_extract関数でSELECTした場合は”(ダブルクオート)は不要です。詳細は以下の例を参照下さい。
root@localhost [NEW57]> select * from NEW57.features where cast(json_extract(feature,'$.properties.STREET') as char) = 'MARKET' limit 1; Empty set (1.03 sec) root@localhost [NEW57]> select * from NEW57.features where cast(json_extract(feature,'$.properties.STREET') as char) = '"MARKET"' limit 1; +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ | id | feature | feature_type | feature_street | +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ | 12250 | {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[[-122.39836263491878, 37.79189388899312, 0], [-122.39845248797837, 37.79233030084018, 0], [-122.39768507706792, 37.7924280850133, 0], [-122.39836263491878, 37.79189388899312, 0]]]}, "properties": {"TO_ST": "388", "BLKLOT": "0265003", "STREET": "MARKET", "FROM_ST": "388", "LOT_NUM": "003", "ST_TYPE": "ST", "ODD_EVEN": "E", "BLOCK_NUM": "0265", "MAPBLKLOT": "0265003"}} | "Feature" | "MARKET" | +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ 1 row in set (0.05 sec) root@localhost [NEW57]> select * from NEW57.features where json_extract(feature,'$.properties.STREET') = '"MARKET"' limit 1; Empty set (1.36 sec) root@localhost [NEW57]> select * from NEW57.features where json_extract(feature,'$.properties.STREET') = 'MARKET' limit 1; +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ | id | feature | feature_type | feature_street | +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ | 12250 | {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[[-122.39836263491878, 37.79189388899312, 0], [-122.39845248797837, 37.79233030084018, 0], [-122.39768507706792, 37.7924280850133, 0], [-122.39836263491878, 37.79189388899312, 0]]]}, "properties": {"TO_ST": "388", "BLKLOT": "0265003", "STREET": "MARKET", "FROM_ST": "388", "LOT_NUM": "003", "ST_TYPE": "ST", "ODD_EVEN": "E", "BLOCK_NUM": "0265", "MAPBLKLOT": "0265003"}} | "Feature" | "MARKET" | +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ 1 row in set (0.08 sec)
JSON_EXTRACTと生成列を常に同じように”(ダブルクオート)を利用する事無く比較するには?
結論としては,CASTしてしまうと”(ダブルクオート)はついてしまうので、生成列を作る時に以下の例のように、
json_unquoteとjson_extractを同時に使う事で常に”(ダブルクオート)を利用する事無くJSONデータを参照する事が可能になります。
CREATE TABLE `features_with_unquote` ( `id` int(11) NOT NULL AUTO_INCREMENT, `feature` json NOT NULL, `feature_type` varchar(30) GENERATED ALWAYS AS (json_unquote(json_extract(`feature`,'$.type'))) VIRTUAL, `feature_street` varchar(30) GENERATED ALWAYS AS (json_unquote(json_extract(`feature`,'$.properties.STREET'))) VIRTUAL, PRIMARY KEY (`id`), KEY `idx_feature_type` (`feature_type`), KEY `idx_feature_street` (`feature_street`) ) ENGINE=InnoDB AUTO_INCREMENT=206561 DEFAULT CHARSET=utf8mb4; root@localhost [NEW57]> insert into features_with_unquote(id,feature) select id,feature from features; Query OK, 206560 rows affected (9.62 sec) Records: 206560 Duplicates: 0 Warnings: 0
json_extractでJSONデータを参照
root@localhost [NEW57]> select * from NEW57.features_with_unquote where json_extract(feature,'$.properties.STREET') = 'MARKET' limit 1; +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ | id | feature | feature_type | feature_street | +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ | 12250 | {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[[-122.39836263491878, 37.79189388899312, 0], [-122.39845248797837, 37.79233030084018, 0], [-122.39768507706792, 37.7924280850133, 0], [-122.39836263491878, 37.79189388899312, 0]]]}, "properties": {"TO_ST": "388", "BLKLOT": "0265003", "STREET": "MARKET", "FROM_ST": "388", "LOT_NUM": "003", "ST_TYPE": "ST", "ODD_EVEN": "E", "BLOCK_NUM": "0265", "MAPBLKLOT": "0265003"}} | Feature | MARKET | +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ 1 row in set (0.04 sec)
生成列の参照(json_unquoteとjson_extractで生成)
root@localhost [NEW57]> select * from NEW57.features_with_unquote where feature_street = 'MARKET' limit 1; +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ | id | feature | feature_type | feature_street | +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ | 12250 | {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[[-122.39836263491878, 37.79189388899312, 0], [-122.39845248797837, 37.79233030084018, 0], [-122.39768507706792, 37.7924280850133, 0], [-122.39836263491878, 37.79189388899312, 0]]]}, "properties": {"TO_ST": "388", "BLKLOT": "0265003", "STREET": "MARKET", "FROM_ST": "388", "LOT_NUM": "003", "ST_TYPE": "ST", "ODD_EVEN": "E", "BLOCK_NUM": "0265", "MAPBLKLOT": "0265003"}} | Feature | MARKET | +-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------------+ 1 row in set (0.00 sec)
こちらの処理により常に、”(ダブルクオート)を付けずにデータが参照できるようになりました。
参照: 8.3.9 Optimizer Use of Generated Column Indexes
PlanetMySQL Voting: Vote UP / Vote DOWN