Как вывести актуальный адрес и цену для каждого объявления в SQL
Что проверяют
Умеете ли вы выбрать одну последнюю запись из истории изменений и не размножить при этом строки. Объявления живут долго, адрес и цена меняются, и в хранилище обычно лежат не значения, а их версии с датой. Следом почти всегда спрашивают, что будет, если у двух версий одинаковая метка времени.
Как отвечать
Начните с уточнения схемы: цены и адреса хранятся в отдельных таблицах истории вида ad_id, value, updated_at или как версии с valid_from и valid_to (SCD второго типа). Дальше каркас:
- оконная функция:
ROW_NUMBER() OVER (PARTITION BY ad_id ORDER BY updated_at DESC)и фильтрrn = 1во внешнем запросе. Работает в любой современной базе, а в Snowflake, BigQuery и DuckDB фильтр записывается короче черезQUALIFY; DISTINCT ON (ad_id): короткий вариант для PostgreSQL, обязательно сORDER BY ad_id, updated_at DESC;LATERALили коррелированный подзапрос:ORDER BY updated_at DESC LIMIT 1на каждое объявление, быстро при индексе(ad_id, updated_at DESC);argMax(price, updated_at): идиоматичный способ в ClickHouse;- версии с интервалами: условие
valid_to IS NULLдля текущей версии.
Ключевой момент: последнюю цену и последний адрес выбирают отдельно, в двух CTE, и только потом присоединяют к таблице объявлений через LEFT JOIN. Иначе объявление без цены пропадёт из выдачи. Для воспроизводимого результата добавьте в сортировку второй ключ, например id версии.
Где спотыкаются
Соединяют объявления сразу с обеими историями, а потом ищут максимум. Каждая версия цены умножается на каждую версию адреса, и результат либо раздувается, либо склеивает цену одной даты с адресом другой.
Пишут соединение с max(updated_at) через GROUP BY и не думают о равных датах: при двух записях с одной меткой времени объявление задвоится. ROW_NUMBER всегда оставит одну строку, а второй ключ сортировки сделает выбор стабильным.
Ставят фильтр по истории в WHERE после LEFT JOIN. Условие на правую таблицу превращает внешнее соединение во внутреннее, и объявления без адреса исчезают. Такое условие должно стоять в ON.