UPDATE FROM в PostgreSQL: сначала сделайте источник однозначным

Допустим, нужно перенести согласованные цены из staging в рабочий каталог. В staging осталось две версии цены одного товара: поставщик отправил исправление, а загрузчик сохранил обе строки. Обычный SELECT с JOIN показывает две строки, но UPDATE изменяет товар один раз. Какую именно цену получит каталог?

В UPDATE FROM одна целевая строка должна сопоставляться максимум с одной строкой источника. Если совпадений несколько, PostgreSQL использует одно из них; строить бизнес-правило на выборе такой строки нельзя. ORDER BY в отдельном просмотре staging этого отношения не меняет.

Как изменить решение

WITH ranked AS (
  SELECT product_id, price,
         row_number() OVER (
           PARTITION BY product_id
           ORDER BY version DESC, import_id DESC
         ) AS rn
  FROM price_stage
)
UPDATE products AS p
SET price = r.price
FROM ranked AS r
WHERE r.rn = 1 AND r.product_id = p.id
RETURNING p.id, p.price;

Для товара с версиями 4 и 5 будет выбрана версия 5. Если версия совпала, решение определит import_id. Этот столбец должен иметь согласованный смысл: например, уникальный номер принятой поставки. Случайный порядок строк в файле таким правилом не становится.

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

Что проверить на своих данных

Перед обновлением выполните SELECT product_id, count(*) FROM price_stage GROUP BY product_id HAVING count(*) > 1. Затем добавьте в тест две строки одной версии и проверьте правило разрешения ничьей. После UPDATE сверяйте сами пары товар–цена, а не только число затронутых строк.

Отдельно решите, что означает отсутствующая цена. NULL в новой версии может отменять цену или означать, что поле не прислали. COALESCE автоматически выберет вторую трактовку и способен скрыть корректное обнуление. Правило версий определяет победившую запись; правило пустых полей определяет её применение. Их полезно проверять разными примерами.

Практика дальше

После однозначного обновления цен следующая задача — встроить его в воспроизводимую загрузку. На Data Engineering Fast-Track вы собираете конвейер Kafka → Spark → Lakehouse под управлением Airflow: с контрактами данных, расписанием и backfill. Так подготовка источника становится частью пайплайна, который можно повторить после сбоя.

Посмотреть программу и практику курса

Источник: UPDATE FROM в PostgreSQL — официальная документация.

Другие задачи по теме: PostgreSQL: запись, блокировки и планы запросов.

Продолжить на практике

  • Data Engineering Fast‑Track: Kafka → Airflow → Spark ↗

    Практический fast-track по data engineering: за 4–6 недель собираем рабочий конвейер Kafka → Spark → Lakehouse под управлением Airflow. Разберём batch и streaming, окна и watermark, схемы и контракты. Мини‑проекты, Docker Compose, шаблоны DAG и пайплайнов. Курс подойдёт инженерам, аналитикам и тем, кто хочет освоить современные пайплайны данных с уверенным Python.