Расширенные типы данных в PostgreSQL

PostgreSQLBeginner
Практиковаться сейчас

Введение

В этой лабораторной работе вы изучите расширенные типы данных PostgreSQL, сосредоточившись на JSON/JSONB, массивах и UUID. Вы узнаете, как хранить, запрашивать и обрабатывать данные этих типов.

Лабораторная работа начинается с демонстрации того, как хранить и запрашивать данные JSON и JSONB, включая создание таблицы со столбцом JSONB, вставку JSON-данных и использование операторов -> и ->> для извлечения конкретных значений. Затем вы перейдете к изучению столбцов-массивов и UUID.

Хранение и запрос данных JSON и JSONB

На этом этапе вы узнаете, как хранить и запрашивать данные JSON и JSONB в PostgreSQL. PostgreSQL предоставляет два типа данных для хранения JSON: JSON и JSONB. Тип данных JSON хранит точную копию входного текста JSON, в то время как тип JSONB хранит данные JSON в декомпозированном бинарном формате. JSONB обычно предпочтительнее, так как он обеспечивает более высокую производительность при выполнении запросов и индексации.

Давайте начнем с открытия оболочки PostgreSQL. Сначала подключитесь к базе данных labex:

sudo -u postgres psql -d labex

Вы должны увидеть приглашение PostgreSQL:

labex=#

Теперь создадим таблицу для хранения данных JSONB:

CREATE TABLE products (id SERIAL PRIMARY KEY, data JSONB);

Эта SQL-команда создает таблицу с именем products. В таблице есть два столбца: id (автоинкрементный целочисленный первичный ключ) и data (столбец JSONB для хранения JSON-данных).

Вы должны увидеть вывод, похожий на этот:

CREATE TABLE

Теперь вставим некоторые данные в таблицу products:

INSERT INTO products (data) VALUES ('{"name": "Laptop", "price": 1200, "features": ["16GB RAM", "512GB SSD"]}');
INSERT INTO products (data) VALUES ('{"name": "Keyboard", "price": 75, "features": ["Mechanical", "RGB Backlight"]}');

Эти команды вставляют две строки в таблицу products. Каждая строка содержит JSON-объект с информацией о продукте.

Для каждой вставки вы должны увидеть вывод, похожий на этот:

INSERT 0 1

Для запроса JSON-данных можно использовать операторы -> и ->>. Оператор -> возвращает JSON-объект, а оператор ->> возвращает значение JSON в виде текста.

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

SELECT data ->> 'name' FROM products WHERE id = 1;

Эта команда выбирает значение, связанное с ключом name из столбца data таблицы products, где id равен 1. Оператор ->> гарантирует, что результат будет возвращен в виде текста.

Вы должны увидеть вывод, похожий на этот:

  ?column?
----------
 Laptop
(1 row)

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

SELECT data -> 'features' ->> 0 FROM products WHERE id = 1;

Эта команда сначала выбирает массив features из столбца data, а затем выбирает элемент с индексом 0 из этого массива. Оператор ->> гарантирует, что результат будет возвращен в виде текста.

Вы должны увидеть вывод, похожий на этот:

  ?column?
----------
 16GB RAM
(1 row)

Вы также можете использовать оператор @>, чтобы проверить, содержит ли JSON-объект определенную пару ключ-значение. Например, чтобы найти все продукты с ценой 75, можно использовать следующий запрос:

SELECT data ->> 'name' FROM products WHERE data @> '{"price": 75}';

Эта команда выбирает имя всех строк из таблицы products, где столбец data содержит JSON-объект с ключом price и значением 75.

Вы должны увидеть вывод, похожий на этот:

  ?column?
----------
 Keyboard
(1 row)

Отлично! Мы успешно создали нашу первую таблицу с данными JSONB и научились делать к ней запросы. Мы продолжим работать с этой таблицей на следующем этапе, чтобы добавить более продвинутые функции.

Добавление столбцов-массивов в существующую таблицу

На этом этапе вы узнаете, как добавлять столбцы-массивы и работать с ними в PostgreSQL. Мы расширим нашу существующую таблицу products, добавив столбец-массив для хранения тегов. Столбцы-массивы позволяют хранить несколько значений одного и того же типа данных в одном столбце, что полезно для хранения списков элементов, таких как теги, категории или характеристики.

Поскольку мы уже подключены к базе данных и у нас есть таблица products с предыдущего этапа, давайте добавим столбцы-массивы в нашу таблицу:

ALTER TABLE products ADD COLUMN name VARCHAR(255);
ALTER TABLE products ADD COLUMN tags TEXT[];

Эти команды добавляют два новых столбца в нашу таблицу products: name (строка) и tags (массив строк). Тип данных TEXT[] указывает, что столбец tags является массивом текстовых значений.

Для каждой команды ALTER вы должны увидеть вывод, похожий на этот:

ALTER TABLE

Теперь обновим существующие данные и вставим новые данные с использованием новых столбцов:

