Собесов

Finstar SQL — оставить одну актуальную строку на ID_клиента

SQLДедупликацияЛёгкаяJunior

Условие

Файл клиентской информации содержит дубли по ID_клиента (одна сущность, разные снимки). Строки не являются полными дублями — могут отличаться по фамилии, месту работы и пр.

Написать запрос, оставляющий по каждому ID_клиента ровно одну строку. Актуальной считаем последнюю; если определить «последнюю» сложно — берём любую.

clients(ID_клиента, Фамилия, Имя, Отчество, Дата_рождения, Место_работы, Должность, ...)

Решение

Подход

Если есть колонка времени (updated_at, dt_load, etl_ts) — берём последнюю по ней. Если нет — нужен любой стабильный tie-breaker (любой rowid / случайный).

Вариант 1 — есть updated_at

SELECT *
FROM (
  SELECT
    *,
    ROW_NUMBER() OVER (PARTITION BY ID_клиента ORDER BY updated_at DESC) AS rn
  FROM clients
) t
WHERE rn = 1;

Вариант 2 — есть только rowid (PostgreSQL: ctid; Oracle: ROWID)

DELETE FROM clients
WHERE ctid NOT IN (
  SELECT MIN(ctid)
  FROM clients
  GROUP BY ID_клиента
);

или select-вариант:

SELECT DISTINCT ON (ID_клиента) *
FROM clients;            -- PostgreSQL: возьмёт «первую» в естественном порядке

Вариант 3 — нет ничего, просто «любая»

SELECT *
FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY ID_клиента ORDER BY (SELECT 1)) AS rn
  FROM clients
) t
WHERE rn = 1;

ORDER BY (SELECT 1) или ORDER BY 1 — недетерминированный, но рабочий.

Если в проде нужно сделать таблицу уникальной

-- Создать новую с UNIQUE-ключом, перелить туда дедуп
CREATE TABLE clients_dedup AS
SELECT DISTINCT ON (ID_клиента) *
FROM clients;
ALTER TABLE clients_dedup ADD CONSTRAINT pk_clients PRIMARY KEY (ID_клиента);

Подводные камни

  1. SELECT DISTINCT * не сработает — строки не полные дубли, отличаются хотя бы одним полем.
  2. GROUP BY ID + MAX(other_field) — ошибка. Это «склеит» поля от разных снимков (новая фамилия + старая должность) → франкенштейн.
  3. Tie-breaker не определён — повторный запуск может дать разные результаты, что критично для воспроизводимости отчётов. Лучше всегда иметь ETL-метку времени.
  4. Большие таблицы. ROW_NUMBER сортирует все строки — на миллиардах нужен индекс по (ID_клиента, updated_at DESC).
  5. Удалять (DELETE) или выгружать (SELECT) дедуп? Удаление в исходной таблице — необратимо. Лучше делать новую витрину.

Эталонный ответ

ROW_NUMBER() OVER (PARTITION BY ID_клиента ORDER BY updated_at DESC), фильтр rn=1. Если нет временной метки — DISTINCT ON (ID_клиента) (PostgreSQL) или любой фиктивный ORDER BY. Не использовать MAX/MIN по другим полям отдельно — это смешает данные из разных снимков.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти