144 lines
7.6 KiB
SQL
144 lines
7.6 KiB
SQL
-- Задача 1. Давность регистрации пользователей
|
||
|
||
SELECT name,
|
||
CAST(created_at AS DATE) AS created_date,
|
||
CURRENT_DATE - created_at::DATE AS days_since_registration,
|
||
is_active
|
||
FROM dmezentsev.users
|
||
WHERE CURRENT_DATE - created_at::DATE > 180
|
||
ORDER BY days_since_registration DESC;
|
||
|
||
/* - Как бы ты использовал такое поле в реальном продукте
|
||
(что можно делать с очень «старыми» пользователями)?
|
||
|
||
В реальности можно давать какие-то бонусы самым лояльным клиентам. Ну или
|
||
наоборот, удалить их, если зарегистрированы давно, а активности никакой
|
||
|
||
- Почему удобнее сразу вычитать created_at::date, а не created_at как timestamp?
|
||
|
||
Если вычитать ::date, то получается число, удобно сравнивать, применять при
|
||
фильтрации, в арифметических выражениях. Если timestamp, то интервал
|
||
*/
|
||
|
||
-- Задача 2. Нормализация email и доменов
|
||
|
||
SELECT name,
|
||
LOWER(email) AS email_clean,
|
||
SUBSTRING(LOWER(email) FROM POSITION('@' IN email) + 1) AS domain
|
||
FROM dmezentsev.users
|
||
WHERE email IS NOT NULL
|
||
ORDER BY domain, name;
|
||
|
||
/* - Зачем в реальных системах приводят email к нижнему регистру и выделяют домен отдельно?
|
||
|
||
email - адреса приводят к нижнему регистру, потому что в БД один и тот же
|
||
почтовый ящик в разном регистре будет считаться двумя почтовыми ящиками, что
|
||
приведет к дублям. Так же если пользователь введет свой почтовый ящик не в таком
|
||
регистре, в котором ящик сохранен в БД, то он не сможет залогиниться
|
||
Домен выделяют отдельно для анализа, маркетинга. Так же ради безопасности,
|
||
чтобы блочить подозрительные домены
|
||
*/
|
||
|
||
-- Задача 3. Сегменты по возрасту
|
||
|
||
SELECT name,
|
||
age,
|
||
CASE
|
||
WHEN age < 25 THEN 'young'
|
||
WHEN age BETWEEN 25 AND 35 THEN 'adult'
|
||
ELSE 'senior'
|
||
END AS age_group
|
||
FROM dmezentsev.users
|
||
ORDER BY age;
|
||
|
||
/* - Почему удобнее держать логику сегментации в SQL (CASE), а не только в коде приложения?
|
||
|
||
Вот тут точно не знаю, но мне кажется, что сегментация в SQL вернет результат.
|
||
А если писать это в коде приложения, то выгрузка всех строк выльется в большой
|
||
объем данных, которые нужно будет еще и обработать
|
||
|
||
- Что произойдёт, если в CASE забыть ветку ELSE
|
||
(что получат пользователи, не попавшие ни в одно условие)?
|
||
|
||
Если забыть ELSE, то пользователи, которые не попадут ни под одно из условий,
|
||
получат NULL
|
||
*/
|
||
|
||
-- Задача 4. «Горячие» пользователи по дате и активности
|
||
|
||
SELECT
|
||
name,
|
||
created_at::DATE AS created_at_date,
|
||
CURRENT_DATE - created_at::DATE AS days_since_registration,
|
||
CASE
|
||
WHEN is_active AND (CURRENT_DATE - created_at::DATE <= 30) THEN 'hot'
|
||
ELSE 'regular'
|
||
END AS hot_flag
|
||
FROM dmezentsev.users
|
||
WHERE is_active AND (CURRENT_DATE - created_at::DATE <= 30);
|
||
|
||
/* - Чем отличается фильтрация по дате в WHERE от простого CASE с меткой 'hot',
|
||
но без фильтрации?
|
||
|
||
С фильтрацией в WHERE получим горячих с датой регистрации не больше 30 дней.
|
||
Если без WHERE, то получим всех пользователей (и горячих, и обычных)
|
||
|
||
- Какие типы рассылок/кампаний логично запускать только на «горячих»?
|
||
|
||
Предположу, что горячим можно рассылать специальные предложения, чтобы
|
||
дополнительно завлечь. Так же можно сделать рассылку по онбордингу, чтобы
|
||
легче вкатиться в фичи сервиса
|
||
*/
|
||
|
||
-- Задача 5. Качество данных по email
|
||
|
||
SELECT COUNT(*) AS total_users,
|
||
COUNT(email) AS with_email,
|
||
COUNT(*) FILTER (WHERE email IS NULL) AS without_email,
|
||
COUNT(*) FILTER (WHERE email IS NULL)::NUMERIC
|
||
/ COUNT(*) * 100 AS without_email_percent
|
||
FROM dmezentsev.users;
|
||
|
||
/* - Почему для проверки «долей» и «заполненности» полей
|
||
лучше использовать агрегаты в SQL, а не просто «на глаз» смотреть на таблицу?
|
||
|
||
Агрегаты гораздо легче воспринимать на глаз, чем смотреть всю таблицу.
|
||
По сути, это уже готовый ответ на поставленный вопрос. Другое дело
|
||
пытаться понять по всем данным, что вообще происходит.
|
||
Особенно, если там миллионы строк.
|
||
|
||
- Какие действия по данным ты бы предложил продукту,
|
||
если доля пользователей без email слишком большая?
|
||
|
||
Если бы доля юзеров, не заполняюших email, оказалась слишком большой, то я
|
||
бы советовал сделать ввод email обязательным, ну или использовать email как
|
||
логин. Как вариант, предложить какой-то бонус типа промокода
|
||
*/
|
||
|
||
-- Задача 6. «Стаж» пользователя и его группа
|
||
|
||
SELECT name,
|
||
CASE
|
||
WHEN CURRENT_DATE - created_AT::DATE <= 30 THEN 'new'
|
||
WHEN CURRENT_DATE - created_AT::DATE BETWEEN 31 AND 180 THEN 'middle'
|
||
ELSE 'old'
|
||
END AS tenure_group,
|
||
CURRENT_DATE - created_AT::DATE AS days_since_registration,
|
||
is_active
|
||
FROM dmezentsev.users
|
||
ORDER BY tenure_group,
|
||
days_since_registration;
|
||
|
||
/* Чем сегментация по «стажу» отличается по смыслу от сегментации по возрасту?
|
||
|
||
Стаж - это время с момента регистрации. Думаю, такая сегментация необходима для того, чтобы
|
||
по этим сегментам проводить какую-то свою аналитику, отличную для каждого из них.
|
||
Например: стаж - аналитика лояльности, возраст - аналитика платежеспособности
|
||
|
||
- Если пересчитывать tenure_group прямо в SQL каждый день,
|
||
какие плюсы и минусы по сравнению с хранением статического поля segment?
|
||
|
||
Плюс перерасчета стажа каждый день - это актуальность данных, которые потом
|
||
будут использоваться аналитиками
|
||
*/
|
||
|