UPDATE products SET name = data ->> 'name' WHERE id = 1;
UPDATE products SET name = data ->> 'name' WHERE id = 2;
UPDATE products SET tags = ARRAY['electronics', 'computers', 'portable'] WHERE id = 1;
UPDATE products SET tags = ARRAY['electronics', 'accessories', 'input'] WHERE id = 2;

Эти команды обновляют наши существующие продукты информацией об имени и тегах, извлеченной из данных JSONB, и новыми значениями массивов.

Для каждого обновления вы должны увидеть вывод, похожий на этот:

UPDATE 1

Для запроса данных массива можно использовать индексацию массива. Индексы массивов в PostgreSQL начинаются с 1.

Например, чтобы получить первый тег первого продукта, можно использовать следующий запрос:

SELECT tags[1] FROM products WHERE id = 1;

Эта команда выбирает элемент с индексом 1 из массива tags таблицы products, где id равен 1.

Вы должны увидеть вывод, похожий на этот:

   tags
-----------
 electronics
(1 row)

Вы также можете использовать функцию UNNEST для развертывания массива в набор строк.

Например, чтобы получить все теги всех продуктов, можно использовать следующий запрос:

SELECT name, UNNEST(tags) AS tag FROM products;

Эта команда выбирает name и каждый отдельный tag из массива tags, создавая новую строку для каждого тега.

Вы должны увидеть вывод, похожий на этот:

  name   |     tag
---------+-------------
 Laptop  | electronics
 Laptop  | computers
 Laptop  | portable
 Keyboard| electronics
 Keyboard| accessories
 Keyboard| input
(6 rows)

Вы можете использовать оператор @>, чтобы проверить, содержит ли массив определенное значение.

Например, чтобы найти все продукты с тегом 'electronics', можно использовать следующий запрос:

SELECT name FROM products WHERE tags @> ARRAY['electronics'];

Эта команда выбирает имя всех строк из таблицы products, где массив tags содержит значение 'electronics'.

Вы должны увидеть вывод, похожий на этот:

  name
----------
 Laptop
 Keyboard
(2 rows)

Вы также можете использовать оператор &&, чтобы проверить, есть ли у двух массивов общие элементы.

Например, чтобы найти все продукты, у которых есть общие теги с первым продуктом, можно использовать следующий запрос:

SELECT p2.name FROM products p1, products p2 WHERE p1.id = 1 AND p1.tags && p2.tags AND p2.id != 1;

Эта команда выбирает имя всех строк из таблицы products (с псевдонимом p2), у которых есть хотя бы один общий тег с тегами первого продукта (с псевдонимом p1), исключая сам первый продукт.

Вы должны увидеть вывод, похожий на этот:

  name
----------
 Keyboard
(1 row)

Отлично! Мы успешно добавили столбцы-массивы в нашу таблицу и научились работать с массивами. Теперь наша таблица products содержит как типы данных JSONB, так и массивы, и готова к следующему этапу.

Добавление столбца UUID и генерация UUID

На этом этапе вы узнаете, как генерировать и использовать UUID (универсально уникальные идентификаторы) в PostgreSQL. UUID — это 128-битные числа, которые спроектированы так, чтобы быть уникальными в пространстве и времени. Они часто используются в качестве уникальных идентификаторов в таблицах баз данных, чтобы избежать конфликтов при объединении данных из разных источников.

Поскольку мы продолжаем работать в текущем сеансе с нашей таблицей, мы добавим столбец UUID в таблицу products, чтобы продемонстрировать функциональность UUID.

Сначала включим расширение UUID, которое предоставляет функции генерации UUID:

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

Эта команда создает расширение uuid-ossp, если оно еще не существует.

Вы должны увидеть вывод, похожий на этот:

CREATE EXTENSION

Теперь добавим столбец UUID в нашу таблицу products:

ALTER TABLE products ADD COLUMN uuid_id UUID;

Эта команда добавляет новый столбец UUID в нашу таблицу products.

Вы должны увидеть вывод, похожий на этот:

ALTER TABLE

Теперь мы можем обновить существующие строки значениями UUID, используя функцию uuid_generate_v4():

UPDATE products SET uuid_id = uuid_generate_v4() WHERE id = 1;
UPDATE products SET uuid_id = uuid_generate_v4() WHERE id = 2;

Эти команды обновляют наши существующие продукты уникальными значениями UUID. Функция uuid_generate_v4() генерирует новый UUID для каждой строки.

Для каждого обновления вы должны увидеть вывод, похожий на этот:

UPDATE 1

Для запроса данных с использованием UUID можно использовать значения UUID в предложениях WHERE. Поскольку UUID генерируются случайным образом, давайте сначала посмотрим на наши текущие данные:

SELECT id, name, uuid_id FROM products;

Эта команда выбирает все столбцы из таблицы products, чтобы увидеть сгенерированные UUID вместе с другими нашими данными.

Вы должны увидеть вывод, похожий на этот (ваш UUID будет другим):

 id |   name   |               uuid_id
----+----------+--------------------------------------
  1 | Laptop   | 8f14e45f-ea7b-4f9f-a2b0-73f9c3f85a9b
  2 | Keyboard | c9f0f895-fb98-4635-bd31-4f7f4d8f9e7a
