Программы
Подготовка к собеседованиямA/B-тесты для аналитиковML и Causal InferenceA/B-тесты для менеджеровОбучение для команд
Бесплатные материалы
База знаний185+ тестовых заданийRoadmap по A/BЧеклист A/B-тестаОтзывыО проектеОставить заявку
Собеседования и тестовые задания

7 SQL задач, которые задают на собеседовании в телеком-компании – Часть 2

12 сентября 2024·4 мин чтения·Павел Бухтик·Оригинал в Telegram ↗

Продолжаем разминать мозги и дорешиваем оставшиеся 3 из 7 задач на SQL. Первую часть разбора вы можете найти по ссылке.

1. Существует таблица tbl_terminal_device связи мобильного номера и устройства imei.

sql
select * from tbl_terminal_device
mob_numimeicreated_at
79000000008683590226802021-04-13
79100000003554280717792021-04-14
79200000003535820374932021-04-15
79900000003554280717792021-04-13
79100000003535820374932021-04-22

При каждой замене устройства абонентом фиксируется новая дата создания связки номера с этим устройством. На текущий момент актуальна одна последняя связка с последним устройством, т.е. последняя по дате создания created_dt.

По первым 8 символам в идентификаторе устройства imei можно определить модель устройства tac.

Необходимо отобрать всех абонентов, у которых последнее устройство Samsung, т.е. tac = 12345678.

Воспользуемся CTE с оконной функцией ROW_NUMBER() с партицированием по mob_num абонента и сортировкой по убыванию даты:

sql
WITH devices AS (    SELECT        mob_num,        imei,        created_dt,        ROW_NUMBER() OVER (            PARTITION BY mob_num            ORDER BY created_dt DESC        ) AS rn    FROM        tbl_terminal_device)

Таким образом мы сможем впоследствии без труда извлекать последнюю дату и устройство для каждого абонента.

После, в основном запросе оставим только последнюю актуальную запись для каждого абонента (rn = 1) и воспользуемся функцией SUBSTRING, чтобы извлечь первые 8 символов IMEI и отфильтровать по ним:

sql
WITH devices AS (    ...)SELECT    mob_num,    imei,    created_dtFROM    devicesWHERE rn = 1    AND SUBSTRING(imei, 1, 8) = '12345678'

Готово: отобрали всех абонентов, у которых последнее устройство — Samsung.

2. Существует таблица EXAM_RESULTS с результатами последней завершенной сессии.

sql
select * from EXAM_RESULTS
studentsubjecttest_dtscoredone
77529матан18.06.20225[NULL]
77529черчение11.06.20222[NULL]
77529черчение17.06.20225[NULL]
77529англ.яз16.06.20225[NULL]
77529культуролог09.06.2022[NULL]"+"
77529ОБЖ19.06.2022[NULL]"-"

Необходимо найти студентов, которые получили на всех зачетах + и 5 на всех экзаменах.

Для этой задачи явно требуется задать уточняющие вопросы интервьюеру. Мы же будем исходить из следующих условий:

  • Актуальной оценкой по предмету считается наивысшая.
  • У каждого студента может быть свое количество дисциплин.

Через CTE для каждого студента найдем общее количество дисциплин и сколько дисциплин было сдано на 5 или с зачетом:

sql
WITH students AS (    SELECT        student,        count(DISTINCT subject) as subjects,        count(CASE            WHEN score = 5 or done = '+'            THEN subject            ELSE NULL        END) AS subjects_passed    GROUP BY student)

Далее выберем тех студентов, у которых количество сданных на 5 и зачтенных предметов равно количеству доступных для студента дисциплин.

Итоговое решение выглядит так:

sql
WITH students AS (    SELECT        student,        count(DISTINCT subject) as subjects,        count(CASE            WHEN score = 5 or done = '+'            THEN subject            ELSE NULL        END) AS subjects_passed    GROUP BY student)SELECT studentFROM studentsWHERE subjects = subjects_passed

3. Существует таблица абонентов appn_history с историей изменений. Каждая запись по абоненту действует в пределах периода с valid_from_dt по valid_to_dt. По каждому абоненту, в момент времени, должна быть только одна действующая запись.

sql
select * from appn_history
appnvalid_from_dtvalid_to_dt
0abnt 12020-01-032020-02-20
1abnt 12020-02-212020-03-10
2abnt 12020-03-082020-05-15
3abnt 12020-05-162020-07-26
4abnt 22019-02-102020-04-12
5abnt 22019-02-102020-04-12

Необходимо написать запрос, отбирающий записи, которые нарушают условие хранения данных в таблице.

appnvalid_from_dtvalid_to_dt
0abnt 12020-02-212020-03-10
1abnt 22019-02-102020-04-12

Эту задачу можно решить огромным количеством способов с разной степенью оптимальности.

Предлагаю следующее решение:

Для каждого пользователя пронумеруем каждый период при помощи функции ROW_NUMBER():

sql
WITH sorted_history AS (    SELECT        appn,        valid_from_dt,        valid_to_dt,        ROW_NUMBER() OVER (            PARTITION BY appn            ORDER BY valid_from_dt        ) AS rn    FROM appn_history)

Для каждого абонента сравним каждую запись с предыдущей при помощи SELF JOIN и отфильтруем пересечения:

sql
WITH sorted_history AS (    ...)SELECT    t1.appn,    t1.valid_from_dt,    t1.valid_to_dtFROM sorted_history t1INNER JOIN sorted_history t2ON t1.appn = t2.appnAND t1.rn = t2.rn - 1AND t1.valid_to_dt >= t2.valid_from_dtAND t1.valid_from_dt <= t2.valid_to_dt

Готовишься к собеседованию на аналитика?

Посмотри базу реальных тестовых заданий и разборы кейсов.

Перейти к тестовым заданиям