Lindorm寬表引擎支持在建表、插入數據和更新數據時使用JSON數據類型。JSON(JavaScriptObject Notation)是一種可以在多種語言之間進行數據格式交換的數據類型。JSON數據的格式為鍵值對,結構清晰,語法易讀,同時也方便前后端的數據傳輸。
適用引擎
JSON數據類型僅適用于寬表引擎。
前提條件
使用限制
Lindorm寬表的主鍵列不支持JSON數據類型。
DDL
您可以在創建表或者修改表(新增列時)語句中指定相關列為JSON數據類型。
創建表語句示例如下,相關語法請參見CREATE TABLE。
CREATE TABLE tb (p1 INT, c1 VARCHAR, c2 JSON, PRIMARY KEY(p1));
修改表(新增c3列)語句示例如下,相關語法請參見ALTER TABLE。
ALTER TABLE tb ADD c3 JSON;
說明增加列時,數據表不會被鎖定,DML請求可以正常進行。
上述示例中,表tb的主鍵列為p1且數據類型為INT。c1為非主鍵列且數據類型為VARCHAR。c2為非主鍵列且數據類型為JSON。c3為非主鍵列且數據類型為JSON。通過以下語句查看tb的表結構。
DESCRIBE tb;
返回結果如下:
+--------------+---------------------+------------------------+---------+----------------+------------+
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | TYPE | IS_PRIMARY_KEY | SORT_ORDER |
+--------------+---------------------+------------------------+---------+----------------+------------+
| default | tb | p1 | INT | true | ASC |
| default | tb | c1 | VARCHAR | false | none |
| default | tb | c2 | JSON | false | none |
| default | tb | c3 | JSON | false | none |
+--------------+---------------------+------------------------+---------+----------------+------------+
DML
以下內容介紹對JSON數據類型的列進行數據寫入、讀取和刪除。
UPSERT
通過以下三種方式寫入JSON數據。如果在JSON列中寫入的數據不是JSON對象或者JSON字符串,寫入過程中會報錯。Lindorm寬表SQL提供的json_object
和json_array
函數將寫入的數據轉換為JSON對象。
直接寫入JSON字符串。列舉以下兩種寫入方式。
使用
Statement()
方式寫入SQL,把JSON格式的字符串寫入JSON列。Connection conn = DriverManager.getConnection("Lindorm URL", properties); Statement stmt = conn.createStatement(); String jsonStr1 = "{\"k1\":4,\"k2\":{\"k3\":{\"k4\":4}}}"; String upsertSQL = "UPSERT INTO tb(p1, c1, c2) VALUES(1, '1', '"+ jsonStr1 + "')"; //返回寫入數據條數 int ret = stmt.executeUpdate(upsertSQL);
使用
PrepareStatement()
方式寫入SQL,先進行SQL預處理再為該SQL模板的參數指定參數值。String jsonStr1 = "{\"k1\":4,\"k2\":{\"k3\":{\"k4\":4}}}"; //寫入的SQL模板 String upsertSQL = "UPSERT INTO tb(p1, c1, c2) VALUES(1, '1', ?)"; PreparedStatement preStmt = conn.prepareStatement(upsertSQL); //將JSON字符串寫入JSON列 preStmt.setString(1, jsonStr1); int ret = stmt.executeUpdate();
使用
json_object
函數將函數中的數據按照寫入順序轉換為key-value形式的JSON對象,再把JSON對象寫入JSON列。String upsert = "UPSERT INTO tb(p1,c1,c2) VALUES(2,'2',json_object('k1', 2, 'k2', '2'))";
如果使用Lindorm寬表SQL寫入上述數據時,請執行以下語句。
UPSERT INTO tb(p1,c1,c2) VALUES(2,'2','{"k1":2,"k2":"2"}');
使用
json_array
函數將函數中的數據按照寫入順序轉換為數組形式JSON對象,再把JSON對象寫入JSON列。String upsert = "UPSERT INTO " + tableName + "(p1,c1,c2) VALUES(3,'3', json_array(1, 2, json_object('k1', 3, 'k2', '3')))";
如果使用Lindorm寬表SQL寫入上述數據時,請執行以下語句。
UPSERT INTO tb(p1,c1,c2) VALUES(3,'3','[1,2,{"k1":3,"k2":"3"}]');
結果驗證
執行以下語句,可以驗證數據寫入結果。
SELECT * FROM tb;
SELECT
在查詢JSON列的數據時,需要使用json_extract
函數返回JSON列的值或者對JSON列的值進行條件過濾。
在Lindorm寬表SQL和MySQL中json_extract
函數的用法類似。查詢操作中json_extract
函數可以在SELECT子句中或者WHERE子句中使用。
當
json_extract
函數在SELECT子句中使用時,表示獲取JSON列上具體的值并返回給用戶。示例如下:寫入的JSON列數據為
"{\"k1\":1}"
String json = "{\"k1\":1}"; //SELECT子句中使用json_extract函數,表示返回c2列中k1鍵的值,并將返回結果的列名設置為j String select = "select p1, c1, c2, json_extract(c2, '$.k1') j from tb where p1 = 1"; ResultSet resultSet = stmt.executeQuery(select); resultSet.next(); String resultC2 = resultSet.getString(c2); //resultC2等于c2列 String resultC2k1 = resultSet.getString("j"); //resultC2k1等于c2列中k1鍵的值,結果為1
寫入的JSON列數據為
"{\"k1\":2,\"k2\":\"2\"}"
String json ="{\"k1\":2,\"k2\":\"2\"}"; //SELECT子句中使用json_extract函數,表示返回c2列中k2鍵的值,并將返回結果的列名設置為j String select = "select p1, c1, c2, json_extract(c2, '$.k2') j from tb where p1 = 2"; ResultSet resultSet = stmt.executeQuery(select); resultSet.next(); String resultC2 = resultSet.getString(c2); //resultC2等于c2列 String resultC2k1 = resultSet.getString("j"); //resultC2k1等于c2列中k2鍵的值,結果為2
寫入的JSON列數據為
"[1,2,{\"k1\":3,\"k2\":\"3\"}]"
String json ="[1,2,{\"k1\":3,\"k2\":\"3\"}]"; //SELECT子句中使用json_extract函數,表示返回c2列中JSON數組第2個index上的k2鍵的值,并將返回結果的列名設置j String "select json_extract(c2, '$[2].k2') j from tb where p1 = 3"; ResultSet resultSet = stmt.executeQuery(select); resultSet.next(); String resultC2 = resultSet.getString(c2); //resultC2等于c2列 String resultC2k1 = resultSet.getString("j"); //resultC2k1等于JSON數組上index為2的JSON對象以k2為鍵的值,結果為3
當
json_extract
函數在WHERE子句中使用時,表示獲取JSON列上具體的值并進行條件過濾。如果json_extract
函數在WHERE子句中涉及數據比較和篩選時,不同數據類型之間比較數據類型的等級,比較方式與MySQL的比較方式相同,具體請參見The JSON Data Type。寫入的JSON列數據為
"{\"k1\":2,\"k2\":\"2\"}"
String json = "{\"k1\":2,\"k2\":\"2\"}"; //WHERE子句中使用json_extract函數,表示返回c2列中k2鍵的值大于0的數據 String select = "select p1, c1, c2 from tb where where p1 >= 1 and p1 < 4 and json_extract(c2, '$.k2') > '0'"; ResultSet resultSet = stmt.executeQuery(select); resultSet.next(); String resultC2 = resultSet.getString(c2); //resultC2等于c2列
寫入的JSON列數據為
"{\"k1\":4,\"k2\":{\"k3\":{\"k4\":4}}}"
String json = "{\"k1\":4,\"k2\":{\"k3\":{\"k4\":4}}}"; //WHERE子句中使用json_extract函數,表示返回c2列的指定路徑'$.k2.k3.k4'的值大于4的數據 String select = "select * from tb where p1 >= 4 and p1 < 6 and json_extract(c2, '$.k2.k3.k4') > 4"; ResultSet resultSet = stmt.executeQuery(select); resultSet.next(); String resultC2 = resultSet.getString(c2); //resultC2等于c2列
寫入的JSON列數據為
"[1,2,{\"k1\":3,\"k2\":\"3\"}]"
String json = "[1,2,{\"k1\":3,\"k2\":\"3\"}]"; //WHERE子句中使用json_extract函數,表示返回c2列中JSON數組第2個index上的k2鍵的值大于0的數據。 String select = "select * from tb where p1 >= 1 and p1 < 4 and json_extract(c2, '$[2].k2') > '0'"; ResultSet resultSet = stmt.executeQuery(select); resultSet.next(); String resultC2 = resultSet.getString(c2); //resultC2等于c2列
UPDATE
執行UPDATE
的路徑必須是map格式。例如:c2列中k1路徑的值為2,則不允許更新k1路徑的值;c2列中k1路徑的值為{"k2":"value"}
,允許更新k1路徑的值。您可以通過UPSERT INTO
語句,以正確的格式覆蓋原來的值,再進行UPDATE
操作。
在更新JSON列中指定路徑的數據時,需要使用UPDATE
語法,目前JSON列支持以下操作:
JSON_SET
:更新JSON列中指定路徑的值或者新增不存在JSON列的路徑以及路徑上的值。例如:如果
c2
列中存在k1.k2
路徑,那么將k1.k2
路徑的值更新為value
。如果c2
列中不存在k1.k2
路徑,則新增k1.k2
路徑并將值設置為value
。新增k1.k2
路徑時需要確保存在k1
路徑。UPDATE tb SET c2 = JSON_SET(c2, '$.k1.k2', 'value') WHERE p1 = 2;
JSON_INERT
:插入不存在的JSON列路徑上的值。例如:如果
c2
列中不存在k1.k2
路徑,則在k1.k2
路徑上插入值nvalue
,您需要確保存在k1
路徑。UPDATE tb SET c2 = JSON_INSERT(c2 ,'$.k1.k2' ,'nvalue') WHERE p1 = 2;
JSON_REPLACE
:更新已存在的JSON列路徑上的值。例如:如果
c2
列中存在k1
路徑,那么將k1
路徑的值更新為nvalue
。UPDATE tb SET c2 = JSON_REPLACE(c2 ,'$.k1' ,'nvalue') WHERE p1 = 2;
JSON_REMOVE
:刪除已存在的JSON列路徑和路徑上的值。例如:如果
c2
列中存在k1
路徑,那么刪除k1
路徑和路徑的值。UPDATE tb SET c2 = JSON_REMOVE(c2 , '$.k1') WHERE p1 = 2;
結果驗證
執行以下語句,可以驗證數據修改結果。
SELECT * FROM tb;
構建二級索引
Lindorm寬表SQL支持為JSON數據類型列中指定路徑的數據構建二級索引,但是在構建二級索引時需要指定JSON列的json_extract
函數類型。
語法
create_index_statement ::= CREATE INDEX [ index_name ]
ON table_name '(' index_identifier ')'
[INCLUDE include_identifier]
[ASYNC]
[ index_options ]
index_identifier ::= '('json_extract_type(column, json_path)')'
include_identifier ::= '('column_name1,...,column_namen ')'
參數
參數 | 描述 |
index_name | 索引表名。 |
table_name | 寬表名。 |
json_extract_type | 通過json_extract_type從JSON列中提取對應的數據類型的字段作為二級索引的,如果數據類型不匹配則不構建二級索引。支持以下函數類型:
|
column | JSON列的列名。 |
json_path | JSON列的路徑,用于提取JSON列指定路徑的值。 |
ASYNC | 異步構建索引,不添加ASYNC表示同步構建索引。 |
示例
為c3列中k1.k2
路徑的數據(數據類型為LONG)構建二級索引,同時指定索引表的壓縮算法為ZSTD。如果數據類型不是LONG,則不進行構建操作。
CREATE INDEX idx1 ON tb(json_extract_long(c2, '$.k1.k2')) INCLUDE(c1,c3) ASYNC 'COMPRESSION'='ZSTD';
結果驗證
執行以下語句,查看索引創建結果。
SHOW INDEX FROM tb;