Разбор тестового задания на SQL в EdTech-компании
Как и в любом навыке, регулярная практика — твой лучший друг. Особенно если речь идет про прохождение технических интервью, нужно постоянно держать себя в форме.
Предлагаю потренироваться на задачах с тестового задания на SQL.
Дано две таблицы.
Первая — finished_lessons, в ней лежит информация о пройденных уроках:
| Поле | Описание поля | Тип |
|---|---|---|
user_id | id пользователя | integer |
lesson_id | id урока | integer |
date_created | дата создания записи / дата прохождения урока | date |
Вторая — profession_dop, в ней лежит информация о подписках пользователей на профессии:
| Поле | Описание поля | Тип |
|---|---|---|
profession_name | название профессии | string |
user_id | id пользователя | integer |
subscription_date | дата подписки | date |
squad | поток | string |
Разберем несколько SQL-запросов по этим таблицам.
1. Необходимо посмотреть, какое количество пользователей находятся в потоке 17 по профессии data-analyst
Простой запрос из одной таблицы. Фильтруем profession_dop по потоку 17 и профессии data-analyst, затем считаем количество пользователей:
SELECT COUNT(DISTINCT user_id) AS usersFROM profession_dopWHERE profession_name = 'data-analyst' AND squad = '17';2. Необходимо посмотреть, какое количество пользователей по профессии data-scientist подписались в 2020 году
Тоже довольно прямолинейный запрос. Используем функцию YEAR() для извлечения года из даты подписки и фильтруем по 2020 году:
SELECT COUNT(DISTINCT user_id) AS usersFROM profession_dopWHERE profession_name = 'data-scientist' AND YEAR(subscription_date) = 2020;3. Необходимо найти, какое количество уроков прошли студенты по профессии project-manager
Для решения этой задачи подойдет и подзапрос user_id IN (SELECT …), и JOIN двух таблиц.
Предлагаемый ниже запрос обеспечивает лучшую читаемость и производительность за счет CTE:
WITH project_managers AS ( SELECT DISTINCT user_id FROM profession_dop WHERE profession_name = 'project-manager')SELECT COUNT(DISTINCT lesson_id) AS lessonsFROM finished_lessonsINNER JOIN project_managers USING(user_id);4. Необходимо найти первый пройденный урок (lesson_id) для пользователей, подписавшихся на профессию qa в марте 2020 года
Здесь я бы в первую очередь уточнял задачу у интервьюера, т.к. она может трактоваться по-разному:
1. Найти самый первый урок за все время для каждого пользователя из подписавшихся на профессию qa в марте 2020 года.
2. Найти первый урок по qa для каждого пользователя из подписавшихся на профессию qa в марте 2020 года.
Решение первого варианта восприятия:
WITH qa_users AS ( SELECT DISTINCT user_id FROM profession_dop WHERE profession_name = 'qa' AND YEAR(subscription_date) = 2020 AND MONTH(subscription_date) = 3),first_lesson_date AS ( SELECT fl.user_id, MIN(fl.date_created) AS first_lesson_date FROM finished_lessons fl INNER JOIN qa_users USING(user_id) GROUP BY fl.user_id)SELECT fl.user_id, fl.lesson_idFROM finished_lessons AS flINNER JOIN first_lesson_date AS fld ON fl.user_id = fld.user_id AND fl.date_created = fld.first_lesson_date;Решение второго варианта можете предложить в комментариях.
5. Необходимо вычислить, на какие из профессий подписано больше всего студентов — вывести топ-5
Используем оконную функцию DENSE_RANK() для ранжирования профессий по количеству студентов, затем выбираем топ-5.
Способ формирования топа лучше уточнять. В моем случае, если какая-то профессия делит с кем-то место — хотим видеть все и считать за одно место:
WITH profession_counts AS ( SELECT profession_name, COUNT(DISTINCT user_id) AS student_count, DENSE_RANK() OVER ( ORDER BY COUNT(DISTINCT user_id) DESC ) AS rank FROM profession_dop GROUP BY profession_name)SELECT profession_name, student_countFROM profession_countsWHERE rank <= 5;6. Необходимо вывести уроки и даты их прохождения по пользователю 145768, отсортировав от первого пройденного урока к последнему, по профессии project-manager
Вообще кажется, что в БД должно быть больше полей. Для решения этой задачи таблица profession_dop должна содержать поле с id курса — profession_id. А база с уроками должна содержать id профессии, в которой был выполнен урок. Либо должна быть третья таблица для маппинга профессий и id уроков у них.
Если студент будет одновременно проходить два курса или начнет один, потом забросит его, потом начнет другой, а после этого вернется к первому — невозможно будет разделить уроки двух разных курсов.
Тем не менее, мой вариант решения. Находим пользователя и дату начала его обучения по профессии project-manager. Ищем все уроки этого пользователя, которые начались после его поступления на эту профессию. Сортируем в порядке возрастания даты:
WITH project_manager_user AS ( SELECT user_id, subscription_date FROM profession_dop WHERE profession_name = 'project-manager' AND user_id = 145768)SELECT fl.lesson_id, fl.date_createdFROM finished_lessons flINNER JOIN project_manager_user USING(user_id)WHERE date_created >= subscription_dateORDER BY fl.date_created ASC;Главный вывод: на собеседовании важно не только написать SQL-запрос, но и заметить ограничения схемы данных.
Иногда правильный ответ – это не просто код, а уточнение бизнес-логики и объяснение, почему задача в текущем виде решается неоднозначно.
Готовишься к собеседованию на аналитика?
Посмотри базу реальных тестовых заданий и разборы кейсов.