Gymterview
middle

Как работать с массивами в PostgreSQL?

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

Пример
CREATE TABLE products (
    id serial PRIMARY KEY,
    name text,
    tags text[],
    prices numeric[]
);

INSERT INTO products (name, tags, prices) VALUES
('Кредит наличными', ARRAY['кредит', 'физлица', 'наличные'], ARRAY[100000, 500000, 1000000]),
('Вклад "Надёжный"', '{вклад,физлица,депозит}', '{50000,100000}');
Операции с массивами
-- Доступ к элементу (индексация с 1!)
SELECT tags[1] FROM products;  -- 'кредит'

-- Срез массива
SELECT tags[1:2] FROM products;  -- {'кредит','физлица'}

-- Проверка вхождения элемента
SELECT * FROM products WHERE 'кредит' = ANY(tags);
SELECT * FROM products WHERE tags @> ARRAY['кредит'];

-- Проверка пересечения массивов
SELECT * FROM products WHERE tags && ARRAY['вклад', 'кредит'];  -- overlap

-- Конкатенация
SELECT tags || ARRAY['новый_тег'] FROM products;
SELECT array_append(tags, 'новый_тег') FROM products;

-- Удаление элемента
SELECT array_remove(tags, 'физлица') FROM products;

-- Длина массива
SELECT array_length(tags, 1) FROM products;

-- Разворачивание массива в строки
SELECT unnest(tags) AS tag FROM products;

-- Агрегация в массив
SELECT array_agg(name) FROM products WHERE 'физлица' = ANY(tags);

Индексирование массивов

Пример
-- GIN-индекс для поиска по массивам
CREATE INDEX idx_products_tags ON products USING GIN (tags);

-- После создания индекса эффективно работают: @>, &&, <@
SELECT * FROM products WHERE tags @> ARRAY['кредит', 'физлица'];

Когда массивы, а когда отдельная таблица

  • Массивы — для небольших списков тегов/меток, которые в основном читаются целиком
  • Отдельная таблица (many-to-many) — если нужны JOIN, агрегации, FOREIGN KEY, или если массивы большие

На собеседовании: индексация массивов в PostgreSQL начинается с 1, а не с 0 — классический вопрос-ловушка. Также стоит упомянуть, что для поиска по массивам нужен GIN-индекс, а не B-tree.