Загрузка данных


-- физическая схема: магазин игрушек (postgresql)
-- auto_increment -> generated by default as identity, datetime -> timestamp

create table category (
    id_category integer generated by default as identity primary key,
    name varchar(50) not null unique,
    description varchar(500)
);

create table manufacturer (
    id_manufacturer integer generated by default as identity primary key,
    name varchar(100) not null,
    country varchar(60) not null,
    phone varchar(20),
    email varchar(256)
);

create table toy (
    id_toy integer generated by default as identity primary key,
    id_category integer not null,
    id_manufacturer integer not null,
    article varchar(20) not null unique,
    name varchar(120) not null,
    description varchar(2000),
    price decimal(10,2) not null check (price > 0),
    min_age integer not null default 0 check (min_age between 0 and 18),
    material varchar(50),
    stock_quantity integer not null default 0 check (stock_quantity >= 0),
    constraint fk_idcategory_toy
        foreign key (id_category)
        references category(id_category),
    constraint fk_idmanufacturer_toy
        foreign key (id_manufacturer)
        references manufacturer(id_manufacturer)
);

create table customer (
    id_customer integer generated by default as identity primary key,
    full_name varchar(100) not null,
    phone varchar(20) not null,
    email varchar(256) not null unique,
    password varchar(255) not null, -- хеш, не сам пароль
    address varchar(300),
    registration_date timestamp not null default current_timestamp
);

create table employee (
    id_employee integer generated by default as identity primary key,
    full_name varchar(100) not null,
    position varchar(30) not null
        check (position in ('Администратор','Менеджер','Продавец','Кладовщик')),
    phone varchar(20) not null,
    email varchar(256) not null unique,
    password varchar(255) not null
);

create table orders (
    id_order integer generated by default as identity primary key,
    id_customer integer not null,
    id_employee integer, -- null, пока заказ никто не взял
    order_date timestamp not null default current_timestamp,
    status varchar(20) not null default 'Новый'
        check (status in ('Новый','Подтверждён','Собран','Доставляется','Выполнен','Отменён')),
    payment_method varchar(20) not null
        check (payment_method in ('Наличные','Карта','Онлайн')),
    delivery_address varchar(300), -- null при самовывозе
    total_amount decimal(12,2) not null default 0 check (total_amount >= 0),
    constraint fk_idcustomer_orders
        foreign key (id_customer)
        references customer(id_customer),
    constraint fk_idemployee_orders
        foreign key (id_employee)
        references employee(id_employee)
);

create table order_item (
    id_order_item integer generated by default as identity primary key,
    id_order integer not null,
    id_toy integer not null,
    quantity integer not null check (quantity >= 1),
    unit_price decimal(10,2) not null check (unit_price > 0),
    unique (id_order, id_toy), -- одна игрушка = одна позиция в заказе
    constraint fk_idorder_order_item
        foreign key (id_order)
        references orders(id_order),
    constraint fk_idtoy_order_item
        foreign key (id_toy)
        references toy(id_toy)
);

create table review (
    id_review integer generated by default as identity primary key,
    id_toy integer not null,
    id_customer integer not null,
    rating integer not null check (rating between 1 and 5),
    text varchar(1000),
    creation_date timestamp not null default current_timestamp,
    constraint fk_idtoy_review
        foreign key (id_toy)
        references toy(id_toy),
    constraint fk_idcustomer_review
        foreign key (id_customer)
        references customer(id_customer)
);

insert into category values
(1,'Конструкторы','Наборы для сборки'),
(2,'Мягкие игрушки','Плюшевые и текстильные игрушки'),
(3,'Настольные игры','Игры для всей семьи'),
(4,'Куклы','Куклы и аксессуары'),
(5,'Машинки','Машинки и транспорт'),
(6,'Развивающие','Игрушки для малышей');

insert into manufacturer values
(1,'Весна','Россия','+7 495 111-22-33','info@vesna-toys.ru'),
(2,'Огонёк','Россия','+7 812 222-33-44','sales@ogonek.ru'),
(3,'WoodKids','Германия','+49 30 1234567',null),
(4,'PlayTime','Китай',null,'export@playtime.cn'),
(5,'Kinderfun','Польша',null,'hello@kinderfun.pl');

