MySQL裡面的JSON特性

2021-09-28 11:44:52 字數 4513 閱讀 7060

在我們梳理的開發規範裡面,明確規定對於lob型別的使用原則只有乙個,那就是盡量不要使用。但是很明顯,開發同學走到了我們前面,如果你碰到開發同學使用json資料型別該怎麼建議呢,至少在建議前我們也得了解下json型別的使用要領吧。 

在說json型別之前,我們來說下在沒有json資料型別之前我們是怎麼處理一些複雜的資料對映的。

對於開發語言還是資料庫技術來說,字串處理總是很有魅力的乙個特性,所以我會花更多的精力在這個上面。比如之前做了乙個簡單的測試。裡面用到了一些看起來複雜的字串處理函式find_in_set,substring_index等。

問題的背景是我們為乙個表建立了兩個列col1,col2,然後插入一些屬性值。即col1裡面的屬性值和col2裡面的屬性值是對應的。或者換句話來說,col1裡面存放的是key,col2存放的是value.

create table test1 ( col1 varchar(100),col2 varchar(100)); 

insert test1 select 

'26,59,6', '1502.5,1690,2276.77' union all select 

'59,33,6', '3502.1,1020,2276.77' union all select 

'22,8,59', '1332.6,2900,1520.77'; 

寫入資料之後,表裡的資料分布是這樣的:

mysql> select *from test1;

| col1    | col2                |

| 26,59,6 | 1502.5,1690,2276.77 |

| 59,33,6 | 3502.1,1020,2276.77 |

| 22,8,59 | 1332.6,2900,1520.77 |

3 rows in set (0.00 sec)

現在我們如果要做乙個資料查詢,把key是59的value值查出來,然後需要value值小於2000.

如果使用sql,可能會是這樣的解決方法。

mysql> select col1,col2 

-> from (select *,find_in_set('59',col1) as rn from test1) k 

-> where substring_index(concat(',',substring_index(col2,',',rn)),',',-1) 

->  <'2000'; 

| col1    | col2                |

| 26,59,6 | 1502.5,1690,2276.77 |

| 22,8,59 | 1332.6,2900,1520.77 |

2 rows in set (0.00 sec)

當然可能你會有更好的解決方案,但是看起來似乎也不是乙個很好的解決方法,比如這種設計中,如果要加乙個字段,那簡直就是災難性的。

在這種模式下,使用json其實也是一種改進思路,當然這是在mysql 5.7之後了。

我們建立的表為json_test,然後插入兩行記錄。

create table json_test ( uid int auto_increment,data json,primary key(uid))engine=innodb;

insert into json_test values (null,'');

insert into json_test values (null,'');

現在到了json發揮作用的時候了,如果要查詢出資料,我們可以使用類似引用的語法"->"即可。所以我們可以把資料很方便的解析出來。

mysql>  select data->"$.name" as name,(data->"$.location") from json_test group by name;

| name       | (data->"$.location") |

| "jeanron"  | "beijing"            |

| "jianrong" | "gansu"              |

2 rows in set (0.00 sec)

在這種模式下,上面的第乙個難題其實就完全可以使用這種方式來解決了。

在這個基礎上我們更近一步,在5.7裡面還有輔助的特性虛擬列和相關的索引,可以提高我們查詢的效率。我們新增乙個虛擬列user_name.

alter table json_test  add user_name varchar(128) generated always as(json_extract(data,'$.name')) virtual;

使用desc檢視,其實可以看到user_name的屬性是相對特殊的。

mysql> desc json_test;

| field     | type         | null | key | default | extra             |

| uid       | int(11)      | no   | pri | null    | auto_increment    |

| data      | json         | yes  |     | null    |                   |

| user_name | varchar(128) | yes  | mul | null    | virtual generated |

3 rows in set (0.00 sec)

然後在這個基礎上新增乙個索引。

alter table json_test add index idx_username(user_name);

使用show create table的方式檢視建表ddl可以清晰的看到是有乙個輔助索引。

create table `json_test` (

`uid` int(11) not null auto_increment,

`data` json default null,

`user_name` varchar(128) generated always as (json_extract(`data`,'$.name')) virtual,

primary key (`uid`),

key `idx_username` (`user_name`)

) engine=innodb auto_increment=3 default charset=utf8 |

然後我們再次查詢,注意在這裡的user_name使用了雙引號單引號混合的方式。

mysql>  select user_name,(data->"$.location") from json_test where user_name = '"jianrong"';

| user_name  | (data->"$.location") |

| "jianrong" | "gansu"              |

1 row in set (0.00 sec)

如果帶有疑惑,我們只有單引號是否可以,答案會讓你失望。

mysql>  select user_name,(data->"$.location") from json_test where user_name = 'jianrong';

empty set (0.00 sec)

所以不是嚴格意義上100%的相容性,至少在各式統一上我們還是需要一些額外的工作。

然後來看下執行計畫的情況,可以看到語句明顯使用到了索引,對於後期的資料分析和處理還是大有幫助的。

mysql>  explain select user_name,(data->"$.location")from json_test where user_name = '"jeanron"';

| id | select_type | table     | partitions | type | possible_keys | key          | key_len | ref   | rows | filtered | extra |

|  1 | ******      | json_test | null       | ref  | idx_username  | idx_username | 387     | const |    1 |   100.00 | null  |

1 row in set, 1 warning (0.00 sec)

在這個基礎上如果做更多的分析,其實explain format=json也是一種改進方式,對於執行計畫,我們可以得到屬性值,通過解析的方式能夠把執行計畫做得更好。

json的新特性對於mysql來說確實是乙個不錯的特性,如果資料量巨大,還是需要考慮通過空間換時間的思路來改進。如果大家了解oracle,postgresql等資料庫,其實這些特性也是有的,oracle 12c裡面明確有這個特性,postgresql也有這個特性,還區分為json和jsonb,對於nosql來說,那更是它們擅長的,所以mysql實現這個是一種輔助,絕對不是做了顛覆性的改進。

歷史的車輪要前進,我們的方案只能是折中之後的方案。就好比早年的時候oracle全面支援xml,結果到現在這類需求明顯有些涼了。

json也不是萬金油,推薦大家參考這一篇。

獲取Json裡面的值

例如 我想獲取裡面code的值,也就是0000。兩種情況 1.知道json的名字 sr sr com.alibaba.fastjson.jsonobject jsonmap com.alibaba.fastjson.jsonobject.parseobject sr.tostring string ...

JSON學習四 JSON裡面的類

json的定義 一種輕量級的資料交換格式,具有良好的可讀和便於快速編寫的特性。業內主流技術為其提供了完整的解決方案 有點類似於正規表示式 獲得了當今大部分語言的支援 從而可以在不同平台間進行資料交換。json採用相容性很高的文字格式,同時也具備類似於c語言體系的行為。json.org 為什麼用jso...

JSON學習四 JSON裡面的類

json的定義 一種輕量級的資料交換格式,具有良好的可讀和便於快速編寫的特性。業內主流技術為其提供了完整的解決方案 有點類似於正規表示式 獲得了當今大部分語言的支援 從而可以在不同平台間進行資料交換。json採用相容性很高的文字格式,同時也具備類似於c語言體系的行為。json.org 為什麼用jso...