diff --git a/dmedvedev/4.4.greenplum_external_tables.sql b/dmedvedev/4.4.greenplum_external_tables.sql new file mode 100644 index 0000000..02f964e --- /dev/null +++ b/dmedvedev/4.4.greenplum_external_tables.sql @@ -0,0 +1,50 @@ +## Задача 1: + +У тебя в Hive есть таблица `sales (id, amount)` + +Ты хочешь читать её из Greenplum, **не копируя**. + +CREATE EXTERNAL TABLE hive_sales ( + id INT, + amount NUMERIC +) +LOCATION ('pxf://hive.sales?PROFILE=hive') +FORMAT 'CUSTOM' (FORMATTER='pxfwritable_import'); + +Greenplum ↔ PXF ↔ Hive Metastore + │ + ▼ + HDFS файлы (ORC, Parquet...) + +### ❓ Вопросы: + +1. Где реально хранятся данные? + ЖАННЫЕ ХРАНЯТСЯ В ХРАНИЛИЩЕ HIVE. +2. Можно ли писать обратно в Hive? + ДА, КОНЕЧНО. +3. Что если структура таблицы в Hive изменилась? + В ТАКОМ СЛУЧАЕ ПРИ ЗАПРОСЕ МОГУТ БЫТЬ ОШИБКИ ИЛИ ПУСТЫЕ СТОЛБЦЫ СО ЗНАЧЕНИЯМИ, ТАК КАК GREENPLUM НИКАКИМ ОБРАЗОМ НЕ СМОЖЕТ УСЗНАТЬ КАКИЕ ИЗМЕНЕНИЯ ПРОИЗОШЛИ. ПОЭТОМУ НЕОБХОДИМО ПЕРЕСОЗДАТЬ ВНЕШНЮЮ ТАБЛИЦУ ПУТЕМ УДАЛЕНИЯ И СОЗДАНИЯ. + +## Задача 2: + +Есть файл `s3://my-data-bucket/orders.parquet`. Нужно читать его как таблицу. + +CREATE EXTERNAL TABLE s3_orders ( + order_id INT, + amount NUMERIC +) +LOCATION ('pxf://my-data-bucket/orders.parquet?PROFILE=s3') +FORMAT 'CUSTOM' (FORMATTER='parquet'); + +### 🔍 Визуализация: + +Greenplum ↔ PXF ↔ S3 + ▼ + Чтение .parquet + +### ❓ Вопросы: + +1. Данные копируются в Greenplum? + НЕТ, ДАННЫЕ НЕ КОПИРУЮТСЯ. +2. Что быстрее: EXTERNAL или `CREATE TABLE AS SELECT`? + CREATE TABLE AS SELECT БЫСТРЕЕ, НО МЕНЕЕ ЭФФЕКТИВНЕЕ В ДОЛГУЮ, ТАК КАК РАСХОДУЮТСЯ ОБЪЕМЫ ПАМЯТИ И ДОП МОЩНОСТИ. \ No newline at end of file diff --git a/dmedvedev/4.5.greenplum_pet_project.sql b/dmedvedev/4.5.greenplum_pet_project.sql new file mode 100644 index 0000000..5feb6a6 --- /dev/null +++ b/dmedvedev/4.5.greenplum_pet_project.sql @@ -0,0 +1,261 @@ +--Добавление raw-таблиц в GREENPLUM +CREATE TABLE raw.pet_users +( + id INT PRIMARY KEY NOT NULL, + name VARCHAR(50) NOT NULL, + age INT, + email VARCHAR(100) NOT NULL, + registration_date DATE NOT NULL +); + +CREATE TABLE raw.pet_courses +( + id INT PRIMARY KEY NOT NULL, + title VARCHAR(100) NOT NULL, + category VARCHAR(50) NOT NULL, + created_at DATE NOT NULL +); + +CREATE TABLE raw.pet_lessons +( + id INT PRIMARY KEY NOT NULL, + course_id INT NOT NULL, + FOREIGN KEY (course_id) REFERENCES raw.pet_courses (id), + title VARCHAR(50), + duration_min INT NOT NULL +); + +CREATE TABLE raw.pet_enrollments +( + id INT PRIMARY KEY NOT NULL, + user_id INT NOT NULL, + FOREIGN KEY (user_id) REFERENCES raw.pet_users (id), + course_id INT NOT NULL, + FOREIGN KEY (course_id) REFERENCES raw.pet_courses (id), + enrolled_at DATE NOT NULL +); + +CREATE TABLE raw.pet_lesson_views +( + id INT PRIMARY KEY NOT NULL, + user_id INT NOT NULL, + FOREIGN KEY (user_id) REFERENCES raw.pet_users (id), + lesson_id INT NOT NULL, + FOREIGN KEY(lesson_id) REFERENCES raw.pet_lessons (id), + viewed_at TIMESTAMPTZ NOT NULL +); + +--Создание core-таблиц (измерений - dimension) в GREENPLUM +-- Измерение пользователей +CREATE TABLE core.dim_user +( + user_id INT PRIMARY KEY, + name TEXT, + age INT, + email TEXT, + registration_date DATE +); + +-- Измерение курсов +CREATE TABLE core.dim_course +( + course_id INT PRIMARY KEY, + title TEXT, + category TEXT +); + +-- Измерение уроков +CREATE TABLE core.dim_lesson +( + lesson_id INT PRIMARY KEY, + title TEXT, + duration_min INT, + course_id INT REFERENCES core.dim_course(course_id) +); + +-- Опционально: измерение времени +CREATE TABLE core.dim_date +( + date DATE PRIMARY KEY, + year INT, + month INT, + day INT, + weekday TEXT +); + +--Создание core-таблиц (факты - fact table) в GREENPLUM + +CREATE TABLE core.fact_lesson_views +( + user_id INT REFERENCES core.dim_user(user_id), + lesson_id INT REFERENCES core.dim_lesson(lesson_id), + course_id INT REFERENCES core.dim_course(course_id), + viewed_at TIMESTAMP +); + +CREATE TABLE core.enrollments +( + user_id INT REFERENCES core.dim_user(user_id), + course_id INT REFERENCES core.dim_course(course_id), + enrolled_at TIMESTAMP +); + +--ETL переброска данных из raw в core в GREENPLUM +--переброска для dim_user +INSERT INTO core.dim_user + (user_id, name, age, email, registration_date) +SELECT DISTINCT + user_id::INT, + name::TEXT, + age::INT, + email::TEXT, + registration_date::DATE +FROM raw.users; + +--переброска для dim_course +INSERT INTO core.dim_course + (course_id, title, category) +SELECT DISTINCT + id::INT, + title::VARCHAR(100), + category::VARCHAR(50) +FROM raw.pet_courses; + +--переброска для dim_lesson +INSERT INTO core.dim_lesson + (lesson_id, title, duration_min, course_id) +SELECT DISTINCT + id::INT, + title::VARCHAR(50), + duration_min::INT, + course_id::INT +FROM raw.pet_lessons; + +--переброска для dim_date +--ЗДЕСЬ Я ВЫБРАЛ ПОДТЯГИВАНИЕ ДАТ ИЗ ТАБЛИЦЫ ПРОСМОТРОВ УРОКОВ +INSERT INTO core.dim_date + (date, YEAR, MONTH, DAY, weekday) +SELECT DISTINCT + viewed_at::date, + EXTRACT(YEAR FROM viewed_at), + EXTRACT(MONTH FROM viewed_at), + EXTRACT(DAY FROM viewed_at), + EXTRACT(ISODOW FROM viewed_at) +FROM raw.pet_lesson_views; + +--переброска для enrollments +INSERT INTO core.enrollments + (user_id, course_id, enrolled_at) +SELECT + user_id::INT, + course_id::INT, + enrolled_at::DATE +FROM raw.pet_enrollments; + +--переброска для fact_lesson_views +--ЗДЕСЬ ПО ИДЕЕ ДОЛЖЕН БЫЛ ПОДТЯНУТЬ АЙДИШНИК КУРСА, НО ЕГО НЕТ В ИСХОДНОЙ ТАБЛИЦЕ raw.pet_lesson_views, ПРАВИЛЬНО ЛИ Я СДЕЛАЛ, ЧТО ПРОСТО НЕ ДОБАВИЛ ЕГО В ЗАПРОС?????? +INSERT INTO core.fact_lesson_views + (user_id, lesson_id, viewed_at) +SELECT + user_id::INT, + lesson_id::INT, + viewed_at::TIMESTAMPTZ +FROM raw.pet_lesson_views; + +--ВИТРИНА dm.lesson_popularity_summary +CREATE MATERIALIZED VIEW dm.lesson_popularity_summary AS + +SELECT + flv.lesson_id AS lesson_id, + dl.title AS lesson_title, + dc.course_id AS course_id, + dc.title AS course_title, + COUNT(*) AS total_views, + COUNT(DISTINCT user_id) AS unique_users, + MIN(viewed_at) AS first_view, + MAX(viewed_at) AS last_view +FROM core.fact_lesson_views flv +LEFT JOIN core.dim_lesson dl ON flv.lesson_id = dl.lesson_id +LEFT JOIN core.dim_course dc ON dl.course_id = dc.course_id +GROUP BY flv.lesson_id, dl.title, dc.course_id, dc.title +ORDER BY lesson_id; + +--ВИТРИНА dm.inactive_users_summary +CREATE MATERIALIZED VIEW dm.inactive_users_summary AS +SELECT + du.user_id AS user_id, + du.name AS user_name, + du.email, + du.age, + COUNT(DISTINCT e.course_id) AS enrollments_count, + du.registration_date +FROM core.dim_user du +LEFT JOIN core.enrollments e ON du.user_id = e.user_id +LEFT JOIN core.fact_lesson_views flv ON du.user_id = flv.user_id +WHERE flv.user_id IS NULL +GROUP BY du.user_id, du.name, du.email, du.age, du.registration_date; + +--ВИТРИНА dm.course_completion_rate +CREATE MATERIALIZED VIEW dm.course_completion_rate AS +SELECT + du.user_id AS user_id, + du.name AS user_name, + e.course_id AS course_id, + ( + SELECT + dc.title + FROM core.dim_course dc + WHERE e.course_id = dc.course_id + ) AS course_title, + COUNT(DISTINCT dl.lesson_id) AS lessons_in_course, + COUNT(DISTINCT flv.lesson_id) AS lessons_viewed, + ROUND(COUNT(DISTINCT flv.lesson_id) * 1.0 / NULLIF(COUNT(dl.lesson_id), 0), 2) AS completion_rate +FROM core.dim_user du + +LEFT JOIN core.enrollments e ON du.user_id = e.user_id +LEFT JOIN core.dim_lesson dl ON e.course_id = dl.course_id +LEFT JOIN core.fact_lesson_views flv ON dl.lesson_id = flv.lesson_id AND du.user_id = flv.user_id +GROUP BY du.user_id, du.name, e.course_id, course_title; + + +--✅ Задание 10: Проверь витрины +ДАННЫЕ ВЫВОДЯТСЯ, ОНИ КОРРЕКТНЫ +SELECT * FROM dm.lesson_popularity_summary LIMIT 10; + +SELECT * FROM dm.lesson_popularity_summary LIMIT 10; +lesson_id lesson_title course_id course_title total_views unique_users first_view last_view +2 JOIN 1 SQL для начинающих 6 6 2023-01-07 10:15:00.000 2023-02-26 11:20:00.000 +3 WHERE и фильтрация 1 SQL для начинающих 5 5 2023-01-08 09:00:00.000 2023-04-10 11:30:00.000 +6 Pandas основы 2 Python для анализа данных 6 6 2023-01-10 14:00:00.000 2023-03-12 14:00:00.000 +7 NumPy 2 Python для анализа данных 4 4 2023-01-11 10:00:00.000 2023-03-13 09:00:00.000 +9 Основы BI 3 BI с нуля 6 6 2023-01-10 08:00:00.000 2023-04-02 10:00:00.000 +14 Графики и диаграммы 4 Excel для аналитиков 1 1 2023-02-07 10:00:00.000 2023-02-07 10:00:00.000 +15 Введение в ML 5 Основы машинного обучения 1 1 2023-03-21 10:00:00.000 2023-03-21 10:00:00.000 +16 Линейная регрессия 5 Основы машинного обучения 1 1 2023-03-22 11:00:00.000 2023-03-22 11:00:00.000 +1 SELECT и FROM 1 SQL для начинающих 9 8 2023-01-07 10:00:00.000 2023-04-10 11:00:00.000 +4 Агрегации и GROUP BY 1 SQL для начинающих 5 3 2023-01-08 09:30:00.000 2023-01-18 10:00:00.000 + +SELECT * FROM dm.inactive_users_summary LIMIT 10; +user_id user_name email age enrollments_count registration_date +8 Henry henry@mail.com 32 3 2023-01-20 +16 Paul paul@mail.com 29 2 2023-02-15 +18 Rita rita@mail.com 27 2 2023-04-01 +17 Quinn quinn@gmail.com 24 2 2023-03-20 +20 Tina tina@mail.com 25 1 2023-04-18 +10 Jack jack@mail.com 27 1 2023-04-15 +19 Sam sam@gmail.com 30 2 2023-04-10 +11 Kelly kelly@gmail.com 23 1 2023-04-20 +15 Oscar oscar@gmail.com 33 3 2023-01-10 + +SELECT * FROM dm.course_completion_rate LIMIT 10; +user_id user_name course_id course_title lessons_is_course lesson_viewed completion_rate +2 Bob 1 SQL для начинающих 5 1 0.20 +2 Bob 3 BI с нуля 3 2 0.67 +3 Charlie 1 SQL для начинающих 5 2 0.40 +3 Charlie 2 Python для анализа данных 3 1 0.33 +4 Diana 2 Python для анализа данных 3 2 0.67 +4 Diana 3 BI с нуля 3 2 0.67 +6 Frank 1 SQL для начинающих 5 5 1.00 +6 Frank 2 Python для анализа данных 3 3 1.00 +6 Frank 3 BI с нуля 3 1 0.33 +6 Frank 4 Excel для аналитиков 3 3 1.00 \ No newline at end of file