Статьи

Как использовать поля данных JSON в базах данных MySQL

JSON в MySQL В моей статье « SQL против NoSQL: различия» я упоминал, что грань между базами данных SQL и NoSQL становится все более размытой, поскольку каждый лагерь перенимает функции другого. MySQL 5.7 Базы данных InnoDB и PostgreSQL 9.4 обе напрямую поддерживают типы документов JSON в одном поле. В этой статье мы рассмотрим реализацию JSON MySQL более подробно.

(PostgreSQL поддерживал JSON до версии 9.4, и любая база данных будет принимать документы JSON в виде одного строкового двоичного объекта. Однако MySQL и PostgreSQL теперь напрямую поддерживают проверенные данные JSON в реальных парах ключ / значение, а не в базовой строке.)

Просто потому, что вы можете хранить JSON …

… это не следует за вами.

Нормализация — это метод, используемый для оптимизации структуры базы данных. Правило «Первая нормальная форма» (1NF) определяет, что каждый столбец должен содержать одно значение, что нарушается при хранении многозначных документов JSON.

Если у вас есть четкие требования к реляционным данным, используйте соответствующие поля с одним значением. JSON следует использовать в качестве крайней меры. Поля значений JSON не могут быть проиндексированы, поэтому избегайте его использования в столбцах, которые обновляются или регулярно ищутся. Кроме того, все меньше клиентских приложений поддерживают JSON, а технология новее и, возможно, менее стабильна, чем другие типы.

Тем не менее, существуют хорошие сценарии использования JSON для малонаселенных данных или пользовательских атрибутов.

Создать таблицу с полем JSON

Рассмотрим магазин по продаже книг. Таблица книг будет иметь идентификатор, номер ISBN , название, издателя, количество страниц и другие данные, относящиеся ко всем книгам. Предположим, мы хотим добавить любое количество тегов категории в любую книгу. Мы могли бы добиться этого в SQL, используя:

  1. таблица тегов, в которой каждое имя тега хранится по уникальному идентификатору
  2. таблица тегов с записями « многие ко многим», отображающая идентификаторы книг в идентификаторы тегов

Это будет работать, но это громоздкие и значительные усилия для незначительной функции. Поэтому мы определим поле JSON тегов в таблице книг нашей базы данных MySQL:

CREATE TABLE `book` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `tags` json DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

Обратите внимание, что столбцы JSON не могут иметь значение по умолчанию, использоваться в качестве первичного ключа, ссылаться на внешний ключ или иметь индекс. Вы можете создавать вторичные индексы для сгенерированных виртуальных столбцов , но, возможно, проще сохранить индексированное значение в отдельном поле.

Добавление данных JSON

Целые документы JSON могут быть переданы в операторах INSERT или UPDATE . Например, наши теги книги могут быть переданы в виде массива:

 INSERT INTO `book` (`title`, `tags`)
VALUES (
  'ECMAScript 2015: A SitePoint Anthology',
  '["JavaScript", "ES2015", "JSON"]'
);

JSON также может быть создан с помощью:

  • Функция JSON_ARRAY (), которая создает массивы, например
     -- returns [1, 2, "abc"]:
    SELECT JSON_ARRAY(1, 2, 'abc');
  • JSON_OBJECT () функция, которая создает объекты, например
     -- returns {"a": 1, "b": 2}:
    SELECT JSON_OBJECT('a', 1, 'b', 2);
  • JSON_MERGE () функция для объединения документов, например
     -- returns ["a", 1, {"key": "value"}]:
    SELECT JSON_MERGE('["a", 1]', '{"key": "value"}');
  • или вы можете (CAST anyValue AS JSON)

Функция JSON_TYPE () позволяет вам проверять типы значений JSON. Должен возвращать OBJECT, ARRAY или ошибку, например

 -- returns ARRAY:
SELECT JSON_TYPE('[1, 2, "abc"]');

-- returns OBJECT:
SELECT JSON_TYPE('{"a": 1, "b": 2}');

-- returns an error:
SELECT JSON_TYPE('{"a": 1, "b": 2');

Точно так же функция JSON_VALID () возвращает 1, когда JSON действителен:

 -- returns 1:
SELECT JSON_TYPE('[1, 2, "abc"]');

-- returns 1:
SELECT JSON_TYPE('{"a": 1, "b": 2}');

-- returns 0:
SELECT JSON_TYPE('{"a": 1, "b": 2');

