13.5 Тип данных JSON
MySQL поддерживает собственный тип данных (JavaScript Object Notation) определённый RFC 8259, который позволяет эффективно получать доступ к данным в документах JSON. Тип данных JSON предоставляет следующие преимущества перед хранением JSON-строк в столбце типа строка:
Автоматическая валидация документов JSON, хранящихся в столбцах типа JSON. Некорректные документы вызывают ошибку.
Оптимизированный формат хранения. Документы JSON, хранящиеся в столбцах типа JSON, преобразуются в внутренний формат, который позволяет быстро получить доступ к элементам документа. При последующем чтении значения JSON в этом двоичном формате, серверу не нужно его парсить из текстового представления. Двоичный формат структурирован таким образом, чтобы сервер мог искать подобъекты или вложенные значения напрямую по ключу или индексу массива без чтения всех значений, предшествующих или последующих им в документе.
MySQL также поддерживает формат JSON Merge Patch, определенный в RFC 7396, используя функцию JSON_MERGE_PATCH(). См. описание этой функции, а также Нормализация, слияние и автообёртка значений JSON для примеров и дополнительной информации.
В этом обсуждении используется шрифт моноширинный (monotype) для обозначения типа данных JSON, а «JSON» в обычном шрифте для обозначения данных JSON вообще.
Место, занимаемое документом JSON, примерно такое же, как для LONGBLOB или LONGTEXT; см. Раздел 13.7, «Требования к хранению типов данных» для получения дополнительной информации. Важно помнить, что размер любого документа JSON, хранящегося в столбце типа JSON, ограничен значением системной переменной max_allowed_packet. (Когда сервер обрабатывает значение JSON внутри памяти, оно может быть больше; ограничение применяется, когда сервер сохраняет его.) Размер места, занимаемого документом JSON, можно получить, используя функцию JSON_STORAGE_SIZE(); обратите внимание, что для столбца типа 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. Максимальное количество столбцов типа JSON на один столбец в таблице MySQL NDB Cluster составляет 3.
Частичные обновления значений JSON
В MySQL 9.2 оптимизатор может выполнить частичное, внедренное обновление столбца типа JSON вместо удаления старого документа и записи нового документа целиком в столбец. Эта оптимизация может быть выполнена для обновления, которое удовлетворяет следующим условиям:
Обновляемый столбец был объявлен как столбец типа JSON.
-
Оператор
UPDATEиспользует любые из трёх функцийJSON_SET(),JSON_REPLACE()илиJSON_REMOVE()для обновления столбца. Прямая присваивание значения столбцу (например,UPDATE mytable SET jcol = '{"a": 10, "b": 25}') не может быть выполнено как частичное обновление.Обновления нескольких столбцов типа JSON в одном операторе
UPDATEмогут быть оптимизированы таким же образом; 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.
END_OF_DOCUMENT_MARKERСоздание значений JSON
Массив JSON содержит список значений, разделенных запятыми и заключенных в символы [ и ]:
["abc", 10, null, true, false]
Объект JSON содержит набор пар ключ-значение, разделенных запятыми и заключенных в символы { и }:
{"k1": "value", "k2": 10}
Как показывают примеры, массивы и объекты JSON могут содержать скалярные значения, которые являются строками или числами, литерал JSON null или литералы JSON boolean 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]}
Вы также можете получить значения JSON с помощью ряда функций MySQL для этой цели (см. Раздел 14.17.2, «Функции для создания значений JSON»), а также путем приведения значений других типов к типу JSON с помощью CAST( (см. Преобразование между значениями JSON и не-JSON). Следующие несколько абзацев описывают, как MySQL обрабатывает значения JSON, предоставленные в качестве входных данных. value AS
JSON)
В MySQL значения JSON записываются в виде строк. MySQL анализирует любую строку, используемую в контексте, требующем значения JSON, и выдает ошибку, если она не является допустимым JSON. Эти контексты включают вставку значения в столбец, имеющий тип данных JSON, и передачу аргумента функции, ожидающей значение JSON (обычно показанной как json_doc или json_val в документации по функциям MySQL JSON), как демонстрируют следующие примеры:
-
Попытка вставки значения в столбец
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 обрабатывает строки, используемые в контексте JSON, используя наборы символов utf8mb4 и правила сопоставления utf8mb4_bin. Строки в других наборах символов преобразуются в 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, которые можно писать в любом регистре:
mysql> SELECT ISNULL(null), ISNULL(Null), ISNULL(NULL);
+--------------+--------------+--------------+
| ISNULL(null) | ISNULL(Null) | ISNULL(NULL) |
+--------------+--------------+--------------+
| 1 | 1 | 1 |
+--------------+--------------+--------------+
Иногда может потребоваться или быть желательным вставить символы кавычек (" или ') в JSON-документ. Предположим для этого примера, что вы хотите вставить несколько JSON-объектов, содержащих строки, представляющие предложения, описывающие некоторые факты о MySQL, каждый сопоставленный с соответствующим ключевым словом, в таблицу, созданную с помощью SQL-запроса, показанного здесь:
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-объекта одним из только что показанных способов вы можете увидеть, что обратные слэши присутствуют в значении столбца JSON, выполнив простое SELECT, как это:
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". |
+-----------------------------------------+
Предыдущий пример не работает так, как показано, если включен серверный SQL-режим NO_BACKSLASH_ESCAPES. Если этот режим установлен, для вставки литерала 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. (Ошибка #86866, Ошибка #26369555)
MySQL отбрасывает лишние пробелы между ключами, значениями или элементами в исходном документе JSON и оставляет (или вставляет при необходимости) один пробел после каждой запятой (,) или двоеточия (:) при отображении. Это делается для повышения удобочитаемости.
Функции MySQL, которые производят значения JSON (см. Раздел 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, за которым необязательно следуют селекторы, указывающие последовательно более конкретные части документа:
Точка, за которой следует имя ключа, называет член в объекте с заданным ключом. Имя ключа должно быть указано в двойных кавычках, если имя без кавычек не является допустимым в выражениях путей (например, если оно содержит пробел).
-
[, добавленный кN]path, который выбирает массив, называет значение в позиции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) -
[указывает подмножество или диапазон значений массива, начиная со значения в позицииMtoN]Mи заканчивая значением в позицииN.lastподдерживается как синоним индекса правого элемента массива. Поддержка относительной адресации элементов массива также включена. Еслиpathне выбирает значение массива,path[последний] оценивается так же, как и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
NM и 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->pathJSON_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]соответствуетfalseв элементеbи удаляет его.Второй экземпляр
$[1].b[1]ничего не соответствует: этот элемент уже удалён, путь больше не существует и не оказывает никакого влияния.
Синтаксис путей JSON
Многие функции JSON, поддерживаемые MySQL и описанные в другом месте данного руководства (см. Раздел 14.17, «Функции JSON»), требуют выражения пути для идентификации конкретного элемента в документе JSON. Путь состоит из области действия пути, за которой следует один или несколько сегментов пути. Для путей, используемых в функциях JSON MySQL, область действия всегда является документом, по которому выполняется поиск или иные операции, представленная ведущим символом $. Сегменты пути разделены точками (.). Ячейки в массивах представлены как [, где 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 9.2 не поддерживает их.
Дикий символ * и маркер ** используются следующим образом:
.*представляет значения всех членов объекта.[*]представляет значения всех ячеек в массиве.-
[представляет все пути, начинающиеся сprefix]**suffixprefixи заканчивающиесяsuffix.prefixнеобязательно, аsuffixобязательно; другими словами, путь не может заканчиваться на**.Кроме того, путь не может содержать последовательность
***.
Примеры синтаксиса путей см. в описаниях различных функций JSON, которые принимают пути в качестве аргументов, таких как JSON_CONTAINS_PATH(), JSON_SET() и JSON_REPLACE(). Примеры, включающие использование диких символов * и **, см. в описании функции JSON_SEARCH().
MySQL также поддерживает нотацию диапазона для подмножеств массивов JSON, используя ключевое слово to (например, $[2 to
10]), а также ключевое слово 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
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Строки упорядочиваются лексикографически по первым
Nбайтам двоичного представления двух сравниваемых строк, гдеN— это длина более короткой строки. Если первыеNбайты двух строк идентичны, более короткая строка считается меньшей, чем более длинная строка.Пример:
"a" < "ab" < "b" < "bc"
Это упорядочение эквивалентно упорядочению SQL строк с сортировкой
utf8mb4_bin. Так как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 согласно правилам в следующей таблице, затем значения сравниваются как описано ранее.
Преобразование между 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 | Приводит к значению NULL типа JSON. | Не применимо. |
| Типы геометрии | Значение геометрии преобразуется в документ JSON с помощью вызова ST_AsGeoJSON(). | Незаконная операция. Обходной путь: передать результат CAST( в ST_GeomFromGeoJSON(). |
| Все остальные типы | Приводит к документу JSON, содержащему единственное скалярное значение. | Успешно, если документ JSON состоит из единственного скалярного значения целевого типа и это скалярное значение может быть приведено к целевому типу. В противном случае возвращает NULL и выводит предупреждение. |
Сортировка и GROUP BY для значений JSON работают по этим принципам:
Порядок скалярных значений JSON использует те же правила, что и в предыдущем обсуждении.
Для возрастающей сортировки SQL
NULLсортирует перед всеми значениями JSON, включая JSON null-литерал; для убывающей сортировки SQLNULLсортирует после всех значений JSON, включая JSON null-литерал.Ключи сортировки для значений JSON ограничены значением системной переменной
max_sort_length, поэтому ключи, отличающиеся только после первыхmax_sort_lengthбайтов, сравниваются как равные.Сортировка нескалярных значений в настоящее время не поддерживается и возникает предупреждение.
Для сортировки может быть полезно привести скалярное значение JSON к какому-либо другому встроенному типу MySQL. Например, если столбец с именем jdoc содержит JSON объекты, имеющие член, состоящий из ключа id и неотрицательного значения, используйте это выражение для сортировки по значениям 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 значений может не давать осмысленного результата.
© 2025 Oracle
Licensed under the GPLv2 License.