Merge pull request 'Решение по базовый SQL' (#39) from ebelykh into main
Reviewed-on: https://gitea.de-infra.servehttp.com/de-infrastructure/DWH/pulls/39
@@ -0,0 +1,20 @@
|
|||||||
|
--Задача 1068
|
||||||
|
SELECT p.product_name, s.year, s.price
|
||||||
|
FROM Sales s
|
||||||
|
JOIN Product p
|
||||||
|
ON s.product_id = p.product_id;
|
||||||
|
|
||||||
|
--Задача 1378
|
||||||
|
SELECT EmployeeUNI.unique_id,
|
||||||
|
Employees.name
|
||||||
|
FROM Employees
|
||||||
|
LEFT JOIN EmployeeUNI
|
||||||
|
ON Employees.id = EmployeeUNI.id;
|
||||||
|
|
||||||
|
--Задача 577
|
||||||
|
SELECT e.name, b.bonus
|
||||||
|
FROM Employee e
|
||||||
|
LEFT JOIN Bonus b
|
||||||
|
ON e.empId = b.empId
|
||||||
|
WHERE b.bonus < 1000
|
||||||
|
OR b.bonus IS NULL;
|
||||||
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 104 KiB |
@@ -0,0 +1,233 @@
|
|||||||
|
CREATE TABLE users (
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
name TEXT,
|
||||||
|
age INT,
|
||||||
|
email TEXT,
|
||||||
|
registration_date DATE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO users VALUES
|
||||||
|
(1, 'Alice', 25, 'alice@mail.com', '2023-01-10'),
|
||||||
|
(2, 'Bob', 30, 'bob@gmail.com', '2023-02-05'),
|
||||||
|
(3, 'Charlie', 22, 'charlie@mail.com', '2023-02-20'),
|
||||||
|
(4, 'Diana', 28, 'diana@mail.com', '2023-03-01'),
|
||||||
|
(5, 'Ethan', 35, 'ethan@gmail.com', '2023-03-10');
|
||||||
|
|
||||||
|
SELECT * FROM users ORDER BY id;
|
||||||
|
|
||||||
|
CREATE TABLE courses (
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
title TEXT,
|
||||||
|
category TEXT,
|
||||||
|
created_at DATE
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO courses VALUES
|
||||||
|
(1, 'SQL для начинающих', 'data', '2023-01-01'),
|
||||||
|
(2, 'Python для анализа данных', 'programming', '2023-01-15'),
|
||||||
|
(3, 'BI с нуля', 'business', '2023-03-01');
|
||||||
|
|
||||||
|
|
||||||
|
SELECT * FROM courses ORDER BY id;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE lessons (
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
course_id INT,
|
||||||
|
title TEXT,
|
||||||
|
duration_min INT,
|
||||||
|
FOREIGN KEY (course_id) REFERENCES courses(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO lessons VALUES
|
||||||
|
(1, 1, 'SELECT и FROM', 10),
|
||||||
|
(2, 1, 'JOIN', 15),
|
||||||
|
(3, 2, 'Pandas', 20),
|
||||||
|
(4, 3, 'Основы BI', 12),
|
||||||
|
(5, 3, 'Метрики и дашборды', 18);
|
||||||
|
|
||||||
|
SELECT * FROM lessons ORDER BY id;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE enrollments (
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
user_id INT,
|
||||||
|
course_id INT,
|
||||||
|
enrolled_at DATE,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||||
|
FOREIGN KEY (course_id) REFERENCES courses(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO enrollments VALUES
|
||||||
|
(1, 1, 1, '2023-01-15'),
|
||||||
|
(2, 1, 2, '2023-02-01'),
|
||||||
|
(3, 2, 1, '2023-01-20'),
|
||||||
|
(4, 3, 2, '2023-03-05'),
|
||||||
|
(5, 4, 3, '2023-04-01');
|
||||||
|
|
||||||
|
SELECT * FROM enrollments ORDER BY id;
|
||||||
|
|
||||||
|
CREATE TABLE lesson_views (
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
user_id INT,
|
||||||
|
lesson_id INT,
|
||||||
|
viewed_at TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||||
|
FOREIGN KEY (lesson_id) REFERENCES lessons(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO lesson_views VALUES
|
||||||
|
(1, 1, 1, '2023-01-16 10:00:00'),
|
||||||
|
(2, 1, 2, '2023-01-16 10:15:00'),
|
||||||
|
(3, 2, 1, '2023-01-21 09:00:00'),
|
||||||
|
(4, 3, 3, '2023-03-06 12:00:00'),
|
||||||
|
(5, 4, 4, '2023-04-02 10:00:00'),
|
||||||
|
(6, 4, 5, '2023-04-03 10:00:00');
|
||||||
|
|
||||||
|
SELECT * FROM lesson_views ORDER BY id;
|
||||||
|
|
||||||
|
--Создаем materialized view c названием user_activity_summary
|
||||||
|
|
||||||
|
DROP MATERIALIZED VIEW IF EXISTS user_activity_summary;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW user_activity_summary AS
|
||||||
|
WITH
|
||||||
|
enr AS (
|
||||||
|
SELECT
|
||||||
|
user_id,
|
||||||
|
COUNT(*) AS enrollment_count
|
||||||
|
FROM enrollments
|
||||||
|
GROUP BY user_id
|
||||||
|
),
|
||||||
|
views AS (
|
||||||
|
SELECT
|
||||||
|
user_id,
|
||||||
|
COUNT(DISTINCT lesson_id) AS viewed_lessons_count,
|
||||||
|
MIN(viewed_at) AS first_view,
|
||||||
|
MAX(viewed_at) AS last_view
|
||||||
|
FROM lesson_views
|
||||||
|
GROUP BY user_id
|
||||||
|
),
|
||||||
|
possible AS (
|
||||||
|
SELECT
|
||||||
|
e.user_id,
|
||||||
|
COUNT(l.id) AS possible_lessons
|
||||||
|
FROM enrollments e
|
||||||
|
JOIN lessons l ON l.course_id = e.course_id
|
||||||
|
GROUP BY e.user_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
u.id AS user_id,
|
||||||
|
u.name,
|
||||||
|
enr.enrollment_count,
|
||||||
|
COALESCE(views.viewed_lessons_count, 0) AS viewed_lessons_count,
|
||||||
|
views.last_view,
|
||||||
|
views.first_view,
|
||||||
|
ROUND(
|
||||||
|
COALESCE(views.viewed_lessons_count, 0)::numeric
|
||||||
|
/ possible.possible_lessons * 100,
|
||||||
|
2
|
||||||
|
) || '%' AS engagement_rate
|
||||||
|
FROM users u
|
||||||
|
JOIN enr ON enr.user_id = u.id
|
||||||
|
JOIN possible ON possible.user_id = u.id
|
||||||
|
LEFT JOIN views ON views.user_id = u.id
|
||||||
|
ORDER BY u.id;
|
||||||
|
|
||||||
|
--Проверка
|
||||||
|
SELECT * FROM user_activity_summary ORDER BY user_id;
|
||||||
|
|
||||||
|
|
||||||
|
--Создаем materialized view c названием course_summary
|
||||||
|
|
||||||
|
DROP MATERIALIZED VIEW IF EXISTS course_summary;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW course_summary AS
|
||||||
|
WITH
|
||||||
|
-- 1) сколько уроков в каждом курсе
|
||||||
|
lessons_cnt AS (
|
||||||
|
SELECT
|
||||||
|
course_id,
|
||||||
|
COUNT(*) AS lessons_count
|
||||||
|
FROM lessons
|
||||||
|
GROUP BY course_id
|
||||||
|
),
|
||||||
|
|
||||||
|
-- 2) сколько уникальных пользователей записано на курс
|
||||||
|
users_cnt AS (
|
||||||
|
SELECT
|
||||||
|
course_id,
|
||||||
|
COUNT(DISTINCT user_id) AS unique_users
|
||||||
|
FROM enrollments
|
||||||
|
GROUP BY course_id
|
||||||
|
),
|
||||||
|
|
||||||
|
-- 3) просмотры по курсу (через lessons -> course_id)
|
||||||
|
views_cnt AS (
|
||||||
|
SELECT
|
||||||
|
l.course_id,
|
||||||
|
COUNT(lv.id) AS total_views,
|
||||||
|
MIN(lv.viewed_at) AS first_viewed_at,
|
||||||
|
MAX(lv.viewed_at) AS last_viewed_at
|
||||||
|
FROM lessons l
|
||||||
|
LEFT JOIN lesson_views lv ON lv.lesson_id = l.id
|
||||||
|
GROUP BY l.course_id
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
c.id AS course_id,
|
||||||
|
c.title AS course_title,
|
||||||
|
|
||||||
|
COALESCE(u.unique_users, 0) AS unique_users,
|
||||||
|
COALESCE(ls.lessons_count, 0) AS lessons_count,
|
||||||
|
COALESCE(v.total_views, 0) AS total_views,
|
||||||
|
|
||||||
|
ROUND(
|
||||||
|
COALESCE(v.total_views, 0)::numeric / NULLIF(COALESCE(u.unique_users, 0), 0),
|
||||||
|
2
|
||||||
|
) AS avg_views_per_user,
|
||||||
|
|
||||||
|
v.first_viewed_at,
|
||||||
|
v.last_viewed_at
|
||||||
|
FROM courses c
|
||||||
|
LEFT JOIN users_cnt u ON u.course_id = c.id
|
||||||
|
LEFT JOIN lessons_cnt ls ON ls.course_id = c.id
|
||||||
|
LEFT JOIN views_cnt v ON v.course_id = c.id
|
||||||
|
ORDER BY c.id;
|
||||||
|
|
||||||
|
SELECT * FROM course_summary ORDER BY course_id;
|
||||||
|
|
||||||
|
--Создаем materialized view c названием platform_summary
|
||||||
|
|
||||||
|
DROP MATERIALIZED VIEW IF EXISTS platform_summary;
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW platform_summary AS
|
||||||
|
SELECT
|
||||||
|
(SELECT COUNT(*) FROM users) AS total_users,
|
||||||
|
(SELECT COUNT(*) FROM courses) AS total_courses,
|
||||||
|
(SELECT COUNT(*) FROM lessons) AS total_lessons,
|
||||||
|
|
||||||
|
(SELECT COUNT(DISTINCT user_id) FROM lesson_views) AS users_with_views,
|
||||||
|
|
||||||
|
ROUND(
|
||||||
|
(SELECT COUNT(*) FROM lessons)::numeric
|
||||||
|
/ (SELECT COUNT(*) FROM courses),
|
||||||
|
2
|
||||||
|
) AS avg_lessons_per_course,
|
||||||
|
|
||||||
|
ROUND(
|
||||||
|
(SELECT COUNT(*) FROM lesson_views)::numeric
|
||||||
|
/ (SELECT COUNT(*) FROM lessons),
|
||||||
|
2
|
||||||
|
) AS avg_views_per_lesson;
|
||||||
|
|
||||||
|
SELECT * FROM platform_summary;
|
||||||
|
|
||||||
|
--Доп. задание: EXPLAIN
|
||||||
|
EXPLAIN SELECT * FROM user_activity_summary;
|
||||||
|
EXPLAIN SELECT * FROM course_summary;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 117 KiB |
@@ -0,0 +1,72 @@
|
|||||||
|
--Задача 1
|
||||||
|
В варианте А: WHERE фильтрует товары и убирает те, которые не в наличии, потом эти товары(строки)участвуют в группировке, в варианте В - HAVING фильтрует уже сгруппированные данные(категории)
|
||||||
|
|
||||||
|
--Задача 2356
|
||||||
|
SELECT teacher_id,
|
||||||
|
COUNT(DISTINCT subject_id) AS cnt
|
||||||
|
FROM teacher
|
||||||
|
GROUP BY teacher_id;
|
||||||
|
|
||||||
|
--Задача 1581
|
||||||
|
SELECT customer_id,
|
||||||
|
COUNT(v.visit_id) AS count_no_trans
|
||||||
|
FROM visits v
|
||||||
|
LEFT JOIN transactions t
|
||||||
|
ON v.visit_id = t.visit_id
|
||||||
|
WHERE t.transaction_id IS NULL
|
||||||
|
GROUP BY customer_id;
|
||||||
|
|
||||||
|
--Задача 1141
|
||||||
|
SELECT activity_date AS day,
|
||||||
|
COUNT(DISTINCT user_id) AS active_users
|
||||||
|
FROM Activity
|
||||||
|
WHERE activity_date BETWEEN '2019-06-28' AND '2019-07-27'
|
||||||
|
GROUP BY activity_date;
|
||||||
|
|
||||||
|
--Задача 596
|
||||||
|
SELECT class
|
||||||
|
FROM Courses
|
||||||
|
GROUP BY class
|
||||||
|
HAVING COUNT(student) >= 5;
|
||||||
|
|
||||||
|
--Задача 1075
|
||||||
|
SELECT project_id,
|
||||||
|
ROUND(AVG(experience_years), 2) AS average_years
|
||||||
|
FROM Project p
|
||||||
|
JOIN Employee e
|
||||||
|
ON p.employee_id = e.employee_id
|
||||||
|
GROUP BY project_id;
|
||||||
|
|
||||||
|
--Задача 1729
|
||||||
|
SELECT user_id,
|
||||||
|
COUNT (follower_id) AS followers_count
|
||||||
|
FROM followers
|
||||||
|
GROUP BY user_id
|
||||||
|
ORDER BY user_id;
|
||||||
|
|
||||||
|
--Задача 1633
|
||||||
|
SELECT
|
||||||
|
r.contest_id,
|
||||||
|
ROUND(
|
||||||
|
COUNT(r.user_id) * 100.0 / (SELECT COUNT(*) FROM Users),
|
||||||
|
2
|
||||||
|
) AS percentage
|
||||||
|
FROM Register r
|
||||||
|
GROUP BY r.contest_id
|
||||||
|
ORDER BY percentage DESC, r.contest_id ASC;
|
||||||
|
|
||||||
|
--Задача 1251
|
||||||
|
SELECT
|
||||||
|
p.product_id,
|
||||||
|
COALESCE(
|
||||||
|
ROUND(
|
||||||
|
SUM(u.units * p.price) * 1.0 / NULLIF(SUM(u.units), 0),
|
||||||
|
2
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS average_price
|
||||||
|
FROM Prices p
|
||||||
|
LEFT JOIN UnitsSold u
|
||||||
|
ON p.product_id = u.product_id
|
||||||
|
AND u.purchase_date BETWEEN p.start_date AND p.end_date
|
||||||
|
GROUP BY p.product_id;
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
--Задача 1757
|
||||||
|
SELECT product_id
|
||||||
|
FROM Products
|
||||||
|
WHERE low_fats = 'Y' AND recyclable = 'Y';
|
||||||
|
|
||||||
|
--Задача 584
|
||||||
|
SELECT name
|
||||||
|
FROM Customer
|
||||||
|
WHERE referee_id!= 2 OR referee_id IS NULL;
|
||||||
|
|
||||||
|
--Задача 595
|
||||||
|
SELECT name, population, area
|
||||||
|
FROM World
|
||||||
|
WHERE area >= 3000000
|
||||||
|
OR population >= 25000000;
|
||||||
|
|
||||||
|
--Задача 1148
|
||||||
|
SELECT DISTINCT author_id AS id
|
||||||
|
FROM Views
|
||||||
|
WHERE author_id = viewer_id
|
||||||
|
ORDER BY id;
|
||||||
|
|
||||||
|
--Задача 1683
|
||||||
|
SELECT tweet_id
|
||||||
|
FROM Tweets
|
||||||
|
WHERE CHAR_LENGTH(content) > 15;
|
||||||
|
|
||||||
|
--Задача 620
|
||||||
|
SELECT id, movie, description, rating
|
||||||
|
FROM Cinema
|
||||||
|
WHERE id % 2 = 1
|
||||||
|
AND description != 'boring'
|
||||||
|
ORDER BY rating DESC;
|
||||||
|
|
||||||
|
--Задача 1667
|
||||||
|
SELECT
|
||||||
|
user_id,
|
||||||
|
CONCAT(
|
||||||
|
UPPER(SUBSTR(name, 1, 1)),
|
||||||
|
LOWER(SUBSTR(name, 2))
|
||||||
|
) AS name
|
||||||
|
FROM Users
|
||||||
|
ORDER BY user_id;
|
||||||
|
After Width: | Height: | Size: 127 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 104 KiB |
@@ -0,0 +1,33 @@
|
|||||||
|
--Задача 1
|
||||||
|
CREATE TABLE products_extended (
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
category VARCHAR(50),
|
||||||
|
price DECIMAL(6,2),
|
||||||
|
in_stock BOOLEAN DEFAULT true,
|
||||||
|
email_support VARCHAR
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--Задача 2 Вариант с DEFAULT (in_stock не указываем)
|
||||||
|
INSERT INTO products_extended (id, name, category, price, email_support)
|
||||||
|
VALUES
|
||||||
|
(1, 'Mi Band 7', 'smartwatch', 49.00, 'miband@xiaomi.com'),
|
||||||
|
(2, 'Surface Pro', 'tablet', 1299.00, 'surface@microsoft.com');
|
||||||
|
|
||||||
|
--Задача 3
|
||||||
|
ALTER TABLE products_extended
|
||||||
|
ADD COLUMN rating DECIMAL(3,2),
|
||||||
|
DROP COLUMN email_support;
|
||||||
|
|
||||||
|
--Задача 4
|
||||||
|
UPDATE products_extended
|
||||||
|
SET price = price * 1.1
|
||||||
|
WHERE name = 'Mi Band 7';
|
||||||
|
|
||||||
|
--Задача 5(удаляет данные/таблицу)
|
||||||
|
TRUNCATE TABLE products_extended;
|
||||||
|
DROP TABLE products_extended;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
SELECT
|
||||||
|
name,
|
||||||
|
category,
|
||||||
|
price,
|
||||||
|
price * 1.10 AS price_with_markup,
|
||||||
|
price * 1.20 AS price_with_vat,
|
||||||
|
COALESCE(email_support, 'нет почты') AS status
|
||||||
|
FROM products_extended
|
||||||
|
WHERE in_stock = true
|
||||||
|
AND category NOT IN ('smartwatch', 'ebook_reader')
|
||||||
|
AND price BETWEEN 300 AND 1300;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
name,
|
||||||
|
category,
|
||||||
|
email_support,
|
||||||
|
price * 1.25 AS price_25,
|
||||||
|
price > 1000 AS premium
|
||||||
|
FROM products_extended
|
||||||
|
WHERE (price < 150 OR price > 1200)
|
||||||
|
AND email_support IS NOT NULL;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
category,
|
||||||
|
price
|
||||||
|
FROM products_extended
|
||||||
|
WHERE category IN ('smartphone', 'laptop', 'tablet')
|
||||||
|
AND name NOT LIKE '%i%'
|
||||||
|
AND price BETWEEN 500 AND 1300
|
||||||
|
ORDER BY id;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
name,
|
||||||
|
category,
|
||||||
|
price,
|
||||||
|
'нет поддержки' AS comment
|
||||||
|
FROM products_extended
|
||||||
|
WHERE in_stock = true
|
||||||
|
AND email_support IS NULL
|
||||||
|
AND price < 800;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
name,
|
||||||
|
category,
|
||||||
|
price,
|
||||||
|
price > 1000 AS premium
|
||||||
|
FROM products_extended
|
||||||
|
WHERE (name LIKE '%Pro' OR name LIKE '%X%')
|
||||||
|
AND in_stock = true
|
||||||
|
AND price > 700;
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
--Задача 1211
|
||||||
|
SELECT
|
||||||
|
query_name,
|
||||||
|
ROUND(AVG(rating * 1.0 / position), 2) AS quality,
|
||||||
|
ROUND(AVG(CASE WHEN rating < 3 THEN 1 ELSE 0 END) * 100,2) AS poor_query_percentage
|
||||||
|
FROM Queries
|
||||||
|
GROUP BY query_name;
|
||||||
|
|
||||||
|
--Задача 1193
|
||||||
|
SELECT
|
||||||
|
TO_CHAR(trans_date, 'YYYY-MM') AS month,
|
||||||
|
country,
|
||||||
|
COUNT(*) AS trans_count,
|
||||||
|
SUM(CASE WHEN state = 'approved' THEN 1 ELSE 0 END) AS approved_count,
|
||||||
|
SUM(amount) AS trans_total_amount,
|
||||||
|
SUM(CASE WHEN state = 'approved' THEN amount ELSE 0 END) AS approved_total_amount
|
||||||
|
FROM Transactions
|
||||||
|
GROUP BY
|
||||||
|
TO_CHAR(trans_date, 'YYYY-MM'),
|
||||||
|
country;
|
||||||