Попытка вставить недопустимый документ JSON вызовет ошибку, и вся запись не будет вставлена ​​/ обновлена.

Поиск данных JSON

Функция JSON_CONTAINS () принимает искомый документ JSON и другой для сравнения. Возвращает 1, когда совпадение найдено, например

 -- all books with the 'JavaScript' tag:
SELECT * FROM `book` 
WHERE JSON_CONTAINS(tags, '["JavaScript"]');

Аналогичная функция JSON_SEARCH () возвращает путь к данному совпадению или NULL, если совпадения нет. Передается искомый документ JSON, 'one''all'

 -- all books with tags starting 'Java':
SELECT * FROM `book` 
WHERE JSON_SEARCH(tags, 'one', 'Java%') IS NOT NULL;

JSON Paths

Путь JSON предназначен для значений и может использоваться для извлечения или изменения частей документа. Функция JSON_EXTRACT () демонстрирует это путем извлечения одного или нескольких значений:

 -- returns "SitePoint":
SELECT JSON_EXTRACT(
  '{"id": 1, "website": "SitePoint"}', 
  '$.website'
);

Все определения пути JSON начинаются с символа $

  • период, за которым следует имя, например, $.website
  • [N]
  • a .[*]
  • [*]
  • подстановочный знак prefix**suffix

Ссылаясь на этот документ JSON:

 {
  "a": 1,
  "b": 2,
  "c": [3, 4],
  "d": {
    "e": 5,
    "f": 6
  }
}

следующие пути:

  • $.a1
  • $.c[3, 4]
  • $.c[1]4
  • $.d.e5
  • $**.e[5]

Извлечение путей JSON в запросах

Мы можем извлечь первый тег из нашей таблицы книг, используя запрос:

 SELECT
  name,
  tags->"$[0]" AS `tag1`
FROM `book`;

Для более сложного примера предположим, что у нас есть пользовательская таблица с данными профиля JSON, например

Я бы имя профиль
1 Craig {
«Твиттер»: «@craigbuckler»,
«Facebook»: «craigbuckler»,
«Googleplus»: «craigbuckler»
}
2 SitePoint {
«Твиттер»: «@sitepointdotcom»
}

Мы можем извлечь имя Twitter, используя путь JSON, например

 SELECT
  name, profile->"$.twitter" AS `twitter`
FROM `user`;

Мы также можем ссылаться на путь JSON в предложении WHERE, чтобы возвращать только пользователей с учетной записью Twitter:

 SELECT
  name, profile->"$.twitter" AS `twitter`
FROM `user`
WHERE
  profile->"$.twitter" IS NOT NULL;

Модификация части документа JSON

Существует несколько функций MySQL для изменения частей документа JSON с использованием обозначения пути. Это включает:

  • JSON_SET(doc, path, val[, path, val]...)
    вставляет или обновляет данные в документе
  • JSON_INSERT(doc, path, val[, path, val]...)
    вставляет данные в документ
  • JSON_REPLACE(doc, path, val[, path, val]...)
    заменяет данные в документе
  • JSON_MERGE(doc, doc[, doc]...)
    объединяет два или более документов
  • JSON_ARRAY_APPEND(doc, path, val[, path, val]...)
    добавляет значения в конец массива
  • JSON_ARRAY_INSERT(doc, path, val[, path, val]...)
    вставляет массив в документ
  • JSON_REMOVE(doc, path[, path]...)
    удаляет данные из документа.

Поэтому мы можем добавить тег «программирования» в любую книгу, в которой уже есть тег «JavaScript»:

 UPDATE `book`
  SET tags = JSON_MERGE(tags, '["technical"]')
WHERE
  JSON_SEARCH(tags, 'one', 'technical') IS NULL AND
  JSON_SEARCH(tags, 'one', 'JavaScript') IS NOT NULL;

Дальнейшая информация

Руководство MySQL предоставляет дополнительную информацию о типе данных JSON и связанных функциях JSON .

Опять же, я призываю вас не использовать JSON, если это не является абсолютно необходимым. Вы можете эмулировать всю документно-ориентированную базу данных NoSQL в MySQL, но это сведет на нет многие преимущества SQL , и вы также можете переключиться на настоящую систему NoSQL. Тем не менее, типы данных JSON могут сэкономить некоторые усилия для более неясных требований к данным в приложении на основе SQL.