
{"id":1515,"date":"2015-11-17T16:21:29","date_gmt":"2015-11-17T15:21:29","guid":{"rendered":"http:\/\/dasini.net\/blog\/?p=1515"},"modified":"2019-04-02T10:10:30","modified_gmt":"2019-04-02T09:10:30","slug":"30-mins-with-json-in-mysql","status":"publish","type":"post","link":"https:\/\/dasini.net\/blog\/2015\/11\/17\/30-mins-with-json-in-mysql\/","title":{"rendered":"30 mins with JSON in MySQL"},"content":{"rendered":"<p><em><a href=\"http:\/\/dasini.net\/blog\/2015\/11\/17\/30-mins-avec-json-en-mysql\/\">Lire cet article en fran\u00e7ais<\/a><\/em><\/p>\n<p><em><span style=\"text-decoration: underline;\">Note<\/span><\/em>: <i>This article is inspired by <\/i><a href=\"http:\/\/www.tocker.ca\/\" target=\"_blank\" rel=\"noopener noreferrer\"><i>Morgan Tocker<\/i><\/a><i>&lsquo;s talk <\/i><a href=\"http:\/\/www.slideshare.net\/morgo\/mysql-57-json\" target=\"_blank\" rel=\"noopener noreferrer\"><i>MySQL 5.7 + JSON<\/i><\/a>.<\/p>\n<p><em><span style=\"text-decoration: underline;\">Note<\/span> 2:\u00a0You may also be interested by\u00a0<strong><a href=\"http:\/\/dasini.net\/blog\/2018\/07\/23\/30-mins-with-mysql-json-functions\/\" target=\"_blank\" rel=\"noopener noreferrer\">30 mins with MySQL JSON functions<\/a><\/strong><\/em><\/p>\n<p><span style=\"text-decoration: underline;\"><em>Note<\/em><\/span> 3: Handling JSON documents could be also done with <a href=\"http:\/\/dasini.net\/blog\/2019\/04\/02\/mysql-json-document-store\/\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL Document Store<\/a>.<\/p>\n<p>As you may know <a href=\"http:\/\/dev.mysql.com\/downloads\/mysql\/5.7.html\" target=\"_blank\" rel=\"noopener noreferrer\">MySQL 5.7<\/a> is <a href=\"https:\/\/www.oracle.com\/corporate\/pressrelease\/mysql-5-7-ga-101915.html\" target=\"_blank\" rel=\"noopener noreferrer\">GA<\/a> and has over than <a href=\"http:\/\/www.thecompletelistoffeatures.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">150 new features<\/a>. One of them is a <b>Native JSON Data Type and JSON Functions<\/b>: <i>\u00ab\u00a0Allows for efficient and flexible storage, search and manipulation of schema-less data. Enhancements include a new internal binary format, support for easy integration within SQL, and index management on the JSON Documents using generated columns\u00a0\u00bb<\/i>.<\/p>\n<p>Sounds interesting! Let&rsquo;s take half an hour to have a foretaste&#8230;<\/p>\n<h1>Get JSON documents<\/h1>\n<p>First let&rsquo;s get data in JSON format. <a href=\"https:\/\/github.com\/zemirco\" target=\"_blank\" rel=\"noopener noreferrer\">Mirco Zeiss<\/a> provides a really big JSON file representing san francisco&rsquo;s subdivision parcels (from <a href=\"https:\/\/data.sfgov.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">SF Open Data<\/a>) at <a href=\"https:\/\/github.com\/zemirco\/sf-city-lots-json\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/zemirco\/sf-city-lots-json<\/a><\/p>\n<p>To use these data some tweaks are necessary:<\/p>\n<pre class=\"lang:sh decode:true\">$ grep \"^{ \\\"type\" citylots.json &gt; properties.json\n\n$ head -n1 properties.json \n{  \n   \"type\":\"Feature\",\n   \"properties\":{  \n      \"MAPBLKLOT\":\"0001001\",\n      \"BLKLOT\":\"0001001\",\n      \"BLOCK_NUM\":\"0001\",\n      \"LOT_NUM\":\"001\",\n      \"FROM_ST\":\"0\",\n      \"TO_ST\":\"0\",\n      \"STREET\":\"UNKNOWN\",\n      \"ST_TYPE\":null,\n      \"ODD_EVEN\":\"E\"\n   },\n   \"geometry\":{  \n      \"type\":\"Polygon\",\n      \"coordinates\":[  \n         [  \n            [  \n               -122.422003528252475,\n               37.808480096967251,\n               0.0\n            ],\n            [  \n               -122.422076013325281,\n               37.808835019815085,\n               0.0\n            ],\n            [  \n               -122.421102174348633,\n               37.808803534992904,\n               0.0\n            ],\n            [  \n               -122.421062569067274,\n               37.808601056818148,\n               0.0\n            ],\n            [  \n               -122.422003528252475,\n               37.808480096967251,\n               0.0\n            ]\n         ]\n      ]\n   }\n}<\/pre>\n<p>Looks good!<\/p>\n<p>Note: <i>The size of JSON documents stored in JSON columns is limited to the value of the <\/i><a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/server-system-variables.html#sysvar_max_allowed_packet\" target=\"_blank\" rel=\"noopener noreferrer\"><i>max_allowed_packet<\/i><\/a><i> system variable. (While the server manipulates a JSON value internally in memory, it can be larger; the limit applies when the server stores it.)<\/i>.<\/p>\n<p>Our JSON document will be stored in an InnoDB table: <i>features<\/i><\/p>\n<pre class=\"lang:mysql decode:true \">CREATE TABLE features (\n id int(11) NOT NULL AUTO_INCREMENT,\n feature json NOT NULL,\n PRIMARY KEY (id)\n) ENGINE=InnoDB;<\/pre>\n<p>Another way to store JSON documents is to put them in a string (VARCHAR or TEXT).<\/p>\n<p>Let&rsquo;s see if there are some differences between JSON documents stored in a string or in a JSON column.<\/p>\n<pre class=\"lang:mysql decode:true\">CREATE TABLE features_TEXT (\n id int(11) NOT NULL AUTO_INCREMENT,\n feature longtext NOT NULL,\n PRIMARY KEY (id)\n) ENGINE=InnoDB;<\/pre>\n<p>Note: <em>TEXT type is not large enough to handle our JSON data. (ERROR 1406 (22001): Data too long for column &lsquo;feature&rsquo; at row 17360). LONGTEXT will do the job<\/em>.<\/p>\n<h1>Populate tables<\/h1>\n<p>Note: <i>In order to have a better idea of query execution time on my old (and not so stable) laptop with a small MySQL config (e.g. Buffer pool = 128MB), I ran the queries many time using <\/i><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/mysqlslap.html\" target=\"_blank\" rel=\"noopener noreferrer\"><i>mysqlslap<\/i><\/a><i>: mysqlslap -c1 -i &lt;N&gt; { Concurrency = 1 \/ Iteration &gt; 20 (depending on the query duration) }<\/i><\/p>\n<p>So most of the time I&rsquo;ll show mysqlslap output e.g. \u00ab\u00a0<i>Minimum number of seconds to run all queries: 59.392 seconds<\/i>\u00a0\u00bb<\/p>\n<p>provides by mysqlslap instead of regular query output e.g. \u00ab\u00a0<em>Query OK, 206560 rows affected (59.39 sec)<\/em>\u00ab\u00a0.<\/p>\n<p><span style=\"text-decoration: underline;\">Copy JSON data in features<\/span><\/p>\n<pre class=\"lang:mysql decode:true\">LOAD DATA INFILE 'properties.json' INTO TABLE features (feature);<\/pre>\n<p><span style=\"color: #993300;\">Minimum number of seconds to run all queries: <b>59.392 seconds<\/b><\/span><\/p>\n<p><span style=\"text-decoration: underline;\">Copy JSON data in features_TEXT<\/span><\/p>\n<pre class=\"lang:mysql decode:true\">LOAD DATA INFILE 'properties.json' INTO TABLE features_TEXT (feature);<\/pre>\n<p><span style=\"color: #993300;\">Minimum number of seconds to run all queries: <b>39.784 seconds<\/b><\/span><\/p>\n<p>Loading 206560 records into my respective tables shows performance difference about 40% slower in JSON column compare to TEXT.<\/p>\n<p>However, be aware that MySQL JSON data type provides:<\/p>\n<ul>\n<li><b>Automatic validation of JSON documents stored in JSON columns<\/b>. Meaning that invalid documents produce an error.<\/li>\n<li><b>Optimized storage format<\/b>. JSON documents stored in JSON columns are converted to an internal format that permits quick read access to document elements. When the server later must read a JSON value stored in this binary format, the value need not be parsed from a text representation. The binary format is structured to enable the server to look up subobjects or nested values directly by key or array index without reading all values before or after them in the document.<\/li>\n<\/ul>\n<p>Nothing comparable with the TEXT data type, in other words these features have a cost, logic and fair!<\/p>\n<p>Let&rsquo;s have a look on tables metadata:<\/p>\n<pre class=\"lang:mysql decode:true\">SHOW TABLE STATUS LIKE 'features'\\G\n*************************** 1. row ***************************\n          Name: features\n        Engine: InnoDB\n       Version: 10\n    Row_format: Dynamic\n          Rows: 184218\nAvg_row_length: 1250\n   Data_length: 230326272  #220 MB\n  Index_length: 0\n     Data_free: 3145728<\/pre>\n<pre class=\"lang:mysql decode:true\">SHOW TABLE STATUS LIKE 'features_TEXT'\\G\n*************************** 1. row ***************************\n          Name: features_TEXT\n        Engine: InnoDB\n       Version: 10\n    Row_format: Dynamic\n          Rows: 188784\nAvg_row_length: 1370\n   Data_length: 258654208  #247 MB\n  Index_length: 0\n     Data_free: 4194304<\/pre>\n<p>Interesting point here, LONGTEXT data type consume more space than the JSON (optimized storage format) data type, about <b>20% more<\/b>.<\/p>\n<h1>Return data from a JSON document<\/h1>\n<p>MySQL 5.7 provides a bunch of <a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/json-functions.html\">JSON functions<\/a>.<\/p>\n<p><a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/json-search-functions.html#function_json-extract\">JSON_EXTRACT<\/a> returns data from a JSON document. Furthermore since MySQL 5.7.9 you can use <b>inlined JSON path expressions<\/b> that simplifies queries that deal with JSON data and make them more human-readable:<\/p>\n<p>e.g.<\/p>\n<p><span style=\"color: #0000ff;\">JSON_EXTRACT(col, \u00ab\u00a0$.json_field\u00a0\u00bb)<\/span> is similar to<span style=\"color: #0000ff;\"> col-&gt;\u00a0\u00bb$.json_field\u00a0\u00bb<\/span><\/p>\n<p>So how about retrieve our JSON documents?<\/p>\n<p><span style=\"text-decoration: underline;\">Table with JSON data type<\/span><\/p>\n<pre class=\"lang:default decode:true\">SELECT DISTINCT feature-&gt;\"$.type\" AS json_extract FROM features\\G\n*************************** 1. row ***************************\n          id: 1\n select_type: SIMPLE\n       table: features\n        type: ALL\npossible_keys: NULL\n         key: NULL\n     key_len: NULL\n         ref: NULL\n        rows: 182309\n    filtered: 100.00\n       Extra: Using temporary<\/pre>\n<p><span style=\"color: #993300;\">Minimum number of seconds to run all queries: <b>4.470 seconds<\/b><\/span><\/p>\n<p><span style=\"text-decoration: underline;\">Table with TEXT data type<\/span><\/p>\n<pre class=\"lang:mysql decode:true \">SELECT DISTINCT feature-&gt;\"$.type\" AS json_extract FROM features_TEXT\\G\n*************************** 1. row ***************************\n          id: 1\n select_type: SIMPLE\n       table: features_TEXT\n        type: ALL\npossible_keys: NULL\n         key: NULL\n     key_len: NULL\n         ref: NULL\n        rows: 177803\n    filtered: 100.00\n       Extra: Using temporary<\/pre>\n<p><span style=\"color: #993300;\">Minimum number of seconds to run all queries: <b>29.365 seconds<\/b><\/span><\/p>\n<p>Get these documents implies a full table scan (no surprise).<\/p>\n<p>However we can see the power of the MySQL JSON internal format that permits quick read access to document elements.<\/p>\n<p>In this example the query execution time is about <b>7 times faster<\/b> with JSON data type compare to TEXT.<\/p>\n<h1>Generated column<\/h1>\n<p>JSON columns cannot be indexed. BUT you can work around this restriction by creating an index on a <a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/create-table.html#create-table-secondary-indexes-virtual-columns\"><b>generated column<\/b><\/a> that extracts a scalar value from the JSON column. Generated columns can either be materialized (stored) or non-materialized (virtual).<\/p>\n<p>Create a generated column is quite easy. And a VIRTUAL one is costless because column values are not stored, but are evaluated when rows are read, immediately after any BEFORE triggers.<\/p>\n<p><b>A virtual column takes no storage<\/b>. It&rsquo;s usually what you&rsquo;ll need in real life.<\/p>\n<p>Last but not least it&rsquo;s only about metadata change so adding a virtual column is fast (<b>no table rebuild<\/b>). It only requires a quick system table update that registers the new metadata.<\/p>\n<pre class=\"lang:mysql decode:true\">ALTER TABLE features ADD feature_type VARCHAR(30) AS (feature-&gt;\"$.type\") VIRTUAL;\nQuery OK, 0 rows affected (0.08 sec)\nRecords: 0  Duplicates: 0  Warnings: 0<\/pre>\n<pre class=\"lang:mysql decode:true\">ALTER TABLE features_TEXT ADD feature_type VARCHAR(30) AS  (feature-&gt;\"$.type\") VIRTUAL;\nQuery OK, 0 rows affected (0.10 sec)\nRecords: 0  Duplicates: 0  Warnings: 0<\/pre>\n<p>Note: <i>The disadvantage of such approach is that values are stored twice; once as the value of the generated column and once in the index<\/i>.<\/p>\n<p>New table descriptions are now:<\/p>\n<pre class=\"lang:mysql decode:true \">CREATE TABLE features (\n id int(11) NOT NULL AUTO_INCREMENT,\n feature json NOT NULL,\n feature_type varchar(30) GENERATED ALWAYS AS (feature-&gt;\"$.type\") VIRTUAL,\n PRIMARY KEY (id)\n) ENGINE=InnoDB<\/pre>\n<pre class=\"lang:mysql decode:true\">CREATE TABLE features_TEXT (\n id int(11) NOT NULL AUTO_INCREMENT,\n feature longtext NOT NULL,\n feature_type varchar(30) GENERATED ALWAYS AS (feature-&gt;\"$.type\") VIRTUAL,\n PRIMARY KEY (id)\n) ENGINE=InnoDB<\/pre>\n<p>Let&rsquo;s have a look on table metadata:<\/p>\n<pre class=\"lang:mysql decode:true\">ANALYZE TABLE features, features_TEXT;\n\nSHOW TABLE STATUS LIKE 'features'\\G\n*************************** 1. row ***************************\n          Name: features\n        Engine: InnoDB\n       Version: 10\n    Row_format: Dynamic\n          Rows: 184218\nAvg_row_length: 1250\n   Data_length: 230326272  #220 MB\n  Index_length: 0\n     Data_free: 314572<\/pre>\n<pre class=\"lang:mysql decode:true\">SHOW TABLE STATUS LIKE 'features_TEXT'\\G\n*************************** 1. row ***************************\n          Name: features_TEXT\n        Engine: InnoDB\n       Version: 10\n    Row_format: Dynamic\n          Rows: 188784\nAvg_row_length: 1370\n   Data_length: 258654208  #247 MB\n  Index_length: 0\n     Data_free: 4194304<\/pre>\n<p>Identical!<\/p>\n<p>As expected data length is respectively the same.<\/p>\n<p>Is there any cost difference between selecting a JSON documents from the virtual column and the JSON_EXTRACT function?<\/p>\n<pre class=\"lang:mysql decode:true\">SELECT DISTINCT feature_type FROM features\\G\n*************************** 1. row ***************************\n          id: 1\n select_type: SIMPLE\n       table: features\n        type: ALL\npossible_keys: NULL\n         key: NULL\n     key_len: NULL\n         ref: NULL\n        rows: 195195\n    filtered: 100.00\n       Extra: Using temporary<\/pre>\n<p><span style=\"color: #993300;\">Minimum number of seconds to run all queries: <b>2.790 seconds<\/b><\/span><\/p>\n<pre class=\"lang:mysql decode:true \">SELECT DISTINCT feature_type FROM features_TEXT\\G\n*************************** 1. row ***************************\n          id: 1\n select_type: SIMPLE\n       table: features_TEXT\n        type: ALL\npossible_keys: NULL\n         key: NULL\n     key_len: NULL\n         ref: NULL\n        rows: 171004\n    filtered: 100.00\n       Extra: Using temporary<\/pre>\n<p><span style=\"color: #993300;\">Minimum number of seconds to run all queries: <b>25.933 seconds<\/b><\/span><\/p>\n<p>Obviously the QEP is the same: Full Table Scan (FTS).<\/p>\n<p>Anyway 2 comments:<\/p>\n<ul>\n<li>MySQL JSON internal format is still more efficient than TEXT data type, in this example query execution time is about <b>8 times faster<\/b> with JSON.<\/li>\n<\/ul>\n<ul>\n<li>In this example FTS on the virtual generated column (feature_type) is faster than the usage of json_extract function on the JSON document in the SELECT clause (from <b>4.470<\/b> to <b>2.790<\/b>).<\/li>\n<\/ul>\n<h1>Create indexes on generated column<\/h1>\n<p>As of MySQL 5.7.8, InnoDB supports secondary indexes on virtual columns.<\/p>\n<p>Adding or dropping a secondary index on a virtual column is an in-place operation.<\/p>\n<pre class=\"lang:mysql decode:true\">ALTER TABLE features ADD INDEX (feature_type);\nQuery OK, 0 rows affected (5.04 sec)\nRecords: 0  Duplicates: 0  Warnings: 0<\/pre>\n<pre class=\"lang:mysql decode:true\">ALTER TABLE features_TEXT ADD INDEX (feature_type);\nQuery OK, 0 rows affected (27.89 sec)\nRecords: 0  Duplicates: 0  Warnings: 0<\/pre>\n<p>New table descriptions are:<\/p>\n<pre class=\"lang:mysql decode:true\">CREATE TABLE features (\n id` int(11) NOT NULL AUTO_INCREMENT,\n feature` json NOT NULL,\n feature_type varchar(30) GENERATED ALWAYS AS (feature-&gt;\"$.type\") VIRTUAL,\n PRIMARY KEY (id),\n KEY feature_type (feature_type)\n) ENGINE=InnoDB<\/pre>\n<pre class=\"lang:mysql decode:true \">CREATE TABLE features_TEXT (\n id int(11) NOT NULL AUTO_INCREMENT,\n feature longtext NOT NULL,\n feature_type varchar(30) GENERATED ALWAYS AS (feature-&gt;\"$.type\") VIRTUAL,\n PRIMARY KEY (id),\n KEY feature_type (feature_type)\n) ENGINE=InnoDB<\/pre>\n<p>Let&rsquo;s have another look on table metadata:<\/p>\n<pre class=\"lang:mysql decode:true\">ANALYZE TABLE features, features_TEXT;\n\nSHOW TABLE STATUS LIKE 'features'\\G\n*************************** 1. row ***************************\n          Name: features\n        Engine: InnoDB\n       Version: 10\n    Row_format: Dynamic\n          Rows: 180400\nAvg_row_length: 1276\n   Data_length: 230326272  #220 MB\n  Index_length: 5783552    #6 MB\n     Data_free: 5242880<\/pre>\n<pre class=\"lang:mysql decode:true \">SHOW TABLE STATUS LIKE 'features_TEXT'\\G\n*************************** 1. row ***************************\n          Name: features_TEXT\n        Engine: InnoDB\n       Version: 10\n    Row_format: Dynamic\n          Rows: 192445\nAvg_row_length: 1344\n   Data_length: 258654208  #247 MB\n  Index_length: 5783552    #6 MB\n     Data_free: 2097152<\/pre>\n<p>The index on <i>feature_type<\/i> column is materialized. Its size it&rsquo;s approximately 6 MB.<\/p>\n<p>Now because of this index, the query should be more efficient:<\/p>\n<pre class=\"lang:mysql decode:true\">SELECT DISTINCT feature_type FROM features\\G\n*************************** 1. row ***************************\n          id: 1\n select_type: SIMPLE\n       table: features\n        type: index\npossible_keys: feature_type\n         key: feature_type\n     key_len: 33\n         ref: NULL\n        rows: 193763\n    filtered: 100.00\n       Extra: Using index<\/pre>\n<p><span style=\"color: #993300;\">Minimum number of seconds to run all queries: <b>0.178<\/b> seconds<\/span><\/p>\n<pre class=\"lang:mysql decode:true \">SELECT DISTINCT feature_type FROM features_TEXT\\G\n*************************** 1. row ***************************\n          id: 1\n select_type: SIMPLE\n       table: features_TEXT\n        type: index\npossible_keys: feature_type\n         key: feature_type\n     key_len: 33\n         ref: NULL\n        rows: 208134\n    filtered: 100.00\n       Extra: Using index<\/pre>\n<p><span style=\"color: #993300;\">Minimum number of seconds to run all queries: <b>0.178<\/b> seconds<\/span><\/p>\n<p>As expected the optimizer uses the index (feature_type) and the query execution time is much better in both cases (from <b>2.790<\/b> to <b>0.178<\/b> for JSON column).<\/p>\n<h1>Wrapup<\/h1>\n<p>MySQL 5.7 implements native JSON data type support and provides a set of function that allows to <b>Create<\/b>, <b>Search<\/b>, <b>Modify<\/b> JSON values and <b>Return<\/b> JSON attributes values as well. That&rsquo;s good and I guess many developers will be happy to use this new feature.<\/p>\n<p>Generated columns is also an interesting feature. It could be used among others to simulate functional indexes, as a materialized cache for often used expressions\u2026 or like we did to provide index management on the JSON documents.<\/p>\n<p>Give it a try, it definitely worth more than 30 minutes.<\/p>\n<p>Want to know more about MySQL 5.7?<\/p>\n<ul>\n<li><strong><span style=\"color: #ff0000;\">December 8th, @Paris join the <a style=\"color: #ff0000;\" href=\"https:\/\/t.co\/MD6xBB4hKS\" target=\"_blank\" rel=\"noopener noreferrer\">Oracle MySQL Tech Tour Paris<\/a> \u00a0<\/span><\/strong><\/li>\n<li>Other location: <a href=\"http:\/\/www.oracle.com\/events\/emea\/en\/mysql-tech-tour\/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">Oracle MySQL Tech Tour<\/a><\/li>\n<\/ul>\n<h1>Going further<\/h1>\n<ul>\n<li><strong><a href=\"http:\/\/dasini.net\/blog\/2018\/07\/23\/30-mins-with-mysql-json-functions\/\" target=\"_blank\" rel=\"noopener noreferrer\">30 mins with MySQL JSON functions<\/a><\/strong><\/li>\n<\/ul>\n<h2>MySQL Documentation<\/h2>\n<h3>The JSON data type<\/h3>\n<p><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/json.html\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/json.html<\/a><\/p>\n<h3>JSON Functions<\/h3>\n<p><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/json-functions.html\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/json-functions.html<\/a><\/p>\n<h3>CREATE TABLE and Generated Columns<\/h3>\n<p><a href=\"http:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/create-table.html#create-table-generated-columns\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/create-table.html#create-table-generated-columns<\/a><\/p>\n<h2>MySQL Server Blog<\/h2>\n<h3>Native JSON Data Type and Binary Format<\/h3>\n<p><a href=\"http:\/\/mysqlserverteam.com\/json-labs-release-native-json-data-type-and-binary-format\/\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/mysqlserverteam.com\/json-labs-release-native-json-data-type-and-binary-format\/<\/a><\/p>\n<h3>JSON functions<\/h3>\n<p><a href=\"http:\/\/mysqlserverteam.com\/json-labs-release-json-functions-part-1-manipulation-json-data\/\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/mysqlserverteam.com\/json-labs-release-json-functions-part-1-manipulation-json-data\/<\/a><\/p>\n<p><a href=\"http:\/\/mysqlserverteam.com\/mysql-5-7-lab-release-json-functions-part-2-querying-json-data\/\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/mysqlserverteam.com\/mysql-5-7-lab-release-json-functions-part-2-querying-json-data\/<\/a><\/p>\n<p><a href=\"https:\/\/mysqlserverteam.com\/new-json-functions-in-mysql-5-7-22\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/mysqlserverteam.com\/new-json-functions-in-mysql-5-7-22\/<\/a><\/p>\n<h3>Inline JSON Path Expressions in MySQL 5.7<\/h3>\n<p><a href=\"http:\/\/mysqlserverteam.com\/inline-json-path-expressions-in-mysql-5-7\/\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/mysqlserverteam.com\/inline-json-path-expressions-in-mysql-5-7\/<\/a><\/p>\n<h3>Getting Started With MySQL &amp; JSON on Windows<\/h3>\n<p><a href=\"http:\/\/mysqlserverteam.com\/getting-started-with-mysql-json-on-windows\/\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/mysqlserverteam.com\/getting-started-with-mysql-json-on-windows\/<\/a><\/p>\n<h3>Effective Functional Indexes in InnoDB<\/h3>\n<p><a href=\"http:\/\/mysqlserverteam.com\/json-labs-release-effective-functional-indexes-in-innodb\/\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/mysqlserverteam.com\/json-labs-release-effective-functional-indexes-in-innodb\/<\/a><\/p>\n<h2>MySQL 5.7<\/h2>\n<h3>What Is New in MySQL 5.7<\/h3>\n<p><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/mysql-nutshell.html\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/dev.mysql.com\/doc\/refman\/5.7\/en\/mysql-nutshell.html<\/a><\/p>\n<h3>Complete list of new features in MySQL 5.7<\/h3>\n<p><a href=\"http:\/\/www.thecompletelistoffeatures.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/www.thecompletelistoffeatures.com\/<\/a><\/p>\n<p>Thanks for using MySQL!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>MySQL 5.7 is GA and has over than 150 new features. One of them is a Native JSON Data Type and JSON Functions: \u00ab\u00a0Allows for efficient and flexible storage, search and manipulation of schema-less data. Enhancements include a new internal binary format, support for easy integration within SQL, and index management on the JSON Documents using generated columns\u00a0\u00bb.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"footnotes":""},"categories":[257,203],"tags":[308,310,312],"class_list":["post-1515","post","type-post","status-publish","format-standard","hentry","category-json","category-mysql-en","tag-generated-column-en","tag-json-en","tag-nosql-en"],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9LfWW-or","jetpack-related-posts":[{"id":2465,"url":"https:\/\/dasini.net\/blog\/2018\/07\/23\/30-mins-with-mysql-json-functions\/","url_meta":{"origin":1515,"position":0},"title":"30 mins with MySQL JSON functions","author":"Olivier DASINI","date":"23 juillet 2018","format":false,"excerpt":"JSON (JavaScript Object Notation) is a popular way for moving data between various systems, including databases. Starting with 5.7 MySQL implemented a native JSON data type and a set of JSON functions that allows you to perform operations on JSON values.","rel":"","context":"Dans &quot;json&quot;","block_context":{"text":"json","link":"https:\/\/dasini.net\/blog\/category\/json\/"},"img":{"alt_text":"MySQL native JSON data type","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/json_icon.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":8442,"url":"https:\/\/dasini.net\/blog\/2025\/10\/14\/querying-the-unstructured-natural-language-to-sql-for-json-data\/","url_meta":{"origin":1515,"position":1},"title":"Querying the Unstructured: Natural Language to SQL for JSON Data","author":"Olivier DASINI","date":"14 octobre 2025","format":false,"excerpt":"Bridging natural language processing with semi-structured data brings both opportunity and complexity. MySQL HeatWave GenAI\u2019s NL2SQL feature shows how natural language can simplify data interaction \u2014 even for JSON documents. Yet, because JSON embeds both data and metadata within a single column, LLMs may struggle without explicit schema cues. By\u2026","rel":"","context":"Dans &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/dasini.net\/blog\/category\/ai\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/10\/Querying-the-Unstructured-Natural-Language-to-SQL-for-JSON-Data-400.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2874,"url":"https:\/\/dasini.net\/blog\/2019\/04\/02\/mysql-json-document-store\/","url_meta":{"origin":1515,"position":2},"title":"MySQL JSON Document Store","author":"Olivier DASINI","date":"2 avril 2019","format":false,"excerpt":"MySQL 8.0 provides another way to handle JSON documents, actually in a \"Not only SQL\" (NoSQL) approach... In other words, if you need\/want to manage JSON documents (collections) in a non-relational manner, with CRUD (acronym for Create\/Read\/Update\/Delete) operations then you can use MySQL 8.0! Did you know that?","rel":"","context":"Dans &quot;Document Store&quot;","block_context":{"text":"Document Store","link":"https:\/\/dasini.net\/blog\/category\/document-store\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2443,"url":"https:\/\/dasini.net\/blog\/2018\/07\/12\/plenty-of-new-mysql-books\/","url_meta":{"origin":1515,"position":3},"title":"Plenty of new MySQL books","author":"Olivier DASINI","date":"12 juillet 2018","format":false,"excerpt":"In the old days, when we wanted to strengthen our skills the only option was to buy a good book. Nowadays one can find a lot of resources on the Internet, however quality is often poor. Fortunately there are still some great people who are brave enough to write new\u2026","rel":"","context":"Dans &quot;Book&quot;","block_context":{"text":"Book","link":"https:\/\/dasini.net\/blog\/category\/book\/"},"img":{"alt_text":"MySQL and JSON: A Practical Programming Guide - Discover how to use JavaScript Object Notation (JSON) with MySQL","src":"https:\/\/i0.wp.com\/images-na.ssl-images-amazon.com\/images\/I\/416UKa-bH%2BL._SX332_BO1%2C204%2C203%2C200_.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2746,"url":"https:\/\/dasini.net\/blog\/2019\/03\/14\/mysql-functional-indexes\/","url_meta":{"origin":1515,"position":4},"title":"MySQL Functional Indexes","author":"Olivier DASINI","date":"14 mars 2019","format":false,"excerpt":"Since MySQL 5.7 one can put indexes on expressions, aka functional indexes, using generated columns. Basically you first need to use the generated column to define the functional expression, then indexed this column. Quite useful when dealing with JSON functions, you can find an example here and the documentation there.\u2026","rel":"","context":"Dans &quot;Astuce&quot;","block_context":{"text":"Astuce","link":"https:\/\/dasini.net\/blog\/category\/astuce\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":3463,"url":"https:\/\/dasini.net\/blog\/2019\/09\/19\/mysql-8-0-17-new-features-summary\/","url_meta":{"origin":1515,"position":5},"title":"MySQL 8.0.17 &#8211; New Features Summary","author":"Olivier DASINI","date":"19 septembre 2019","format":false,"excerpt":"This presentation is a summary of the MySQL 8.0.17 new features.","rel":"","context":"Dans &quot;Group Replication&quot;","block_context":{"text":"Group Replication","link":"https:\/\/dasini.net\/blog\/category\/group-replication-en\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/1515","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/comments?post=1515"}],"version-history":[{"count":16,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/1515\/revisions"}],"predecessor-version":[{"id":2941,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/1515\/revisions\/2941"}],"wp:attachment":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/media?parent=1515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/categories?post=1515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/tags?post=1515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}