insert into toy values
(1,1,4,'KN-001','Конструктор «Город»','Набор из 500 деталей',3490.00,6,'пластик',25),
(2,1,3,'KN-002','Деревянный конструктор','Кубики и арки из бука',1890.00,3,'дерево',40),
(3,2,1,'MI-001','Плюшевый медведь','Медведь 40 см',1290.00,0,'текстиль',60),
(4,2,5,'MI-002','Зайка-сплюшка','Мягкий зайка для сна',890.00,0,'текстиль',0),
(5,3,2,'NI-001','Мемори «Животные»','Игра на память, 48 карточек',590.00,4,'картон',80),
(6,3,2,'NI-002','Шашки и шахматы','Набор 2 в 1',1450.00,7,'дерево',15),
(7,4,1,'KU-001','Кукла Маша','Кукла 35 см с одеждой',1990.00,3,'пластик',30),
(8,4,4,'KU-002','Кукольный домик','Трёхэтажный домик с мебелью',6990.00,4,'дерево',5),
(9,5,4,'MA-001','Машинка на пульте','Джип 1:16 с аккумулятором',2790.00,6,'пластик',12),
(10,5,3,'MA-002','Деревянный паровозик','Паровоз с тремя вагонами',1190.00,2,'дерево',22),
(11,6,5,'RZ-001','Сортер «Домик»','Сортер с 12 фигурами',990.00,1,'пластик',35),
(12,6,3,'RZ-002','Бизиборд','Развивающая доска с замками',2490.00,2,'дерево',8);

insert into customer values
(1,'Никита Павликов','+7 900 111-11-11','nikita.pavlikov@gmail.com','5f4dcc3b5aa765d61d8327deb882cf99','Москва, ул. Ленина, 10','2026-08-01 10:00:00'),
(2,'Анна Смирнова','+7 900 222-22-22','anna.smirnova@mail.ru','d8578edf8458ce06fbc5bb76a58c5ca4','Санкт-Петербург, Невский пр., 25','2026-08-03 14:20:00'),
(3,'Дмитрий Козлов','+7 900 333-33-33','dmitry.kozlov@gmail.com','25d55ad283aa400af464c76d713c07ad',null,'2026-08-10 09:05:00'),
(4,'Елена Волкова','+7 900 444-44-44','elena.volkova@yandex.ru','e99a18c428cb38d5f260853678922e03','Казань, ул. Баумана, 5','2026-08-15 19:40:00'),
(5,'Сергей Морозов','+7 900 555-55-55','sergey.morozov@gmail.com','fcea920f7412b5da7be0cf42b8c93759','Москва, ул. Тверская, 7','2026-08-20 11:30:00'),
(6,'Ольга Новикова','+7 900 666-66-66','olga.novikova@mail.ru','827ccb0eea8a706c4c34a16891f84e7b',null,'2026-09-01 16:00:00'),
(7,'Артём Соколов','+7 900 777-77-77','artem.sokolov@gmail.com','6cb75f652a9b52798eb6cf2201057c73','Екатеринбург, ул. Мира, 3','2026-09-05 12:10:00'),
(8,'Мария Лебедева','+7 900 888-88-88','maria.lebedeva@yandex.ru','0d107d09f5bbe40cade3de5c71e9e9b7','Новосибирск, Красный пр., 50','2026-09-12 08:45:00');

insert into employee values
(1,'Иван Попов','Администратор','+7 901 100-00-01','i.popov@toyshop.ru','a3dcb4d229de6fde0db5686dee47145d'),
(2,'Павлик Никитов','Менеджер','+7 901 100-00-02','p.nikitov@toyshop.ru','1a1dc91c907325c69271ddf0c944bc72'),
(3,'Ксения Орлова','Менеджер','+7 901 100-00-03','k.orlova@toyshop.ru','b59c67bf196a4758191e42f76670ceba'),
(4,'Глеб Фёдоров','Продавец','+7 901 100-00-04','g.fedorov@toyshop.ru','c33367701511b4f6020ec61ded352059'),
(5,'Тимур Зайцев','Кладовщик','+7 901 100-00-05','t.zaitsev@toyshop.ru','8621ffdbc5698829397d97767ac13db3');

