Как сохранить порядок входного массива через WITH ORDINALITY в PostgreSQL

Как сохранить порядок входного массива через WITH ORDINALITY в PostgreSQL
Содержание статьи

Клиент передал ID товаров [30, 10, 30, 99]. Нужно получить именно четыре позиции: повтор 30 сохранить, а для неизвестного 99 показать отсутствие товара. В разборе Mentoring.digital развернём массив в строки, добавим номер позиции и соединим результат с каталогом.

Список значений не задаёт порядок результата

Условие WHERE id = ANY(...) отвечает на вопрос о принадлежности набору. Оно не превращает повторяющийся ID в две строки каталога и не закрепляет порядок выдачи. Поэтому для этой задачи входной список должен стать самостоятельной таблицей с позицией каждого элемента.

unnest() разворачивает массив, а WITH ORDINALITY добавляет к результату порядковый номер типа bigint начиная с 1. Названия id и pos ниже задаются явно через псевдоним.

Разверните массив и соедините его с каталогом

WITH catalog(id, title) AS (
    VALUES (10, 'Notebook'), (30, 'Pen')
)
SELECT requested.pos, requested.id, catalog.title
FROM unnest(ARRAY[30, 10, 30, 99])
     WITH ORDINALITY AS requested(id, pos)
LEFT JOIN catalog ON catalog.id = requested.id
ORDER BY requested.pos;

Ожидаемые строки: 1, 30, Pen; 2, 10, Notebook; 3, 30, Pen; 4, 99, NULL. Здесь все данные определены внутри запроса, существующие таблицы не изменяются.

LEFT JOIN сохраняет входную позицию даже без совпадения. INNER JOIN удалил бы строку 99. Повтор 30 остаётся дважды, потому что во входной таблице ему соответствуют две разные позиции. Окончательный порядок закрепляет именно ORDER BY requested.pos: наличие номера само по себе не сортирует результат.

Проверьте пустой массив и неоднозначное соединение

SELECT value, pos
FROM unnest(ARRAY[]::integer[])
     WITH ORDINALITY AS input(value, pos)
ORDER BY pos;

SELECT value, pos
FROM unnest(ARRAY[NULL, 10]::integer[])
     WITH ORDINALITY AS input(value, pos)
ORDER BY pos;

Первый запрос вернёт ноль строк. Второй сохраняет NULL как первый элемент с позицией 1, затем значение 10 с позицией 2. Порядковый номер означает положение в развёрнутом результате, а не исходную нижнюю границу индекса массива.

В реальном каталоге ID должен однозначно определять товар. Если соединяемая таблица содержит несколько строк с одним ID, каждая входная позиция может размножиться. Не маскируйте это случайным DISTINCT: сначала проверьте ключ и смысл соединения.

Перенесите пример в приложение без потери условий

Передавайте массив параметром запроса, сохраняя нужный тип, вместо сборки SQL из текста пользователя. Зафиксируйте договорённость: нужны ли повторы, что делать с неизвестным ID и допустим ли NULL во входе. Изменение одного из этих решений меняет ожидаемый результат.

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

Похожие статьи по данной теме