MySQL 支援由 RFC 7159 定義的原生 JSON
資料類型,可有效存取 JSON (JavaScript Object Notation) 文件中的資料。JSON
資料類型相較於在字串欄位中儲存 JSON 格式的字串,具有以下優勢
自動驗證儲存在
JSON
欄位中的 JSON 文件。無效的文件會產生錯誤。最佳化的儲存格式。儲存在
JSON
欄位中的 JSON 文件會轉換為內部格式,以便快速讀取文件元素。當伺服器稍後必須讀取以此二進位格式儲存的 JSON 值時,不需要從文字表示法剖析值。二進位格式的結構讓伺服器可以直接按鍵或陣列索引查找子物件或巢狀值,而無需讀取文件中它們之前或之後的所有值。
MySQL 8.4 也支援 JSON 合併修補 格式,此格式定義於 RFC 7396 中,使用 JSON_MERGE_PATCH()
函式。請參閱此函式的說明,以及 JSON 值的正規化、合併和自動包裝,以取得範例和其他資訊。
此討論使用等寬字體的 JSON
來明確表示 JSON 資料類型,並使用一般字體的「JSON」來表示一般的 JSON 資料。
儲存 JSON
文件所需的空間大致與 LONGBLOB
或 LONGTEXT
相同;如需更多資訊,請參閱 第 13.7 節,「資料類型儲存需求」。請務必記住,儲存在 JSON
欄位中的任何 JSON 文件大小都受限於 max_allowed_packet
系統變數的值。(當伺服器在記憶體中內部操作 JSON 值時,它可以大於此值;限制適用於伺服器儲存它時。) 您可以使用 JSON_STORAGE_SIZE()
函式取得儲存 JSON 文件所需的空間量;請注意,對於 JSON
欄位,儲存大小 (因此此函式傳回的值) 是欄位在對其執行任何部分更新之前所使用的大小 (請參閱本節稍後有關 JSON 部分更新最佳化的討論)。
除了 JSON
資料類型之外,還有一組 SQL 函式可用於對 JSON 值執行操作,例如建立、操作和搜尋。以下討論會顯示這些操作的範例。如需個別函式的詳細資訊,請參閱 第 14.17 節,「JSON 函式」。
也有一組用於操作 GeoJSON 值的空間函式。請參閱 第 14.16.11 節,「空間 GeoJSON 函式」。
JSON
欄位 (與其他二進位類型的欄位類似) 並未直接建立索引;相反地,您可以在產生的欄位上建立索引,該欄位會從 JSON
欄位中擷取純量值。如需詳細範例,請參閱 建立產生欄位的索引以提供 JSON 欄位索引。
MySQL 最佳化工具也會在符合 JSON 運算式的虛擬欄位上尋找相容的索引。
InnoDB
儲存引擎支援 JSON 陣列上的多值索引。請參閱 多值索引。
MySQL NDB Cluster 支援 JSON
欄位和 MySQL JSON 函式,包括在從 JSON
欄位產生的欄位上建立索引,作為無法為 JSON
欄位建立索引的替代方法。每個 NDB
表格最多支援 3 個 JSON
欄位。
JSON 值的部分更新
在 MySQL 8.4 中,最佳化工具可以對 JSON
欄位執行部分、就地更新,而不需要移除舊的文件,並將新的文件完整寫入欄位中。對於符合以下條件的更新,可以執行此最佳化
正在更新的欄位已宣告為
JSON
。UPDATE
敘述使用JSON_SET()
、JSON_REPLACE()
或JSON_REMOVE()
這三個函式中的任何一個來更新欄位。無法將欄位值的直接指派 (例如,UPDATE mytable SET jcol = '{"a": 10, "b": 25}'
) 作為部分更新執行。單一
UPDATE
敘述中多個JSON
欄位的更新可以此方式最佳化;MySQL 只能對使用剛才列出的三個函式更新值的欄位執行部分更新。輸入欄位和目標欄位必須是相同的欄位;無法將
UPDATE mytable SET jcol1 = JSON_SET(jcol2, '$.a', 100)
等敘述作為部分更新執行。更新可以使用巢狀呼叫前一個項目中列出的任何函式,以任何組合方式,只要輸入和目標欄位相同即可。
所有變更都會以新的值取代現有的陣列或物件值,且不會將任何新元素新增至父物件或陣列。
要取代的值必須至少與取代值一樣大。換句話說,新的值不能大於舊的值。
當先前的部分更新已留下足夠空間給較大的值時,可能會發生此要求的例外情況。您可以使用函式
JSON_STORAGE_FREE()
來查看JSON
資料行的任何部分更新釋放了多少空間。
此類部分更新可以使用節省空間的精簡格式寫入二進位日誌;您可以將 binlog_row_value_options
系統變數設定為 PARTIAL_JSON
來啟用此功能。
務必區分儲存在表格中的 JSON
資料行值的局部更新與將資料列的局部更新寫入二進位日誌。 JSON
資料行的完整更新可能會以部分更新的形式記錄在二進位日誌中。當不滿足先前列表中最後兩個條件中的任何一個(或兩者)但滿足其他條件時,可能會發生這種情況。
另請參閱 binlog_row_value_options
的說明。
接下來的幾個章節提供有關 JSON 值的建立和操作的基本資訊。
JSON 陣列包含一個由逗號分隔並以 [
和 ]
字元括起來的值清單
["abc", 10, null, true, false]
JSON 物件包含一組以逗號分隔並以 {
和 }
字元括起來的鍵值對
{"k1": "value", "k2": 10}
如範例所示,JSON 陣列和物件可以包含字串或數字的純量值、JSON null 常值,或 JSON 布林值 true 或 false 常值。JSON 物件中的鍵必須是字串。也允許使用時間(日期、時間或日期時間)純量值
["12:18:29.000000", "2015-07-29", "2015-07-29 12:18:29.000000"]
JSON 陣列元素和 JSON 物件鍵值中允許巢狀結構
[99, {"id": "HK500", "cost": 75.99}, ["hot", "cold"]]
{"k1": "value", "k2": [10, 20]}
您也可以從 MySQL 提供的許多函式中取得 JSON 值(請參閱章節 14.17.2,「建立 JSON 值的函式」),以及使用 CAST(
將其他類型的值轉換為 value
AS JSON)JSON
類型(請參閱 在 JSON 和非 JSON 值之間轉換)。接下來的幾個段落描述 MySQL 如何處理作為輸入提供的 JSON 值。
在 MySQL 中,JSON 值以字串形式寫入。MySQL 會解析任何在需要 JSON 值的內容中使用的字串,如果它不是有效的 JSON,則會產生錯誤。這些內容包括將值插入到具有 JSON
資料類型的資料行,以及將引數傳遞給需要 JSON 值的函式(通常在 MySQL JSON 函式的文件中顯示為 json_doc
或 json_val
),如下列範例所示
如果值是有效的 JSON 值,則嘗試將值插入到
JSON
資料行會成功,但如果不是,則會失敗mysql> CREATE TABLE t1 (jdoc JSON); Query OK, 0 rows affected (0.20 sec) mysql> INSERT INTO t1 VALUES('{"key1": "value1", "key2": "value2"}'); Query OK, 1 row affected (0.01 sec) mysql> INSERT INTO t1 VALUES('[1, 2,'); ERROR 3140 (22032) at line 2: Invalid JSON text: "Invalid value." at position 6 in value (or column) '[1, 2,'.
在這些錯誤訊息中,「在位置
N
」的位置是從 0 開始的,但應被視為值中實際發生問題的大概指示。JSON_TYPE()
函式預期會有 JSON 引數,並嘗試將其解析為 JSON 值。如果它有效,則傳回值的 JSON 類型,否則會產生錯誤mysql> SELECT JSON_TYPE('["a", "b", 1]'); +----------------------------+ | JSON_TYPE('["a", "b", 1]') | +----------------------------+ | ARRAY | +----------------------------+ mysql> SELECT JSON_TYPE('"hello"'); +----------------------+ | JSON_TYPE('"hello"') | +----------------------+ | STRING | +----------------------+ mysql> SELECT JSON_TYPE('hello'); ERROR 3146 (22032): Invalid data type for JSON data in argument 1 to function json_type; a JSON string or JSON type is required.
MySQL 使用 utf8mb4
字元集和 utf8mb4_bin
定序來處理 JSON 環境中使用的字串。其他字元集中的字串會根據需要轉換為 utf8mb4
。(對於 ascii
或 utf8mb3
字元集中的字串,則不需要轉換,因為 ascii
和 utf8mb3
是 utf8mb4
的子集。)
除了使用字串常值寫入 JSON 值之外,還存在一些函式可從元件元素組合 JSON 值。JSON_ARRAY()
接受(可能為空的)值清單,並傳回包含這些值的 JSON 陣列
mysql> SELECT JSON_ARRAY('a', 1, NOW());
+----------------------------------------+
| JSON_ARRAY('a', 1, NOW()) |
+----------------------------------------+
| ["a", 1, "2015-07-27 09:43:47.000000"] |
+----------------------------------------+
JSON_OBJECT()
接受(可能為空的)鍵值對清單,並傳回包含這些鍵值對的 JSON 物件
mysql> SELECT JSON_OBJECT('key1', 1, 'key2', 'abc');
+---------------------------------------+
| JSON_OBJECT('key1', 1, 'key2', 'abc') |
+---------------------------------------+
| {"key1": 1, "key2": "abc"} |
+---------------------------------------+
JSON_MERGE_PRESERVE()
接受兩個或多個 JSON 文件,並傳回合併的結果
mysql> SELECT JSON_MERGE_PRESERVE('["a", 1]', '{"key": "value"}');
+-----------------------------------------------------+
| JSON_MERGE_PRESERVE('["a", 1]', '{"key": "value"}') |
+-----------------------------------------------------+
| ["a", 1, {"key": "value"}] |
+-----------------------------------------------------+
1 row in set (0.00 sec)
有關合併規則的資訊,請參閱 JSON 值的正規化、合併和自動包裝。
(MySQL 也支援 JSON_MERGE_PATCH()
,其行為稍有不同。請參閱 JSON_MERGE_PATCH() 與 JSON_MERGE_PRESERVE() 的比較,以取得有關這兩個函式之間差異的資訊。)
JSON 值可以指派給使用者定義的變數
mysql> SET @j = JSON_OBJECT('key', 'value');
mysql> SELECT @j;
+------------------+
| @j |
+------------------+
| {"key": "value"} |
+------------------+
但是,使用者定義的變數不能是 JSON
資料類型,因此,雖然前面範例中的 @j
看起來像 JSON 值,並且具有與 JSON 值相同的字元集和定序,但它不具有 JSON
資料類型。相反,當指派給變數時,來自 JSON_OBJECT()
的結果會轉換為字串。
透過轉換 JSON 值產生的字串具有 utf8mb4
的字元集和 utf8mb4_bin
的定序
mysql> SELECT CHARSET(@j), COLLATION(@j);
+-------------+---------------+
| CHARSET(@j) | COLLATION(@j) |
+-------------+---------------+
| utf8mb4 | utf8mb4_bin |
+-------------+---------------+
由於 utf8mb4_bin
是二進制定序,因此 JSON 值的比較會區分大小寫。
mysql> SELECT JSON_ARRAY('x') = JSON_ARRAY('X');
+-----------------------------------+
| JSON_ARRAY('x') = JSON_ARRAY('X') |
+-----------------------------------+
| 0 |
+-----------------------------------+
大小寫敏感性也適用於 JSON null
、true
和 false
常值,這些常值必須始終以小寫形式寫入
mysql> SELECT JSON_VALID('null'), JSON_VALID('Null'), JSON_VALID('NULL');
+--------------------+--------------------+--------------------+
| JSON_VALID('null') | JSON_VALID('Null') | JSON_VALID('NULL') |
+--------------------+--------------------+--------------------+
| 1 | 0 | 0 |
+--------------------+--------------------+--------------------+
mysql> SELECT CAST('null' AS JSON);
+----------------------+
| CAST('null' AS JSON) |
+----------------------+
| null |
+----------------------+
1 row in set (0.00 sec)
mysql> SELECT CAST('NULL' AS JSON);
ERROR 3141 (22032): Invalid JSON text in argument 1 to function cast_as_json:
"Invalid value." at position 0 in 'NULL'.
JSON 常值的大小寫敏感性與 SQL NULL
、TRUE
和 FALSE
常值的大小寫敏感性不同,SQL 常值可以用任何大小寫形式寫入
mysql> SELECT ISNULL(null), ISNULL(Null), ISNULL(NULL);
+--------------+--------------+--------------+
| ISNULL(null) | ISNULL(Null) | ISNULL(NULL) |
+--------------+--------------+--------------+
| 1 | 1 | 1 |
+--------------+--------------+--------------+
有時可能需要或希望在 JSON 文件中插入引號字元("
或 '
)。在此範例中,假設您要將一些 JSON 物件插入到使用此處顯示的 SQL 陳述式建立的表格中,這些 JSON 物件包含表示有關 MySQL 的一些事實的句子,每個句子都與適當的關鍵字配對
mysql> CREATE TABLE facts (sentence JSON);
在這些關鍵字-句子對中,有這個
mascot: The MySQL mascot is a dolphin named "Sakila".
將其作為 JSON 物件插入到 facts
表格中的一種方法是使用 MySQL JSON_OBJECT()
函式。在這種情況下,您必須使用反斜線來逸出每個引號字元,如下所示
mysql> INSERT INTO facts VALUES
> (JSON_OBJECT("mascot", "Our mascot is a dolphin named \"Sakila\"."));
如果您將值作為 JSON 物件常值插入,則無法以相同的方式運作,在這種情況下,您必須使用雙反斜線逸出序列,如下所示
mysql> INSERT INTO facts VALUES
> ('{"mascot": "Our mascot is a dolphin named \\"Sakila\\"."}');
使用雙反斜線可防止 MySQL 執行逸出序列處理,而是使其將字串常值傳遞給儲存引擎進行處理。以剛才顯示的任一方式插入 JSON 物件後,您可以執行簡單的 SELECT
來查看反斜線是否出現在 JSON 資料行值中,如下所示
mysql> SELECT sentence FROM facts;
+---------------------------------------------------------+
| sentence |
+---------------------------------------------------------+
| {"mascot": "Our mascot is a dolphin named \"Sakila\"."} |
+---------------------------------------------------------+
若要使用 mascot
作為索引鍵來查閱此特定句子,您可以使用資料行路徑運算子 ->
,如下所示
mysql> SELECT col->"$.mascot" FROM qtest;
+---------------------------------------------+
| col->"$.mascot" |
+---------------------------------------------+
| "Our mascot is a dolphin named \"Sakila\"." |
+---------------------------------------------+
1 row in set (0.00 sec)
這會保留反斜線,以及周圍的引號。若要使用 mascot
作為索引鍵顯示所需的值,但不包含周圍的引號或任何逸出符號,請使用內嵌路徑運算子 ->>
,如下所示
mysql> SELECT sentence->>"$.mascot" FROM facts;
+-----------------------------------------+
| sentence->>"$.mascot" |
+-----------------------------------------+
| Our mascot is a dolphin named "Sakila". |
+-----------------------------------------+
如果啟用 NO_BACKSLASH_ESCAPES
伺服器 SQL 模式,則先前的範例無法如所示般運作。如果設定此模式,則可以使用單個反斜線而不是雙反斜線來插入 JSON 物件常值,並且會保留反斜線。如果您在執行插入時使用 JSON_OBJECT()
函式,並且已設定此模式,則必須交替使用單引號和雙引號,如下所示
mysql> INSERT INTO facts VALUES
> (JSON_OBJECT('mascot', 'Our mascot is a dolphin named "Sakila".'));
如需有關此模式對 JSON 值中逸出字元的影響的詳細資訊,請參閱 JSON_UNQUOTE()
函式的說明。
當解析字串並發現它是有效的 JSON 文件時,也會將其正規化。這表示會捨棄具有重複索引鍵的成員,該索引鍵在文件中稍後出現,從左到右讀取。下列 JSON_OBJECT()
呼叫產生的物件值僅包含第二個 key1
元素,因為該索引鍵名稱較早出現在值中,如下所示
mysql> SELECT JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def');
+------------------------------------------------------+
| JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def') |
+------------------------------------------------------+
| {"key1": "def", "key2": "abc"} |
+------------------------------------------------------+
當值插入到 JSON 資料行時,也會執行正規化,如下所示
mysql> CREATE TABLE t1 (c1 JSON);
mysql> INSERT INTO t1 VALUES
> ('{"x": 17, "x": "red"}'),
> ('{"x": 17, "x": "red", "x": [3, 5, 7]}');
mysql> SELECT c1 FROM t1;
+------------------+
| c1 |
+------------------+
| {"x": "red"} |
| {"x": [3, 5, 7]} |
+------------------+
此「最後重複的索引鍵獲勝」行為由 RFC 7159 建議,並且由大多數 JavaScript 解析器實作。(Bug #86866, Bug #26369555)
MySQL 會捨棄原始 JSON 文件中索引鍵、值或元素之間的多餘空白,並在顯示時在每個逗號 (,
) 或冒號 (:
) 後面保留(或在必要時插入)一個空格。這樣做是為了提高可讀性。
產生 JSON 值的 MySQL 函式(請參閱章節 14.17.2,「建立 JSON 值的函式」)始終傳回正規化的值。
為了提高查詢效率,MySQL 也會對 JSON 物件的索引鍵進行排序。您應該知道此排序的結果可能會變更,並且不保證在各個版本之間保持一致。
合併 JSON 值
支援兩種合併演算法,分別由函式 JSON_MERGE_PRESERVE()
和 JSON_MERGE_PATCH()
實作。它們在處理重複索引鍵的方式上有所不同:JSON_MERGE_PRESERVE()
保留重複索引鍵的值,而 JSON_MERGE_PATCH()
則會捨棄除最後一個值之外的所有值。接下來的幾個段落說明這兩個函式中的每一個如何處理不同 JSON 文件組合(即物件和陣列)的合併。
合併陣列。在合併多個陣列的環境中,陣列會合併為單一陣列。JSON_MERGE_PRESERVE()
的做法是將稍後命名的陣列串連到第一個陣列的末尾。JSON_MERGE_PATCH()
會將每個引數視為由單個元素組成的陣列(因此其索引為 0),然後套用「最後重複的索引鍵獲勝」邏輯來僅選擇最後一個引數。您可以比較此查詢顯示的結果
mysql> SELECT
-> JSON_MERGE_PRESERVE('[1, 2]', '["a", "b", "c"]', '[true, false]') AS Preserve,
-> JSON_MERGE_PATCH('[1, 2]', '["a", "b", "c"]', '[true, false]') AS Patch\G
*************************** 1. row ***************************
Preserve: [1, 2, "a", "b", "c", true, false]
Patch: [true, false]
多個物件合併時會產生單一物件。JSON_MERGE_PRESERVE()
處理具有相同鍵的多個物件時,會將該鍵的所有唯一值組合在一個陣列中;然後,此陣列會作為結果中該鍵的值。JSON_MERGE_PATCH()
會捨棄找到重複鍵的值,從左到右處理,因此結果只包含該鍵的最後一個值。以下查詢說明重複鍵 a
的結果差異。
mysql> SELECT
-> JSON_MERGE_PRESERVE('{"a": 1, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}') AS Preserve,
-> JSON_MERGE_PATCH('{"a": 3, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}') AS Patch\G
*************************** 1. row ***************************
Preserve: {"a": [1, 4], "b": 2, "c": [3, 5], "d": 3}
Patch: {"a": 4, "b": 2, "c": 5, "d": 3}
在需要陣列值的環境中使用非陣列值時,會自動包裝:該值會被 [
和 ]
字元包圍,以轉換為陣列。在以下陳述式中,每個引數都會自動包裝為陣列 ([1]
、[2]
)。然後,這些會合併以產生單一結果陣列;與前兩個案例一樣,JSON_MERGE_PRESERVE()
會合併具有相同鍵的值,而 JSON_MERGE_PATCH()
會捨棄除了最後一個以外的所有重複鍵的值,如下所示
mysql> SELECT
-> JSON_MERGE_PRESERVE('1', '2') AS Preserve,
-> JSON_MERGE_PATCH('1', '2') AS Patch\G
*************************** 1. row ***************************
Preserve: [1, 2]
Patch: 2
陣列和物件值會透過將物件自動包裝為陣列,並根據選擇的合併函數(分別為 JSON_MERGE_PRESERVE()
或 JSON_MERGE_PATCH()
)組合值或以「最後一個重複鍵勝出」的方式合併陣列,如下例所示
mysql> SELECT
-> JSON_MERGE_PRESERVE('[10, 20]', '{"a": "x", "b": "y"}') AS Preserve,
-> JSON_MERGE_PATCH('[10, 20]', '{"a": "x", "b": "y"}') AS Patch\G
*************************** 1. row ***************************
Preserve: [10, 20, {"a": "x", "b": "y"}]
Patch: {"a": "x", "b": "y"}
JSON 路徑表達式會選取 JSON 文件中的值。
路徑表達式對於提取或修改 JSON 文件部分內容的函數非常有用,可指定在文件中要操作的位置。例如,以下查詢從 JSON 文件中提取具有 name
鍵的成員的值
mysql> SELECT JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name');
+---------------------------------------------------------+
| JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name') |
+---------------------------------------------------------+
| "Aztalan" |
+---------------------------------------------------------+
路徑語法使用開頭的 $
字元來表示正在考慮的 JSON 文件,後面可選擇性地跟著指示文件中更特定部分的選取器
句點後跟一個鍵名,會命名物件中具有指定鍵的成員。如果未加引號的名稱在路徑表達式中不合法(例如,如果包含空格),則必須將鍵名指定在雙引號內。
附加到選取陣列的
path
的[
會命名陣列中位置N
]N
的值。陣列位置是從零開始的整數。如果path
未選取陣列值,則path
[0] 的評估結果與path
的值相同mysql> SELECT JSON_SET('"x"', '$[0]', 'a'); +------------------------------+ | JSON_SET('"x"', '$[0]', 'a') | +------------------------------+ | "a" | +------------------------------+ 1 row in set (0.00 sec)
[
指定陣列值的子集或範圍,從位置M
toN
]M
的值開始,到位置N
的值結束。last
支援作為最右邊陣列元素的索引的同義詞。也支援陣列元素的相對定址。如果path
未選取陣列值,則path
[last] 的評估結果與path
的值相同,如本節稍後所示(請參閱 最右邊的陣列元素)。路徑可以包含
*
或**
通配符.[*]
的評估結果為 JSON 物件中所有成員的值。[*]
的評估結果為 JSON 陣列中所有元素的值。
的評估結果為所有以命名前綴開始並以命名後綴結束的路徑。prefix
**suffix
文件中不存在的路徑(評估為不存在的資料)的評估結果為
NULL
。
假設 $
指的是這個包含三個元素的 JSON 陣列
[3, {"a": [5, 6], "b": 10}, [99, 100]]
那麼
$[0]
的評估結果為3
。$[1]
的評估結果為{"a": [5, 6], "b": 10}
。$[2]
的評估結果為[99, 100]
。$[3]
的評估結果為NULL
(它指的是不存在的第四個陣列元素)。
因為 $[1]
和 $[2]
的評估結果為非純量值,所以它們可以用作選取巢狀值的更特定路徑表達式的基礎。範例
$[1].a
的評估結果為[5, 6]
。$[1].a[1]
的評估結果為6
。$[1].b
的評估結果為10
。$[2][0]
的評估結果為99
。
如先前所述,如果未加引號的鍵名在路徑表達式中不合法,則命名鍵的路徑組件必須加上引號。假設 $
指的是這個值
{"a fish": "shark", "a bird": "sparrow"}
鍵都包含一個空格,因此必須加上引號
$."a fish"
的評估結果為shark
。$."a bird"
的評估結果為sparrow
。
使用通配符的路徑的評估結果為可能包含多個值的陣列
mysql> SELECT JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.*');
+---------------------------------------------------------+
| JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.*') |
+---------------------------------------------------------+
| [1, 2, [3, 4, 5]] |
+---------------------------------------------------------+
mysql> SELECT JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.c[*]');
+------------------------------------------------------------+
| JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.c[*]') |
+------------------------------------------------------------+
| [3, 4, 5] |
+------------------------------------------------------------+
在以下範例中,路徑 $**.b
的評估結果為多個路徑($.a.b
和 $.c.b
),並產生一個符合路徑值的陣列
mysql> SELECT JSON_EXTRACT('{"a": {"b": 1}, "c": {"b": 2}}', '$**.b');
+---------------------------------------------------------+
| JSON_EXTRACT('{"a": {"b": 1}, "c": {"b": 2}}', '$**.b') |
+---------------------------------------------------------+
| [1, 2] |
+---------------------------------------------------------+
JSON 陣列的範圍。您可以使用 to
關鍵字指定 JSON 陣列的子集。例如,$[1 to 3]
包括陣列的第二、第三和第四個元素,如下所示
mysql> SELECT JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[1 to 3]');
+----------------------------------------------+
| JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[1 to 3]') |
+----------------------------------------------+
| [2, 3, 4] |
+----------------------------------------------+
1 row in set (0.00 sec)
語法為
,其中 M
to N
M
和 N
分別是 JSON 陣列元素範圍的第一個和最後一個索引。N
必須大於 M
;M
必須大於或等於 0。陣列元素的索引從 0 開始。
您可以在支援通配符的環境中使用範圍。
最右邊的陣列元素。last
關鍵字支援作為陣列中最後一個元素的索引的同義詞。表單 last -
的表達式可用於相對定址,以及範圍定義內,如下所示N
mysql> SELECT JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[last-3 to last-1]');
+--------------------------------------------------------+
| JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[last-3 to last-1]') |
+--------------------------------------------------------+
| [2, 3, 4] |
+--------------------------------------------------------+
1 row in set (0.01 sec)
如果路徑評估的值不是陣列,則評估結果與將該值包裝在單一元素陣列中相同
mysql> SELECT JSON_REPLACE('"Sakila"', '$[last]', 10);
+-----------------------------------------+
| JSON_REPLACE('"Sakila"', '$[last]', 10) |
+-----------------------------------------+
| 10 |
+-----------------------------------------+
1 row in set (0.00 sec)
您可以使用
以及 JSON 資料行識別碼和 JSON 路徑表達式,作為 column
->path
JSON_EXTRACT(
的同義詞。如需詳細資訊,請參閱 第 14.17.3 節「搜尋 JSON 值的函數」。另請參閱 為 JSON 資料行索引產生資料行。column
, path
)
某些函數會採用現有的 JSON 文件,以某種方式修改它,並傳回修改後的文件。路徑表達式會指示在文件中進行變更的位置。例如,JSON_SET()
、JSON_INSERT()
和 JSON_REPLACE()
函數各自採用 JSON 文件,以及一或多個路徑值對,這些路徑值對描述要修改文件的位置和要使用的值。這些函數處理文件中現有值和不存在值的方式不同。
請考慮此文件
mysql> SET @j = '["a", {"b": [true, false]}, [10, 20]]';
JSON_SET()
會取代存在路徑的值,並新增不存在路徑的值:。
mysql> SELECT JSON_SET(@j, '$[1].b[0]', 1, '$[2][2]', 2);
+--------------------------------------------+
| JSON_SET(@j, '$[1].b[0]', 1, '$[2][2]', 2) |
+--------------------------------------------+
| ["a", {"b": [1, false]}, [10, 20, 2]] |
+--------------------------------------------+
在此情況下,路徑 $[1].b[0]
會選取現有值 (true
),該值會被路徑引數後面的值 (1
) 取代。路徑 $[2][2]
不存在,因此會將對應的值 (2
) 新增至 $[2]
選取的值。
JSON_INSERT()
會新增新值,但不會取代現有值
mysql> SELECT JSON_INSERT(@j, '$[1].b[0]', 1, '$[2][2]', 2);
+-----------------------------------------------+
| JSON_INSERT(@j, '$[1].b[0]', 1, '$[2][2]', 2) |
+-----------------------------------------------+
| ["a", {"b": [true, false]}, [10, 20, 2]] |
+-----------------------------------------------+
JSON_REPLACE()
會取代現有值,並忽略新值
mysql> SELECT JSON_REPLACE(@j, '$[1].b[0]', 1, '$[2][2]', 2);
+------------------------------------------------+
| JSON_REPLACE(@j, '$[1].b[0]', 1, '$[2][2]', 2) |
+------------------------------------------------+
| ["a", {"b": [1, false]}, [10, 20]] |
+------------------------------------------------+
路徑值對會從左到右評估。評估一對產生的文件會成為評估下一對的新值。
JSON_REMOVE()
採用 JSON 文件和一或多個指定要從文件中移除的值的路徑。傳回值是原始文件,減去文件中存在路徑所選取的值
mysql> SELECT JSON_REMOVE(@j, '$[2]', '$[1].b[1]', '$[1].b[1]');
+---------------------------------------------------+
| JSON_REMOVE(@j, '$[2]', '$[1].b[1]', '$[1].b[1]') |
+---------------------------------------------------+
| ["a", {"b": [true]}] |
+---------------------------------------------------+
這些路徑具有以下效果
$[2]
符合[10, 20]
並移除它。$[1].b[1]
的第一個執行個體符合b
元素中的false
並移除它。$[1].b[1]
的第二個執行個體不符合任何內容:該元素已移除,路徑不再存在,且沒有任何效果。
MySQL 支援的許多 JSON 函數,以及本手冊其他章節描述的函數(請參閱第 14.17 節,「JSON 函數」),都需要路徑表示式才能識別 JSON 文件中的特定元素。路徑由路徑的範圍和一個或多個路徑段組成。對於 MySQL JSON 函數中使用的路徑,範圍始終是正在搜尋或以其他方式操作的文件,以開頭的 $
字元表示。路徑段以句點字元 (.
) 分隔。陣列中的儲存格以 [
表示,其中 N
]N
是一個非負整數。索引鍵的名稱必須是雙引號字串或有效的 ECMAScript 識別符號(請參閱 識別符號名稱和識別符號,在ECMAScript 語言規範中)。路徑表示式,如 JSON 文字,應使用 ascii
、utf8mb3
或 utf8mb4
字元集進行編碼。其他字元編碼會隱式強制轉換為 utf8mb4
。完整的語法如下所示
pathExpression:
scope[(pathLeg)*]
pathLeg:
member | arrayLocation | doubleAsterisk
member:
period ( keyName | asterisk )
arrayLocation:
leftBracket ( nonNegativeInteger | asterisk ) rightBracket
keyName:
ESIdentifier | doubleQuotedString
doubleAsterisk:
'**'
period:
'.'
asterisk:
'*'
leftBracket:
'['
rightBracket:
']'
如先前所述,在 MySQL 中,路徑的範圍始終是正在操作的文件,以 $
表示。您可以使用 '$'
作為 JSON 路徑表示式中文件的同義詞。
某些實作支援 JSON 路徑範圍的欄參考;MySQL 8.4 不支援這些。
萬用字元 *
和 **
符號的使用方式如下
.*
表示物件中所有成員的值。[*]
表示陣列中所有儲存格的值。[
表示以prefix
]**suffix
prefix
開頭且以suffix
結尾的所有路徑。prefix
是可選的,而suffix
是必需的;換句話說,路徑不能以**
結尾。此外,路徑不得包含序列
***
。
如需路徑語法範例,請參閱各種使用路徑作為引數的 JSON 函數的說明,例如 JSON_CONTAINS_PATH()
、JSON_SET()
和 JSON_REPLACE()
。如需包含使用 *
和 **
萬用字元的範例,請參閱 JSON_SEARCH()
函數的說明。
MySQL 也支援使用 to
關鍵字 (例如 $[2 to 10]
) 來表示 JSON 陣列子集的範圍表示法,以及使用 last
關鍵字作為陣列最右邊元素的同義詞。如需更多資訊和範例,請參閱搜尋和修改 JSON 值。
可以使用 =
、<
、<=
、>
、>=
、<>
、!=
和 <=>
運算子來比較 JSON 值。
以下比較運算子和函數尚不支援 JSON 值
針對剛才列出的比較運算子和函數的替代方案是將 JSON 值轉換為原生 MySQL 數值或字串資料類型,使其具有一致的非 JSON 純量類型。
JSON 值的比較發生在兩個層級。第一個比較層級是基於比較值的 JSON 類型。如果類型不同,比較結果完全由哪個類型具有較高的優先順序決定。如果兩個值具有相同的 JSON 類型,則會使用類型特定的規則發生第二個比較層級。
以下清單顯示 JSON 類型的優先順序,從最高優先順序到最低優先順序。(類型名稱是 JSON_TYPE()
函數傳回的名稱。)在同一行中顯示的類型具有相同的優先順序。任何具有清單中較早列出的 JSON 類型的值都比任何具有清單中較晚列出的 JSON 類型的值大。
BLOB
BIT
OPAQUE
DATETIME
TIME
DATE
BOOLEAN
ARRAY
OBJECT
STRING
INTEGER, DOUBLE
NULL
對於具有相同優先順序的 JSON 值,比較規則是類型特定的
BLOB
比較兩個值的前
N
個位元組,其中N
是較短值中的位元組數。如果兩個值的前N
個位元組相同,則較短的值會排在較長的值之前。BIT
與
BLOB
相同的規則。OPAQUE
與
BLOB
相同的規則。OPAQUE
值是不歸類為其他類型之一的值。DATETIME
表示較早時間點的值會排在表示較晚時間點的值之前。如果兩個值最初分別來自 MySQL
DATETIME
和TIMESTAMP
類型,則如果它們表示相同的時間點,則它們相等。TIME
兩個時間值中較小的值會排在較大的值之前。
DATE
較早的日期會排在較晚的日期之前。
ARRAY
如果兩個 JSON 陣列具有相同的長度,並且陣列中對應位置的值相等,則它們相等。
如果陣列不相等,則它們的順序由第一個存在差異的位置中的元素決定。該位置中具有較小值的陣列會先排序。如果較短陣列的所有值都等於較長陣列中對應的值,則較短陣列會先排序。
範例
[] < ["a"] < ["ab"] < ["ab", "cd", "ef"] < ["ab", "ef"]
BOOLEAN
JSON false 文字小於 JSON true 文字。
OBJECT
如果兩個 JSON 物件具有相同的索引鍵集,且每個索引鍵在兩個物件中都具有相同的值,則它們相等。
範例
{"a": 1, "b": 2} = {"b": 2, "a": 1}
兩個不相等的物件的順序是未指定的,但具有確定性。
STRING
字串會依據兩個比較字串的
utf8mb4
表示法的前N
個位元組按詞彙順序排序,其中N
是較短字串的長度。如果兩個字串的前N
個位元組相同,則較短的字串會被視為小於較長的字串。範例
"a" < "ab" < "b" < "bc"
此排序等效於具有定序
utf8mb4_bin
的 SQL 字串排序。因為utf8mb4_bin
是二進制定序,所以 JSON 值的比較會區分大小寫"A" < "a"
INTEGER
、DOUBLE
JSON 值可以包含精確值數字和近似值數字。如需這些類型數字的一般討論,請參閱第 11.1.2 節,「數值文字」。
比較原生 MySQL 數值類型的規則在第 14.3 節,「運算式評估中的類型轉換」中討論,但比較 JSON 值中數字的規則略有不同
在分別使用原生 MySQL
INT
和DOUBLE
數值類型的兩個欄之間的比較中,已知所有比較都涉及一個整數和一個雙精度浮點數,因此對於所有列,整數都會轉換為雙精度浮點數。也就是說,精確值數字會轉換為近似值數字。另一方面,如果查詢比較包含數字的兩個 JSON 欄,則無法預先知道數字是整數還是雙精度浮點數。為了在所有列中提供最一致的行為,MySQL 會將近似值數字轉換為精確值數字。產生的排序是一致的,並且不會遺失精確值數字的精確度。例如,給定純量 9223372036854775805、9223372036854775806、9223372036854775807 和 9.223372036854776e18,排序如下
9223372036854775805 < 9223372036854775806 < 9223372036854775807 < 9.223372036854776e18 = 9223372036854776000 < 9223372036854776001
如果 JSON 比較使用非 JSON 數值比較規則,則可能會發生不一致的排序。常用的 MySQL 數字比較規則會產生以下排序
整數比較
9223372036854775805 < 9223372036854775806 < 9223372036854775807
(未針對 9.223372036854776e18 定義)
雙精度浮點數比較
9223372036854775805 = 9223372036854775806 = 9223372036854775807 = 9.223372036854776e18
對於任何 JSON 值與 SQL NULL
的比較,結果為 UNKNOWN
。
對於 JSON 和非 JSON 值的比較,非 JSON 值會根據下表中的規則轉換為 JSON,然後如前所述比較這些值。
下表提供 MySQL 在 JSON 值和其他類型的值之間轉換時所遵循的規則摘要
表 13.3 JSON 轉換規則
其他類型 | CAST(其他類型 AS JSON) | CAST(JSON AS 其他類型) |
---|---|---|
JSON | 無變更 | 無變更 |
utf8 字元類型 (utf8mb4 、utf8mb3 、ascii ) |
字串會剖析為 JSON 值。 | JSON 值會序列化為 utf8mb4 字串。 |
其他字元類型 | 其他字元編碼會隱式轉換為 utf8mb4 ,並依此字元類型所述進行處理。 |
JSON 值會序列化為 utf8mb4 字串,然後轉換為其他字元編碼。結果可能沒有意義。 |
NULL |
會產生 JSON 類型的 NULL 值。 |
不適用。 |
幾何類型 | 幾何值會透過呼叫 ST_AsGeoJSON() 轉換為 JSON 文件。 |
非法運算。替代方案:將 CAST( 的結果傳遞至 ST_GeomFromGeoJSON() 。 |
所有其他類型 | 會產生由單一純量值組成的 JSON 文件。 | 如果 JSON 文件由目標類型的單一純量值組成,且該純量值可以轉換為目標類型,則會成功。否則,會傳回 NULL 並產生警告。 |
JSON 值的 ORDER BY
和 GROUP BY
根據這些原則運作
純量 JSON 值的排序使用與先前討論中相同的規則。
對於遞增排序,SQL
NULL
會排在所有 JSON 值(包括 JSON null 文字)之前;對於遞減排序,SQLNULL
會排在所有 JSON 值(包括 JSON null 文字)之後。JSON 值的排序索引鍵受限於
max_sort_length
系統變數的值,因此僅在前max_sort_length
個位元組之後不同的索引鍵比較時視為相等。目前不支援非純量值的排序,並且會發生警告。
對於排序,將 JSON 純量轉換為其他一些原生 MySQL 類型可能很有用。例如,如果名為 jdoc
的欄包含具有由 id
索引鍵和非負值組成的成員的 JSON 物件,請使用此運算式依 id
值排序
ORDER BY CAST(JSON_EXTRACT(jdoc, '$.id') AS UNSIGNED)
如果剛好有一個產生的欄位被定義為使用與 ORDER BY
中相同的表達式,MySQL 優化器會識別到這一點,並考慮使用索引來執行查詢計畫。請參閱第 10.3.11 節,「產生欄位索引的優化器使用」。
對於 JSON 值的聚合,SQL 的 NULL
值會像其他資料類型一樣被忽略。非 NULL
值會被轉換為數值型別並進行聚合,但 MIN()
、MAX()
和 GROUP_CONCAT()
除外。轉換為數值應該會為數值純量的 JSON 值產生有意義的結果,儘管(取決於這些值)可能會發生截斷和精度損失。將其他 JSON 值轉換為數值可能不會產生有意義的結果。