insert into orders values
(1,1,2,'2026-09-02 12:00:00','Выполнен','Карта','Москва, ул. Ленина, 10',4780.00),
(2,2,3,'2026-09-10 15:30:00','Доставляется','Онлайн','Санкт-Петербург, Невский пр., 25',3980.00),
(3,3,4,'2026-09-12 11:15:00','Выполнен','Наличные',null,2760.00),
(4,4,2,'2026-09-20 18:00:00','Собран','Карта','Казань, ул. Баумана, 5',6990.00),
(5,5,null,'2026-10-01 10:20:00','Новый','Онлайн','Москва, ул. Тверская, 7',3980.00),
(6,1,3,'2026-10-03 09:40:00','Отменён','Онлайн','Москва, ул. Ленина, 10',1780.00),
(7,6,2,'2026-10-05 14:05:00','Подтверждён','Карта',null,4380.00),
(8,7,null,'2026-10-08 17:50:00','Новый','Наличные','Екатеринбург, ул. Мира, 3',1190.00),
(9,8,4,'2026-10-10 13:25:00','Выполнен','Карта','Новосибирск, Красный пр., 50',3170.00),
(10,2,3,'2026-10-12 20:10:00','Выполнен','Онлайн','Санкт-Петербург, Невский пр., 25',8260.00);

insert into order_item values
(1,1,1,1,3490.00),
(2,1,3,1,1290.00),
(3,2,7,2,1990.00),
(4,3,5,3,590.00),
(5,3,11,1,990.00),
(6,4,8,1,6990.00),
(7,5,9,1,2790.00),
(8,5,10,1,1190.00),
(9,6,4,2,890.00),
(10,7,12,1,2490.00),
(11,7,2,1,1890.00),
(12,8,10,1,1190.00),
(13,9,3,2,1290.00),
(14,9,5,1,590.00),
(15,10,1,1,3490.00),
(16,10,9,1,2790.00),
(17,10,11,2,990.00);

insert into review values
(1,1,1,5,'Сын в восторге, собирали весь вечер','2026-09-05 18:00:00'),
(2,3,1,4,'Мягкий, но немного линяет','2026-09-06 12:00:00'),
(3,5,3,5,'Отличная игра для всей семьи','2026-09-15 20:30:00'),
(4,11,3,4,'Фигурки яркие, ребёнку нравится','2026-09-15 20:35:00'),
(5,3,8,5,'Купили второго медведя в подарок','2026-10-13 10:00:00'),
(6,5,8,3,'Карточки тонковаты','2026-10-13 10:05:00'),
(7,1,2,5,'Детали качественные, ничего не потерялось','2026-10-15 19:20:00'),
(8,9,2,4,'Быстро разряжается, но ездит бодро','2026-10-15 19:25:00'),
(9,11,2,5,'Брали на годик — идеально','2026-10-15 19:30:00'),
(10,7,2,2,'Одежда порвалась через неделю','2026-09-20 11:00:00'),
(11,8,4,5,null,'2026-09-25 16:40:00'),
(12,2,6,4,'Хороший деревянный набор','2026-10-09 09:15:00');



-- 1
-- игрушки дешевле 1000
select id_toy, name, price
from toy
where price < 1000;

-- статусы, в которых больше одного заказа
select status, count(*) as cnt
from orders
group by status
having count(*) > 1;

-- заказы, которые ещё никто не взял
select id_order, id_customer, order_date
from orders
where status = 'Новый';

-- заказы, которые собраны или в доставке
select o.id_order, o.status, o.total_amount
from orders as o
where o.status in ('Собран', 'Доставляется');

-- сколько заказов в каждом статусе
select status, count(*)
from orders
group by status;

-- первые 3 покупателя
select c.id_customer, c.full_name
from customer as c
limit 3;

-- сотрудники по алфавиту
select full_name, position
from employee
order by full_name;

-- 2
-- игрушки не из пластика
select t.id_toy, t.name, t.material
from toy as t
where t.material not in ('пластик')
order by t.id_toy;

-- заказы покупателя с айди 2
select o.id_order, o.order_date, o.status
from orders as o
where o.id_customer = 2
order by o.order_date;

-- топ-3 самых дорогих игрушки
select t.id_toy, t.name, t.price
from toy as t
order by t.price desc
limit 3;

-- отзывы на игрушку с айди 3
select r.id_customer, r.rating, r.text
from review as r
where r.id_toy = 3
order by r.creation_date;

-- самый первый зарегистрированный покупатель
select c.full_name, c.email, c.registration_date
from customer as c
order by c.registration_date
limit 1;


