文件首頁
MySQL 9.0 參考手冊
相關文件 下載本手冊
PDF (美國信紙尺寸) - 40.0Mb
PDF (A4) - 40.1Mb
手冊頁 (TGZ) - 258.2Kb
手冊頁 (Zip) - 365.3Kb
資訊 (Gzip) - 4.0Mb
資訊 (Zip) - 4.0Mb


MySQL 9.0 參考手冊  /  資料類型  /  JSON 資料類型

13.5 JSON 資料類型

MySQL 支援由 RFC 7159 定義的原生 JSON 資料類型,可有效率地存取 JSON (JavaScript 物件表示法) 文件中的資料。JSON 資料類型相較於將 JSON 格式字串儲存在字串欄位中,具有以下優點:

  • 自動驗證儲存在 JSON 欄位中的 JSON 文件。無效的文件會產生錯誤。

  • 最佳化的儲存格式。儲存在 JSON 欄位中的 JSON 文件會轉換為內部格式,以便快速讀取文件元素。當伺服器稍後必須讀取以此二進位格式儲存的 JSON 值時,不需要從文字表示法剖析該值。二進位格式的結構設計可讓伺服器直接依據索引鍵或陣列索引查找子物件或巢狀值,而無需讀取文件中它們之前或之後的所有值。

MySQL 9.0 也支援 RFC 7396 中定義的 JSON 合併修補程式格式,使用 JSON_MERGE_PATCH() 函式。如需範例和其他資訊,請參閱此函式的說明,以及 JSON 值的正規化、合併和自動包裝

注意

本討論使用單間距字型的 JSON 來表示特定的 JSON 資料類型,並使用一般字體的 JSON 來表示一般的 JSON 資料。

儲存 JSON 文件所需的空間大致與 LONGBLOBLONGTEXT 相同;如需更多資訊,請參閱 第 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 叢集支援 JSON 欄位和 MySQL JSON 函式,包括在從 JSON 欄位產生的欄位上建立索引,以作為無法為 JSON 欄位建立索引的解決方法。每個 NDB 表格最多支援 3 個 JSON 欄位。

JSON 值的部分更新

在 MySQL 9.0 中,最佳化工具可以對 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 值

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_docjson_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。(對於 asciiutf8mb3 字元集中的字串,則不需要轉換,因為 asciiutf8mb3utf8mb4 的子集。)

除了使用文字字串寫入 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 nulltruefalse 文字,這些文字必須始終以小寫字母寫入

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 NULLTRUEFALSE 文字的大小寫敏感度不同,後者可以以任何字母大小寫寫入

mysql> SELECT ISNULL(null), ISNULL(Null), ISNULL(NULL);
+--------------+--------------+--------------+
| ISNULL(null) | ISNULL(Null) | ISNULL(NULL) |
+--------------+--------------+--------------+
|            1 |            1 |            1 |
+--------------+--------------+--------------+

有時可能需要或希望將引號字元 ("') 插入 JSON 文件中。在此範例中,假設您想要將一些包含字串的 JSON 物件插入到使用此處顯示的 SQL 陳述式所建立的表格中,這些字串表示一些關於 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_UNQUOTE() 函式的描述,以獲取有關此模式對 JSON 值中逸出字元影響的更多資訊。

JSON 值的正規化、合併和自動包裝

當字串被解析並發現是有效的 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 文件部分的函式中很有用,可以指定在該文件中要操作的位置。例如,以下查詢從 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)
  • [MN] 指定從位置 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,其中 MN 分別是 JSON 陣列中元素範圍的第一個和最後一個索引。N 必須大於 MM 必須大於或等於 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)

您可以將 column->path 與 JSON 欄位識別碼和 JSON 路徑表達式一起使用,作為 JSON_EXTRACT(column, path) 的同義詞。如需更多資訊,請參閱 第 14.17.3 節「搜尋 JSON 值的函式」。另請參閱 為 JSON 欄位索引提供產生的欄位索引

有些函式會接收現有的 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] 的第二個執行個體不符合任何項目:該元素已遭移除,路徑不再存在,因此沒有任何效果。

