Разбор задач из Озона для аналитиков
На собеседованиях любят простые по формулировке задачи, которые при этом сразу проверяют: понимаешь ли ты, как работает SQL и умеешь ли мыслить данными. Хочу разобрать две задачи, которые «встряхнули» меня на интервью. Первая — про JOIN‑ы, вторая — про пересчёт валют.
Задача 1
Две таблички с набором строк:
- слева 4, 5, 6 (Tab 1)
- справа 4, 4, 5, 5, (Tab 2)
Вопрос: скажите количество строк в результирующей таблице при 1. inner join 2. left join 3. cross join
Решение
- INNER JOIN берёт каждую строку из левой таблицы и ищет в правой все строки, где id совпадает. Если для одного значения в левой таблице есть несколько подходящих строк в правой — то SQL создаёт отдельную строку результата для КАЖДОГО совпадения. Это как правило умножения: 1 строка слева × N совпадений справа → N строк в результате.
У нас получается две строки: первая это 4,4; вторая 4;4 так как в табличке справа всего одно значение 4. Далее три пятерки также создают две строки. Всего 4 строки
- LEFT JOIN берёт все строки из левой таблицы (Tab 1) и “подклеивает” подходящие строки из Tab 2.
Если совпадений нет → будет NULL в части Tab 2. Разберём:
id=4 — 2 совпадения → 2 строки
id=5 — 2 совпадения → 2 строки
id=6 — нет совпадений → 1 строка (tab2.id = NULL)
Итого: 2 + 2 + 1 = 5 строк
- CROSS JOIN — декартово произведение: каждая строка Tab 1 умножается на каждую строку Tab 2. В Tab 1 — 3 строки, в Tab 2 — 4 строки. Итог: 3 × 4 = 12 строк
Задача 2. Пересчёт транзакций в рубли
Дано две таблицы:
transactions
- id (int) — PK
- date (date) — дата транзакции (может быть и выходной)
- currency (varchar) — валюта
- amount (float) — сумма в валюте
exchange
- id (int) — PK
- date (date) — дата публикации курса (только будние дни)
- currency (varchar) — валюта
- exc_cource (float) — курс валюты к рублю
Задача:
Добавить в transactions поле rub_amount = сумма в рублях.
- если курс есть на дату транзакции — берём его,
- если нет (например, выходные) — берём последний доступный курс до этой даты.
Основное затруднение
— Курс есть не на любую дату, а только на последние рабочие дни
— Поэтому на выходных берём последний курс до транзакции
Ошибочный путь
Я на собесе сначала пытался просто присоединить по дате и валюте. Но если транзакция попадает на выходной, курса на этот день нет → результат NULL.
Правильный алгоритм
- Для каждой транзакции фильтруем курсы по нужной валюте.
- Оставляем только те, где date <= дата транзакции.
- Берём курс с максимальной датой из этих.
- Умножаем amount * exc_cource.
SELECT
t.*, -- 1. Берём все поля из таблицы транзакций
t.amount * e.exc_cource AS rub_amount -- 2. Добавляем новый столбец: пересчёт суммы в рубли
FROM
transactions t -- 3. Основная таблица транзакций
LEFT JOIN exchange e -- 4. Присоединяем таблицу курсов
ON e.currency = t.currency -- по совпадению валюты
AND e.date = ( -- и по дате:
SELECT MAX(date) -- 5. Вложенный запрос: находим
FROM exchange -- среди курсов
WHERE currency = t.currency -- по нужной валюте
AND date <= t.date -- и не позже даты транзакции!
)Эта задача проверяет умение правильно «подбирать» данные, когда они неполные. На практике это встречается часто (календарные ряды, цены на дату, остатки складов).
Пошагово:
- Читаем из transactions по одной строке.
- Для каждой строки ищем ВСЕ курсы в exchange с этой валютой и датой не позже транзакции.
- Среди них берём САМУЮ ПОЗДНЮЮ дату (MAX(date)).
- Присоединяем найденную строку exchange к транзакции (по валюте и дате курса).
- Считаем сумму в рублях.
- Если курс не найден — rub_amount будет NULL (транзакция останется без курса).
Итог
Две задачи, простые на вид, позволяют показать логику:
- Задача 1 тренирует понимание join‑ов.
- Задача 2 — умение работать с неполными данными и аккуратно подбирать «последнее известное значение».
Именно такие кейсы на собесах отличают того, кто просто знает SQL‑синтаксис, от того, кто умеет применять его для аналитики.