-- 3
-- топ-3 покупателя по числу отзывов
select c.full_name, count(r.id_review) as reviews_count
from customer as c
left join review as r on r.id_customer = c.id_customer
group by c.id_customer, c.full_name
order by reviews_count desc
limit 3;

-- все категории и их игрушки
select cat.name as category, t.id_toy, t.name as toy
from category as cat
left join toy as t on t.id_category = cat.id_category
order by cat.name, t.id_toy;

-- все заказы и id игрушек в них
select o.id_order, o.status, oi.id_toy
from orders as o
left join order_item as oi on oi.id_order = o.id_order
order by o.id_order;

-- 4
-- 1. игрушки с категорией и производителем
select t.id_toy, t.name, t.price, cat.name as category, m.name as manufacturer, m.country
from toy as t
left join category as cat on cat.id_category = t.id_category
left join manufacturer as m on m.id_manufacturer = t.id_manufacturer
order by cat.name, t.id_toy;

-- 2. лента отзывов: кто, на какую игрушку, из какой категории
select r.id_review, r.creation_date, c.full_name as author, t.name as toy, cat.name as category, r.rating, r.text
from review as r
left join customer as c on c.id_customer = r.id_customer
left join toy as t on t.id_toy = r.id_toy
left join category as cat on cat.id_category = t.id_category
order by r.creation_date desc;

-- 3. состав заказов: покупатель, сотрудник, игрушка, сумма позиции
select o.id_order, c.full_name as customer, e.full_name as employee, t.name as toy,
       oi.quantity, oi.unit_price, oi.quantity * oi.unit_price as item_sum
from orders as o
left join customer as c on c.id_customer = o.id_customer
left join employee as e on e.id_employee = o.id_employee
left join order_item as oi on oi.id_order = o.id_order
left join toy as t on t.id_toy = oi.id_toy
order by o.id_order, t.name;

-- 5 вложенные запросы

-- 1. покупатели, которые платили онлайн
select c.id_customer, c.full_name
from ( select distinct id_customer from orders where payment_method = 'Онлайн' )
as online join customer as c on c.id_customer = online.id_customer;

-- 2. игрушки, у которых больше одного отзыва
select r.id_toy, r.cnt
from ( select id_toy, count(*) as cnt from review group by id_toy )
as r where r.cnt > 1
order by r.cnt desc;

-- 3. покупатели и сколько у них заказов
select c.full_name, z.cnt as orders_count
from ( select id_customer, count(*) as cnt from orders group by id_customer )
as z join customer as c on c.id_customer = z.id_customer
order by z.cnt desc;

-- 4. категории и сколько в них игрушек
select cat.name, z.cnt as toys_count
from ( select id_category, count(*) as cnt from toy group by id_category )
as z join category as cat on cat.id_category = z.id_category
order by z.cnt desc;


-- 6 вложенные запросы с <, >, in, not in, exists
-- 1. производители, у которых больше 2 игрушек
select m.id_manufacturer, m.name, z.cnt as toys_count
from ( select id_manufacturer, count(*) as cnt from toy group by id_manufacturer ) as z
left join manufacturer as m on m.id_manufacturer = z.id_manufacturer
where z.cnt > 2
order by z.cnt desc;

-- 2. игрушки со средней оценкой ниже 4
select t.id_toy, t.name, r.avg_rating
from toy as t
join ( select id_toy, round(avg(rating), 2) as avg_rating from review group by id_toy ) as r
  on r.id_toy = t.id_toy
where r.avg_rating < 4
order by r.avg_rating;

-- 3. сотрудники, у которых больше одного незакрытого заказа
select e.id_employee, e.full_name, count(o.id_order) as active_orders
from employee as e
join orders as o on o.id_employee = e.id_employee
where e.id_employee in (
    select id_employee from orders
    where status not in ('Выполнен', 'Отменён')
    group by id_employee
    having count(*) > 1
)
and o.status not in ('Выполнен', 'Отменён')
group by e.id_employee, e.full_name
order by active_orders desc;

-- 4. игрушки, которые ни разу не заказывали
select t.id_toy, t.name
from toy as t
where t.id_toy not in (select id_toy from order_item);

-- 5. категории, где есть игрушка, которой нет на складе
select cat.id_category, cat.name
from category as cat
where exists (
    select 1 from toy as t
    where t.id_category = cat.id_category and t.stock_quantity = 0
)
order by cat.id_category;



