add postgresql homework
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
-- Задача 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 CURRENT_DATE - created_at::DATE 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 SUBSTRING(email FROM POSITION('@' IN email) + 1), 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?
|
||||
|
||||
Плюс перерасчета стажа каждый день - это актуальность данных, которые потом
|
||||
будут использоваться аналитиками
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user