JSON 路徑語法

MySQL 支援的許多 JSON 函式,以及本手冊其他地方所描述的 (請參閱 第 14.17 節,「JSON 函式」),都需要路徑運算式才能識別 JSON 文件中的特定元素。路徑由路徑的範圍以及一或多個路徑段組成。對於在 MySQL JSON 函式中使用的路徑,範圍一律為正在搜尋或以其他方式操作的文件,以開頭的 $ 字元表示。路徑段以句點字元 (.) 分隔。陣列中的儲存格以 [N] 表示,其中 N 為非負整數。索引鍵的名稱必須是雙引號字串或有效的 ECMAScript 識別碼 (請參閱 識別碼名稱和識別碼,在ECMAScript 語言規範中)。路徑運算式,如同 JSON 文字,應使用 asciiutf8mb3utf8mb4 字元集編碼。其他字元編碼會隱式強制轉換為 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 9.0 不支援這些。

萬用字元 *** 權杖的使用方式如下:

  • .* 代表物件中所有成員的值。

  • [*] 代表陣列中所有儲存格的值。

  • [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 值:

針對剛才列出的比較運算子和函式的替代方案是,將 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 DATETIMETIMESTAMP 類型,則如果它們代表相同的時間點,則它們相等。

  • 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"
  • INTEGERDOUBLE

    JSON 值可以包含精確值數字和近似值數字。如需這些類型數字的一般討論,請參閱第 11.1.2 節,「數字文字」

    原生 MySQL 數值型別的比較規則在第 14.3 節,「表達式求值中的型別轉換」中討論,但 JSON 值中數值的比較規則有些不同。

    • 在比較兩個分別使用原生 MySQL INTDOUBLE 數值型別的欄位時,已知所有比較都涉及到一個整數和一個雙精度浮點數,因此所有列的整數都會被轉換為雙精度浮點數。也就是說,精確值數字會被轉換為近似值數字。

    • 另一方面,如果查詢比較兩個包含數字的 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,然後如先前所述比較這些值。

JSON 與非 JSON 值之間的轉換

下表總結了 MySQL 在 JSON 值和其他型別的值之間進行轉換時所遵循的規則

表 13.3:JSON 轉換規則

其他型別 CAST(其他型別 AS JSON) CAST(JSON AS 其他型別)
JSON 無變更 無變更
utf8 字元型別 (utf8mb4utf8mb3ascii) 字串會剖析成 JSON 值。 JSON 值會序列化成 utf8mb4 字串。
其他字元型別 其他字元編碼會隱式轉換為 utf8mb4,並依此字元型別的說明處理。 JSON 值會序列化成 utf8mb4 字串,然後轉換為其他字元編碼。結果可能沒有意義。
NULL 產生 JSON 型別的 NULL 值。 不適用。
幾何型別 幾何值會藉由呼叫 ST_AsGeoJSON() 轉換為 JSON 文件。 非法操作。替代方案:將 CAST(json_val AS CHAR) 的結果傳遞給 ST_GeomFromGeoJSON()
所有其他型別 產生包含單一純量值的 JSON 文件。 如果 JSON 文件包含目標型別的單一純量值,且該純量值可以轉換為目標型別,則成功。否則,會傳回 NULL 並產生警告。

JSON 值的 ORDER BYGROUP BY 會根據以下原則運作

  • 純量 JSON 值的排序使用與前述討論相同的規則。

  • 對於升冪排序,SQL NULL 的排序會在所有 JSON 值(包括 JSON null 字面值)之前;對於降冪排序,SQL NULL 的排序會在所有 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 值的彙總

對於 JSON 值的彙總,會如同其他資料型別一樣忽略 SQL NULL 值。非 NULL 值會轉換為數值型別並進行彙總,但 MIN()MAX()GROUP_CONCAT() 除外。將數值轉換應會為純量數值的 JSON 值產生有意義的結果,儘管(取決於值)可能會發生截斷和精確度損失。將其他 JSON 值轉換為數字可能不會產生有意義的結果。