-- 7. insert on conflict

-- добавить или обновить покупателя
insert into customer (id_customer, full_name, phone, email, password, address)
values (9, 'Егор Иванов', '+7 900 999-99-99', 'egor.ivanov@gmail.com', '9f86d081884c7d659a2feaa0c55ad015', 'Москва, ул. Арбат, 12')
on conflict (id_customer) do update
set full_name = excluded.full_name,
    phone = excluded.phone,
    email = excluded.email,
    address = excluded.address;

-- добавить или обновить сотрудника
insert into employee (id_employee, full_name, position, phone, email, password)
values (6, 'Алина Белова', 'Продавец', '+7 901 100-00-06', 'a.belova@toyshop.ru', '2c26b46b68ffc68ff99b453c1d304134')
on conflict (id_employee) do update
set position = excluded.position,
    phone = excluded.phone;

-- добавить или переименовать категорию
insert into category (id_category, name, description)
values (7, 'Пазлы', 'Пазлы и мозаики')
on conflict (id_category) do update
set name = excluded.name,
    description = excluded.description;

-- та же игрушка в том же заказе -> увеличиваем количество
insert into order_item (id_order_item, id_order, id_toy, quantity, unit_price)
values (18, 1, 1, 1, 3490.00)
on conflict (id_order, id_toy) do update
set quantity = order_item.quantity + excluded.quantity;

-- добавить или переписать отзыв
insert into review (id_review, id_toy, id_customer, rating, text)
values (13, 6, 9, 5, 'Классный набор, играем с дедушкой')
on conflict (id_review) do update
set rating = excluded.rating,
    text = excluded.text;


-- 7.1

-- добавить нового производителя
insert into manufacturer (id_manufacturer, name, country, phone, email)
values (6, 'Сказка', 'Беларусь', '+375 17 123-45-67', 'info@skazka.by');

-- добавить новую игрушку
insert into toy (id_toy, id_category, id_manufacturer, article, name, description, price, min_age, material, stock_quantity)
values (13, 6, 6, 'RZ-003', 'Счёты деревянные', 'Счёты с цветными бусинами', 790.00, 3, 'дерево', 20);

-- добавить нового покупателя (дата регистрации по умолчанию)
insert into customer (id_customer, full_name, phone, email, password)
values (10, 'Олег Петров', '+7 900 101-01-01', 'oleg.petrov@mail.ru', 'fd61a03af4f77d870fc21e05e7e80678');

-- оформить заказ (дата и статус по умолчанию)
insert into orders (id_order, id_customer, payment_method, delivery_address)
values (11, 10, 'Карта', 'Тула, ул. Советская, 1');

-- добавить позицию в заказ
insert into order_item (id_order_item, id_order, id_toy, quantity, unit_price)
values (19, 11, 13, 2, 790.00);

-- оставить отзыв
insert into review (id_review, id_toy, id_customer, rating, text)
values (14, 13, 10, 5, 'Ребёнок учится считать с удовольствием');

-- 8. update
-- поднять цены на машинки на 10%
update toy
set price = price * 1.10
where id_category = 5;

-- взять заказ в работу
update orders
set status = 'Подтверждён',
    id_employee = 3
where id_order = 5;

-- пополнить склад
update toy
set stock_quantity = stock_quantity + 30
where id_toy = 4;

-- сменить хеш пароля покупателя
update customer
set password = 'b3a8e0e1f9ab1bfe3a36f231f676f78b'
where id_customer = 2;

-- пересчитать суммы всех заказов по позициям
update orders as o
set total_amount = coalesce((
    select sum(oi.quantity * oi.unit_price)
    from order_item as oi
    where oi.id_order = o.id_order
), 0);


-- 9. delete

-- убрать позицию из заказа
delete from order_item
where id_order = 7 and id_toy = 2;

-- удалить плохие отзывы
delete from review
where rating <= 2;

-- сначала позиции отменённых заказов, потом сами заказы
delete from order_item
where id_order in (select id_order from orders where status = 'Отменён');

delete from orders
where status = 'Отменён';

-- удалить пустые категории
delete from category as cat
where not exists (select 1 from toy as t where t.id_category = cat.id_category);

-- удалить сотрудника без заказов
delete from employee
where id_employee = 6;