fix mistakes
This commit is contained in:
@@ -51,7 +51,7 @@ SELECT id, name, age
|
|||||||
FROM users
|
FROM users
|
||||||
WHERE
|
WHERE
|
||||||
created_at >= '2026-01-01' AND created_at < '2027-01-01'
|
created_at >= '2026-01-01' AND created_at < '2027-01-01'
|
||||||
AND country = 'россия'
|
AND country IN ('россия', 'Россия', 'РОССИЯ')
|
||||||
AND age > 25;
|
AND age > 25;
|
||||||
|
|
||||||
Исправлено: выдача колонок "все" заменено на "определенные", логика в where с числами переработана, соответствие определенному году переработано и убрал функцию LOWER.
|
Исправлено: выдача колонок "все" заменено на "определенные", логика в where с числами переработана, соответствие определенному году переработано и убрал функцию LOWER.
|
||||||
|
|||||||
@@ -69,8 +69,7 @@ WHERE id = 1;
|
|||||||
--Думаю, потому что также используется функция LOWER внутри WHERE
|
--Думаю, потому что также используется функция LOWER внутри WHERE
|
||||||
|
|
||||||
4. Как исправить запрос, чтобы индекс заработал?
|
4. Как исправить запрос, чтобы индекс заработал?
|
||||||
CREATE INDEX idx_email ON users(email);
|
CREATE INDEX idx_email ON users(LOWER(email))
|
||||||
--я добавил индекс, но все равно EXPLAIN ANALYZE показывается Seq Scan. В чем проблема?
|
|
||||||
----------
|
----------
|
||||||
|
|
||||||
--Задача 7 — Функции убивают индексы
|
--Задача 7 — Функции убивают индексы
|
||||||
|
|||||||
@@ -262,7 +262,7 @@
|
|||||||
**❓ Вопросы:**
|
**❓ Вопросы:**
|
||||||
|
|
||||||
1. Когда выполняется подзапрос в `SELECT`?
|
1. Когда выполняется подзапрос в `SELECT`?
|
||||||
В самом начале.
|
Подзапрос в SELECT вычисляется после того, как отработали секции FROM и WHERE
|
||||||
|
|
||||||
2. Можно ли использовать алиас `orders_count` в `WHERE`?
|
2. Можно ли использовать алиас `orders_count` в `WHERE`?
|
||||||
Нет, потому что WHERE не видит алиасы
|
Нет, потому что WHERE не видит алиасы
|
||||||
@@ -270,14 +270,15 @@
|
|||||||
3. Можно ли использовать его в `ORDER BY`?
|
3. Можно ли использовать его в `ORDER BY`?
|
||||||
Да, потому что order by выполняется после SELECT
|
Да, потому что order by выполняется после SELECT
|
||||||
|
|
||||||
4. Перепиши этот запрос через `JOIN`. Какой вариант быстрее? Проверь с `EXPLAIN ANALYZE`! 0.065-0.15
|
4. Перепиши этот запрос через `JOIN`. Какой вариант быстрее? Проверь с `EXPLAIN ANALYZE`!
|
||||||
SELECT
|
SELECT
|
||||||
u.name,
|
u.name,
|
||||||
u.age,
|
u.age,
|
||||||
COUNT(o.id) AS orders_count
|
COUNT(o.id) AS orders_count
|
||||||
FROM users u
|
FROM users u
|
||||||
LEFT JOIN orders o ON o.user_id = u.id
|
LEFT JOIN orders o ON o.user_id = u.id
|
||||||
|
WHERE u.country = 'Россия'
|
||||||
GROUP BY u.name, u.age
|
GROUP BY u.name, u.age
|
||||||
ORDER BY orders_count DESC;
|
ORDER BY orders_count DESC;
|
||||||
|
|
||||||
Второй запрос через JOIN выполняется дольше (0.06 и 0.08 мс соответственно)
|
Второй запрос через JOIN выполняется быстрее (0.13 и 0.1 мс соответственно)
|
||||||
@@ -2,36 +2,32 @@ CREATE MATERIALIZED
|
|||||||
VIEW user_cohort_analysis AS
|
VIEW user_cohort_analysis AS
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
TO_CHAR(pu.cohort_month, 'YYYY-MM') AS MONTH,
|
TO_CHAR(pu.cohort_month, 'YYYY-MM') AS month,
|
||||||
COUNT(DISTINCT pu.id) AS users_in_cohort,
|
COUNT(DISTINCT pu.id) AS users_in_cohort,
|
||||||
COUNT(DISTINCT pe.user_id) AS enrolled_users,
|
COUNT(DISTINCT pe.user_id) AS enrolled_users,
|
||||||
COUNT(DISTINCT plv.user_id) AS active_users,
|
COUNT(DISTINCT plv.user_id) AS active_users,
|
||||||
--ROUND(COUNT(DISTINCT plv.user_id) * 100.0 / COUNT(DISTINCT pu.id), 2) || '%' AS retention_rate,
|
ROUND(COUNT(DISTINCT plv.user_id) * 100.0 / COUNT(DISTINCT pu.id), 2) || '%' AS retention_rate,
|
||||||
ROUND(COUNT(DISTINCT pe.user_id) * 100.0 / COUNT(DISTINCT pu.id), 2) || '%' AS retention_rate,
|
ROUND(COUNT(DISTINCT pe.id) * 1.0 / COUNT(DISTINCT pu.id), 2) AS avg_courses_per_user,
|
||||||
ROUND(AVG((SELECT COUNT(pe.id)
|
(
|
||||||
FROM pet_enrollments pe)) / COUNT(DISTINCT pu.id), 2) AS avg_courses_per_user,
|
SELECT
|
||||||
(SELECT
|
|
||||||
ROUND(AVG(table_first_viewed_at.first_viewed_at::date - pu2.registration_date::date), 2)
|
ROUND(AVG(table_first_viewed_at.first_viewed_at::date - pu2.registration_date::date), 2)
|
||||||
FROM pet_users pu2
|
FROM pet_users pu2
|
||||||
LEFT JOIN (
|
JOIN (
|
||||||
SELECT
|
SELECT
|
||||||
plv2.user_id,
|
plv2.user_id,
|
||||||
MIN(plv2.viewed_at) AS first_viewed_at
|
MIN(plv2.viewed_at) AS first_viewed_at
|
||||||
FROM pet_lesson_views plv2
|
FROM pet_lesson_views plv2
|
||||||
GROUP BY plv2.user_id
|
GROUP BY plv2.user_id
|
||||||
) AS table_first_viewed_at
|
) AS table_first_viewed_at ON pu2.id = table_first_viewed_at.user_id
|
||||||
|
|
||||||
ON pu2.id = table_first_viewed_at.user_id
|
|
||||||
|
|
||||||
WHERE DATE_TRUNC('month', pu2.registration_date) = pu.cohort_month
|
WHERE DATE_TRUNC('month', pu2.registration_date) = pu.cohort_month
|
||||||
) AS avg_time_to_first_view
|
) AS avg_time_to_first_view
|
||||||
|
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
*,
|
*,
|
||||||
DATE_TRUNC('month', registration_date) AS cohort_month
|
DATE_TRUNC('month', registration_date) AS cohort_month
|
||||||
FROM pet_users
|
FROM pet_users
|
||||||
) AS pu
|
) AS pu
|
||||||
LEFT JOIN pet_enrollments pe ON pu.id = pe.user_id
|
LEFT JOIN pet_enrollments pe ON pu.id = pe.user_id
|
||||||
LEFT JOIN pet_lesson_views plv ON pu.id = plv.user_id
|
LEFT JOIN pet_lesson_views plv ON pu.id = plv.user_id
|
||||||
|
|
||||||
@@ -39,3 +35,4 @@ GROUP BY pu.cohort_month
|
|||||||
ORDER BY pu.cohort_month;
|
ORDER BY pu.cohort_month;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
-- 1. Задание 1: Анализ drop-off точек
|
-- 1. Задание 1: Анализ drop-off точек
|
||||||
-- Используя conversion_funnel, найди курсы с самым большим процентом отвала между этапами enrolled → started (WHERE stage_name IN (...))
|
-- Используя conversion_funnel, найди курсы с самым большим процентом отвала между этапами enrolled → started (WHERE stage_name IN (...))
|
||||||
WITH
|
WITH drop_off AS (
|
||||||
drop_off
|
SELECT
|
||||||
AS
|
course_id,
|
||||||
(
|
stage_name,
|
||||||
SELECT
|
users_count,
|
||||||
course_id,
|
LAG(users_count) OVER (
|
||||||
stage_name,
|
PARTITION BY course_id
|
||||||
users_count,
|
ORDER BY stage_name
|
||||||
LAG(users_count) OVER (PARTITION BY course_id) AS prev
|
) AS prev
|
||||||
|
FROM conversion_funnel
|
||||||
FROM conversion_funnel
|
WHERE stage_name IN ('enrolled', 'started')
|
||||||
)
|
)
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
course_id,
|
course_id,
|
||||||
ROUND(1 - users_count * 1.0 / NULLIF(prev, 0), 2) * 100 || '%' AS percent_of_drop
|
ROUND((1 - users_count * 1.0 / NULLIF(prev, 0)) * 100, 2) || '%' AS percent_of_drop
|
||||||
FROM drop_off
|
FROM drop_off
|
||||||
WHERE stage_name = 'started'
|
WHERE stage_name = 'started'
|
||||||
ORDER BY percent_of_drop DESC
|
ORDER BY (1 - users_count * 1.0 / NULLIF(prev, 0)) DESC;
|
||||||
|
|
||||||
|
|
||||||
-- ОТВЕТ: Самые большие проценты отвала между enrolled и started замечены на курсе 1 и 3 (62 и 67% соответственно).
|
-- ОТВЕТ: Самые большие проценты отвала между enrolled и started замечены на курсе 1 и 3 (62 и 67% соответственно).
|
||||||
|
|
||||||
|
|||||||
@@ -199,4 +199,5 @@
|
|||||||
**Ответ — текстом:**
|
**Ответ — текстом:**
|
||||||
В чём выгода индекса и где он не поможет
|
В чём выгода индекса и где он не поможет
|
||||||
|
|
||||||
Индекс помогает искать данные быстрее (индекс это как содержание в книге). Индекс не поможет в тех столбцах, где все данные в столбце различаются, например email.
|
Индекс помогает искать данные быстрее (индекс это как содержание в книге) за счет плана в виде дерева.
|
||||||
|
Индекс не поможет в тех столбцах, где уникальность данных мала, а также где есть сортировка по знакам >, < или поиск по части строки = "%abc".
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
- по дням.
|
- по дням.
|
||||||
2. Для каждого варианта напиши:
|
2. Для каждого варианта напиши:
|
||||||
- примерно сколько будет партиций за 10 лет;
|
- примерно сколько будет партиций за 10 лет;
|
||||||
по годам - 10 * 1 = 10, по месяцам - 10 * 12 = 120, по дням 366 * 2 + 365 * 4 = 2652.
|
по годам - 10 * 1 = 10, по месяцам - 10 * 12 = 120, по дням 366 * 2 + 365 * 8 = 3652.
|
||||||
|
|
||||||
- плюсы и минусы (удаление старых данных, поддержка, скорость запросов).
|
- плюсы и минусы (удаление старых данных, поддержка, скорость запросов).
|
||||||
|
|
||||||
|
|||||||
+9
-5
@@ -1,4 +1,5 @@
|
|||||||
CREATE TABLE users (
|
CREATE TABLE users
|
||||||
|
(
|
||||||
id INT PRIMARY KEY NOT NULL,
|
id INT PRIMARY KEY NOT NULL,
|
||||||
name VARCHAR(100) NOT NULL,
|
name VARCHAR(100) NOT NULL,
|
||||||
age INT,
|
age INT,
|
||||||
@@ -8,7 +9,8 @@ CREATE TABLE users (
|
|||||||
created_at TIMESTAMPTZ NOT NULL
|
created_at TIMESTAMPTZ NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE products (
|
CREATE TABLE products
|
||||||
|
(
|
||||||
id INT PRIMARY KEY NOT NULL,
|
id INT PRIMARY KEY NOT NULL,
|
||||||
name VARCHAR(100) NOT NULL,
|
name VARCHAR(100) NOT NULL,
|
||||||
category VARCHAR(50),
|
category VARCHAR(50),
|
||||||
@@ -16,15 +18,17 @@ CREATE TABLE products (
|
|||||||
in_stock BOOLEAN
|
in_stock BOOLEAN
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE orders (
|
CREATE TABLE orders
|
||||||
id INT PRIMARY KEY NOT NULL,
|
(
|
||||||
|
id SERIAL PRIMARY KEY NOT NULL,
|
||||||
user_id INT,
|
user_id INT,
|
||||||
order_date TIMESTAMPTZ NOT NULL,
|
order_date TIMESTAMPTZ NOT NULL,
|
||||||
status VARCHAR(20),
|
status VARCHAR(20),
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE order_items (
|
CREATE TABLE order_items
|
||||||
|
(
|
||||||
order_id INT NOT NULL,
|
order_id INT NOT NULL,
|
||||||
product_id INT NOT NULL,
|
product_id INT NOT NULL,
|
||||||
quantity INT NOT NULL,
|
quantity INT NOT NULL,
|
||||||
|
|||||||
Reference in New Issue
Block a user