(2 rows)

Теперь используйте этот UUID в следующей команде, заменив <YOUR_UUID_HERE> на фактический UUID, который вы получили:

SELECT name FROM products WHERE uuid_id = '<YOUR_UUID_HERE>';

Эта команда выбирает name из таблицы products, где uuid_id соответствует указанному UUID.

Вы должны увидеть вывод, похожий на этот (в зависимости от того, какой UUID вы выбрали):

  name
----------
 Laptop
(1 row)

Сохраните таблицу products и расширение uuid-ossp, так как на следующем этапе мы продолжим их использовать.

Извлечение данных из расширенных типов

На этом заключительном этапе вы потренируетесь извлекать данные из всех расширенных типов данных, которые мы добавили в нашу таблицу products в ходе этой лабораторной работы. Теперь наша таблица содержит столбцы JSONB, массивы и UUID, что является исчерпывающим примером расширенных типов данных PostgreSQL.

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

Сначала добавим дополнительные данные JSONB, чтобы наши примеры извлечения стали более полными:

UPDATE products SET data = '{"brand": "Dell", "model": "XPS 13", "specs": {"ram": "16GB", "storage": "512GB SSD"}, "warranty": "3 years"}' WHERE id = 1;
UPDATE products SET data = '{"brand": "Logitech", "model": "G915", "specs": {"type": "Mechanical", "backlight": "RGB"}, "warranty": "2 years"}' WHERE id = 2;

Эти команды обновляют наши существующие данные JSONB более подробной информацией, включая вложенные объекты и дополнительные поля.

Для каждого обновления вы должны увидеть вывод, похожий на этот:

UPDATE 1

Теперь давайте потренируемся извлекать данные из всех наших расширенных типов. Чтобы извлечь данные из столбца JSONB data, можно использовать операторы -> и ->>. Например, чтобы извлечь бренд из наших обновленных данных:

SELECT data ->> 'brand' FROM products WHERE id = 1;

Эта команда извлекает значение, связанное с ключом brand из столбца data.

Вы должны увидеть вывод, похожий на этот:

 ?column?
----------
 Dell
(1 row)

Чтобы извлечь вложенные данные из столбца JSONB, можно объединить операторы -> и ->>. Например, чтобы извлечь характеристику RAM:

SELECT data -> 'specs' ->> 'ram' FROM products WHERE id = 1;

Эта команда извлекает значение, связанное с ключом ram внутри объекта specs.

Вы должны увидеть вывод, похожий на этот:

 ?column?
----------
 16GB
(1 row)

Чтобы извлечь данные из столбца tags (массив), можно использовать индексацию массива, как мы узнали на этапе 2. Например, чтобы извлечь первый тег первого продукта:

SELECT tags[1] FROM products WHERE id = 1;

Эта команда извлекает элемент с индексом 1 из массива tags таблицы products, где id равен 1.

Вы должны увидеть вывод, похожий на этот:

   tags
-----------
 electronics
(1 row)

Теперь давайте создадим комплексный запрос, который извлекает данные из всех наших расширенных типов данных — JSONB, массивов и UUID:

SELECT
    id,
    name,
    data ->> 'brand' AS brand,
    data -> 'specs' ->> 'ram' AS ram,
    tags[1] AS first_tag,
    uuid_id
FROM products;

Эта команда извлекает данные из всех расширенных типов данных, с которыми мы работали: целочисленный id, name, brand из столбца JSONB data, вложенную характеристику ram, первый элемент массива tags и uuid_id.

Вы должны увидеть вывод, похожий на этот:

   name   |  ?column?  |   tags
----------+------------+-----------
 Laptop   | Dell       | electronics
 Keyboard | Logitech   | electronics
(2 rows)

Отлично! Вы успешно поработали со всеми тремя расширенными типами данных PostgreSQL в одной таблице. Этот комплексный пример демонстрирует, как JSONB, массивы и UUID могут использоваться вместе для создания гибких и мощных схем баз данных.

Когда вы закончите все этапы, вы можете выйти из оболочки PostgreSQL, введя:

\q

Вы также можете оставить таблицу для дальнейших экспериментов с расширенными типами данных PostgreSQL.

Резюме

В этой лабораторной работе вы постепенно создали и поработали с комплексной таблицей products, которая демонстрирует расширенные типы данных PostgreSQL в действии. Вы начали с создания таблицы со столбцами JSONB и научились хранить и запрашивать данные JSON с помощью операторов -> и ->>.

Затем вы расширили таблицу, добавив столбцы-массивы, научившись хранить несколько значений в одном столбце и запрашивать их с помощью индексации массивов и функций, таких как UNNEST. Далее вы добавили функциональность UUID, включив расширение uuid-ossp и добавив столбец UUID для генерации уникальных идентификаторов.

Наконец, вы попрактиковались в комплексных методах извлечения данных, которые объединили все три расширенных типа данных — JSONB, массивы и UUID — в сложных запросах. Этот пошаговый подход продемонстрировал, как эти типы данных могут работать вместе в реальных схемах баз данных, обеспечивая гибкость и мощные возможности запросов для современных приложений.