Merge branch 'main' into vberetar_sql_union
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
CREATE TABLE users (
|
||||
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
age SMALLINT CHECK (age > 0),
|
||||
email VARCHAR(100) NOT NULL UNIQUE,
|
||||
country VARCHAR(50),
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
-- age - для возраста не нужны болшие числа
|
||||
-- country - можно было бы использовать VARCHAR(2), я бы тут спросил у тимлида как будет удобней использовать поле)
|
||||
-- created_at - нужно знать не только дату но и время создания/регистрации
|
||||
-- id - если не прописать not null в primary key, то not null ставится автоматически?
|
||||
|
||||
CREATE TABLE products (
|
||||
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
category VARCHAR(50),
|
||||
price DECIMAL(10, 2) NOT NULL,
|
||||
in_stock BOOLEAN DEFAULT true,
|
||||
);
|
||||
|
||||
CREATE TABLE orders(
|
||||
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
user_id INT,
|
||||
order_date TIMESTAMPTZ NOT NULL,
|
||||
status VARCHAR(20),
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
-- status - лучше прописать словами статус, так понятней будет. Я бы сюда поставил ENUM
|
||||
|
||||
CREATE TABLE order_items(
|
||||
order_id INT NOT NULL,
|
||||
product_id INT NOT NULL,
|
||||
quantity INT NOT NULL,
|
||||
price_at_purchase DECIMAL(10, 2) NOT NULL,
|
||||
PRIMARY KEY (order_id, product_id)
|
||||
);
|
||||
@@ -0,0 +1,41 @@
|
||||
INSERT INTO users (name, age, email, country, is_active, created_at)
|
||||
VALUES ('Alice', 25, LOWER('alice@example.com'), 'US', true, '2024-01-10 10:00:00'),
|
||||
('Bob', 30, LOWER('bob@example.com'), 'UK', true, '2024-02-05 09:30:00'),
|
||||
('Carol', 27, LOWER('carol@example.com'), 'US', false, '2024-03-01 15:20:00'),
|
||||
('Diana', 22, LOWER('diana@GMAIL.com'), 'US', true, '2024-01-12 08:15:00'),
|
||||
('Evan', 35, LOWER('EVAN@example.com'), 'UK', true, '2024-01-18 19:40:00'),
|
||||
('Farida', 29, LOWER('farida@example.com'), 'KZ', true, '2024-02-20 11:05:00'),
|
||||
('George', 31, LOWER('george+test@example.com'), 'US', true, '2024-03-05 16:45:00'),
|
||||
('Ivan', 26, LOWER('ivan@example.com'), 'RU', true, '2024-01-11 14:30:00'),
|
||||
('Julia', 28, LOWER('julia@example.com'), 'US', true, '2024-03-07 18:05:00');
|
||||
|
||||
INSERT INTO products (name, category, price, in_stock)
|
||||
VALUES ('iPhone 15', 'Электроника', 80000, true),
|
||||
('Kindle', 'Электроника', 15000, true),
|
||||
('Кроссовки Nike', 'Одежда', 9000, true),
|
||||
('Кроссовки NoName', 'Одежда', 3000, false),
|
||||
('Наушники Pro', 'Электроника', 12000, true),
|
||||
('Фитнес‑браслет', 'Гаджеты', 6000, true),
|
||||
('Подарочная карта', 'Сервисы', 1000, true);
|
||||
|
||||
INSERT INTO orders (user_id, order_date, status)
|
||||
VALUES (1, '2025-01-10 12:00:00', 'paid'),
|
||||
(3, '2025-01-12 09:30:00', 'cancelled'),
|
||||
(1, '2025-01-15 18:20:00', 'paid'),
|
||||
(2, '2025-01-20 11:05:00', 'created'),
|
||||
(4, '2025-02-01 17:45:00', 'paid'),
|
||||
(5, '2025-02-10 10:10:00', 'refunded'),
|
||||
(7, '2025-03-01 09:00:00', 'paid'),
|
||||
(9, '2025-03-05 19:30:00', 'paid');
|
||||
|
||||
INSERT INTO order_items (order_id, product_id, quantity, price_at_purchase)
|
||||
VALUES (1, 1, 1, 78000),
|
||||
(1, 3, 2, 8500),
|
||||
(2, 2, 1, 14000),
|
||||
(3, 5, 1, 11000),
|
||||
(3, 7, 1, 1000),
|
||||
(4, 2, 1, 15000),
|
||||
(5, 6, 1, 6000),
|
||||
(6, 1, 1, 82000),
|
||||
(7, 3, 1, 9000),
|
||||
(8, 4, 2, 2800);
|
||||
@@ -0,0 +1,56 @@
|
||||
-- Задача 1
|
||||
SELECT name, category, price,
|
||||
price * 1.3 AS price_with_markup,
|
||||
price * 1.5 AS price_with_vat
|
||||
FROM products
|
||||
WHERE (price BETWEEN 100 AND 1300)
|
||||
AND category NOT IN ('smartwatch')
|
||||
AND in_stock = TRUE;
|
||||
|
||||
-- Вопрос:
|
||||
-- Что лучше читается BETWEEN 100 AND 1300 или price >= 100 AND price <= 1300 и почему?
|
||||
-- Ответ:
|
||||
-- Для меня BETWEEN 100 AND 1300 читается лучше, сразу понятен срез от 100 и 1300.
|
||||
|
||||
|
||||
-- Задача 2
|
||||
SELECT name, price,
|
||||
price * 0.85 AS price_with_discount
|
||||
FROM products
|
||||
WHERE (price < 100 OR price > 1000);
|
||||
|
||||
-- Вопрос:
|
||||
-- Почему важно явно писать скобки в условии с OR, даже если кажется, что и так понятно?
|
||||
-- Ответ:
|
||||
-- Потому что условие может не правильно отобразить результат, AND выполняется раньше OR.
|
||||
-- Для надежности лучше скобками прописывать явно.
|
||||
|
||||
|
||||
-- Задача 3
|
||||
SELECT id, name, category, price
|
||||
FROM products
|
||||
WHERE price >= 100
|
||||
AND category IN ('tablet', 'smartphone', 'mouse')
|
||||
ORDER BY price DESC;
|
||||
|
||||
|
||||
-- Задача 4
|
||||
SELECT name, category, price
|
||||
FROM products
|
||||
WHERE name NOT LIKE '%iPhone%';
|
||||
|
||||
|
||||
-- Задача 5
|
||||
SELECT name, category, price
|
||||
FROM products
|
||||
WHERE name ILIKE '%pro%';
|
||||
|
||||
SELECT name, category, price
|
||||
FROM products
|
||||
WHERE name ILIKE '%mx%'
|
||||
ORDER BY price DESC;
|
||||
|
||||
-- Вопрос:
|
||||
-- В одном предложении объясни, почему для поисковой строки лучше ILIKE '%pro%', а не = 'Pro'.
|
||||
-- Ответ:
|
||||
-- В названии товаров могут быть буквы (в словах) с разными регистрами, при поиске клиенту можен не показать часть товаров только потому что слово выглядить так - PRO а не так - Pro.
|
||||
@@ -0,0 +1,144 @@
|
||||
-- Задание 1
|
||||
SELECT o.id AS order_id,
|
||||
o.order_date,
|
||||
o.status,
|
||||
u.name AS user_name,
|
||||
u.country
|
||||
FROM orders o
|
||||
JOIN users u ON o.user_id = u.id;
|
||||
|
||||
-- Ответ на вопросы
|
||||
-- 1. Логичнее начать c orders, так как нам в первую очередь нужны все заказы,
|
||||
-- а потом только пользователи которые их сделали.
|
||||
|
||||
-- 2. Я думаю уместнее сделать LEFT.
|
||||
-- Я пробовал сделать с INNER и LEFT и у меня не пропали строки.
|
||||
-- Получается это из за того что у каждого заказа есть пользователь, не дуаю что должен создастся заказ без пользователя.
|
||||
|
||||
|
||||
-- Задание 2
|
||||
SELECT o.id AS order_id,
|
||||
p.name AS product_name,
|
||||
oi.quantity,
|
||||
oi.price_at_purchase
|
||||
FROM orders o
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
JOIN products p ON p.id = oi.product_id
|
||||
WHERE o.id = 101;
|
||||
|
||||
-- Ответ на вопрос
|
||||
-- Выдаст дублированный состав заказа с id 101,
|
||||
-- получается он будет обращаться к каждаму заказу, но отображать продукты заказа с id 101
|
||||
|
||||
|
||||
-- Задание 3
|
||||
SELECT o.id AS order_id,
|
||||
u.name AS name,
|
||||
p.name AS product_name,
|
||||
oi.quantity,
|
||||
oi.price_at_purchase
|
||||
FROM orders o
|
||||
JOIN users u ON u.id = o.user_id
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
JOIN products p ON p.id = oi.product_id;
|
||||
|
||||
-- Ответ на вопрос
|
||||
-- 1. Появляется дубликат заказов потому что у 3 заказа есть несколько товаров, join превращает(размножает) их в несколько строк.
|
||||
-- 2. Отличается тем что мы выводим все заказы с товарами а не один заказ.
|
||||
|
||||
|
||||
-- Задание 4
|
||||
SELECT u.name AS user_name,
|
||||
o.id AS order_id,
|
||||
o.status
|
||||
FROM users u
|
||||
LEFT JOIN orders o ON o.user_id = u.id;
|
||||
|
||||
-- Ответ на вопрос
|
||||
-- INNER JOIN не выдаст пользователей без заказа. Только тех у кого есть пара с orders.
|
||||
|
||||
|
||||
-- Задание 5
|
||||
SELECT u.id AS user_id,
|
||||
u.name AS user_name
|
||||
FROM users u
|
||||
LEFT JOIN orders o ON o.user_id = u.id
|
||||
WHERE o.id IS NULL;
|
||||
|
||||
-- Ответ на вопрос
|
||||
-- 1. Проверять IS NULL нужно по полю orders.id
|
||||
-- 2. Мы должны в запросе получить всех пользователей, а потом в фильтре "узнать" есть ли у них заказы.
|
||||
|
||||
|
||||
-- Задание 6
|
||||
SELECT o.id AS order_id,
|
||||
u.name AS user_name,
|
||||
p.name AS product_name,
|
||||
oi.quantity
|
||||
FROM orders o
|
||||
JOIN users u ON u.id = o.user_id
|
||||
JOIN order_items oi ON oi.order_id = o.id
|
||||
JOIN products p ON p.id = oi.product_id
|
||||
WHERE p.name ILIKE '%kindle%';
|
||||
|
||||
-- Ответ на вопрос
|
||||
-- В заказе 2 есть только один товар с названием Kindle.
|
||||
|
||||
|
||||
-- Задание 7
|
||||
SELECT p.name AS product_name,
|
||||
oi.order_id,
|
||||
oi.quantity
|
||||
FROM products p
|
||||
LEFT JOIN order_items oi ON oi.product_id = p.id;
|
||||
|
||||
-- Ответ на вопрос
|
||||
-- 1. order_id будет NULL
|
||||
-- 2. Если использовать INNER JOIN то он не покажет товар который не заказывали.
|
||||
|
||||
|
||||
-- Задание 8
|
||||
SELECT o.id AS order_id,
|
||||
u.name AS user_name,
|
||||
p.name AS product_name,
|
||||
oi.quantity,
|
||||
oi.price_at_purchase
|
||||
FROM products p
|
||||
JOIN order_items oi ON oi.product_id = p.id
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
JOIN users u ON u.id = o.user_id
|
||||
WHERE o.status = 'paid';
|
||||
|
||||
-- Ответ на вопрос
|
||||
-- У меня получился тот же результат если написать вот так - JOIN orders o ON o.id = oi.order_id and o.status = 'paid'
|
||||
|
||||
|
||||
-- Задание 9
|
||||
SELECT o.id AS order_id,
|
||||
o.order_date,
|
||||
p.name AS product_name,
|
||||
oi.quantity,
|
||||
oi.price_at_purchase
|
||||
FROM products p
|
||||
JOIN order_items oi ON oi.product_id = p.id
|
||||
JOIN orders o ON o.id = oi.order_id
|
||||
JOIN users u ON u.id = o.user_id
|
||||
WHERE u.name = 'Alice';
|
||||
|
||||
-- Ответ на вопрос
|
||||
-- Если у пользователя нет ни одного заказа, то этот запрос вернет пустую строку.
|
||||
-- Не вернет потому что нечего отобразить таблице, если нет заказов то и нет купленных товаров.
|
||||
-- Но если прописать RIGHT JOIN users u ON u.id = o.user_id то отобразит все строки NULL.
|
||||
|
||||
|
||||
-- Задание 10
|
||||
SELECT o.id AS order_id,
|
||||
o.user_id,
|
||||
o.status
|
||||
FROM orders o
|
||||
LEFT JOIN order_items oi ON oi.order_id = o.id
|
||||
WHERE oi.order_id IS NULL;
|
||||
|
||||
-- Ответ на вопрос
|
||||
-- Такой запрос выдаст пустые строки.
|
||||
-- INNER JOIN должен выдать строки только если есть "пара".
|
||||
@@ -0,0 +1,73 @@
|
||||
-- Задание 1
|
||||
SELECT id, name, country, created_at, email
|
||||
FROM users
|
||||
ORDER BY created_at DESC, name;
|
||||
|
||||
-- Почему выбрал именно эти столбцы и зачем такая сортировка?
|
||||
-- Ответ на вопрос:
|
||||
-- Эти столбцы нужны для анализа пользователей, для тех же аналитиков.
|
||||
-- Сортировка по дате и имени, я так предпологаю, нужня для удобства.
|
||||
|
||||
|
||||
-- Задание 2
|
||||
-- Запрос 1
|
||||
SELECT id, name, country
|
||||
FROM users
|
||||
WHERE country = 'US'
|
||||
ORDER BY name;
|
||||
|
||||
-- Запрос 2
|
||||
SELECT id, name, country
|
||||
FROM users
|
||||
WHERE country = 'UK'
|
||||
ORDER BY name;
|
||||
|
||||
-- Какой из двух запросов маркетологу полезнее и почему?
|
||||
-- Ответ на вопрос:
|
||||
-- Я так понимаю маркетологу нужно понять какое кол-во пользователей в каждой из стран зарегестрировалось в приложении.
|
||||
-- Возможно для понимания продвижения продукта и насколько успешна маркетинговая стратегия. И в дальнейшем её корректировка.
|
||||
|
||||
|
||||
-- Задание 3
|
||||
-- Запрос 1
|
||||
SELECT id, name, email
|
||||
FROM users
|
||||
WHERE email IS NOT NULL -- я знаю что email и так должен быть NOT NULL по DDL, но на всякий прописал
|
||||
ORDER BY name;
|
||||
|
||||
-- Запрос 2
|
||||
SELECT DISTINCT LOWER(email) as email_lower
|
||||
FROM users
|
||||
WHERE email IS NOT NULL;
|
||||
|
||||
-- Зачем вообще приводить email к одному регистру и убирать дубликаты на стороне базы?
|
||||
-- Ответ на вопрос:
|
||||
-- Приводить email к нижнему регистру необходимо для того чтобы небыло дубликатов почты.
|
||||
-- После гугления я узнал что в почтовых адресах могут допускаться верхние регистры букв, только потом создать email с теми же буквами но в нижнем регистре нельзя будет, они будут считаться индентичными.
|
||||
-- Получается несколько пользователей могут залогинится под одним и тем же email адресом но с разными регистрами. А такое нельзя допускать.
|
||||
-- Для рассылки дубликаты нужно убирать что-бы не приходила рассылка дважды в один и тот же email.
|
||||
|
||||
-- Задание 4
|
||||
-- Запрос 1
|
||||
SELECT id, name, created_at
|
||||
FROM users
|
||||
WHERE created_at > '2024-01-14'
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- Запрос 2
|
||||
SELECT id, name, created_at
|
||||
FROM users
|
||||
WHERE created_at > '2024-01-14'
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 3;
|
||||
|
||||
-- Запрос 3
|
||||
SELECT id, name, created_at
|
||||
FROM users
|
||||
WHERE created_at > '2024-01-14'
|
||||
ORDER BY created_at DESC
|
||||
OFFSET 1 LIMIT 2;
|
||||
|
||||
-- В каком реальном сценарии тебе пригодится OFFSET?
|
||||
-- Ответ на вопрос:
|
||||
-- Не знаю, возможно понадобится посмотреть несколько записей из середины списка.
|
||||
Reference in New Issue
Block a user