diff --git a/Project/11.12.2024_05.55.52_REC.png b/Project/11.12.2024_05.55.52_REC.png new file mode 100644 index 0000000..70cb058 Binary files /dev/null and b/Project/11.12.2024_05.55.52_REC.png differ diff --git a/Project/README.md b/Project/README.md index 7395f6d..6ae4bda 100644 --- a/Project/README.md +++ b/Project/README.md @@ -48,5 +48,103 @@ _**ТРАНСПОРТ**_ : НАЗВАНИЕ, ТИП, СОСТОЯНИЕ - Инфо логическая модель - Дата логическая модель - - +## 3. Улучшение структуры БД + +### Нормальная форма (Database normalization) +Под. Описание [wikipedia](https://ru.wikipedia.org/wiki/%D0%9D%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%84%D0%BE%D1%80%D0%BC%D0%B0) + +Нормализация позволяет оптимально распределять атрибуты по таблицам. Данная методика избавляет от: +- атрибутов с несколькими значениями; +- повторяющихся атрибутов; +- атрибутов, не поддающихся классификации; +- атрибутов с избыточной информацией; +- атрибутов, созданных из других признаков. + +1. Первая нормальная форма (1NF) +Таблица находится в 1NF, если она имеет Атомарные значения (нет повторяющихся групп или массивов) и +Каждая строка уникально идентифицируется первичным ключом. +Мои схемы уже соответствуют 1NF, так как каждый столбец содержит атомарные значения, и первичные ключи определены для всех таблиц. + +2. Вторая нормальная форма (2NF) +Таблица находится во 2NF, если: +Она находится в 1NF. +Все неключевые атрибуты полностью функционально зависят от первичного ключа (нет частичной зависимости). +Мои схемы уже соответствуют 2NF. + +3. Третья нормальная форма (3NF) +Таблица находится в 3NF, если: +Она находится в 2NF. +Нет транзитивной зависимости, что означает, что неключевые атрибуты не должны зависеть от других неключевых атрибутов. + +Таблица сотрудников: таблица включает ссылку на внешний ключ base_id, что можно считать транзитивной зависимостью.
+Вместо того чтобы напрямую хранить base_id, могу создать новую таблицу, которая связывает сотрудников с их базой, и удалить прямую ссылку из таблицы сотрудников. +Чтобы решить эту проблему, создаю отдельную таблицу employee_base: + +```sql +CREATE TABLE employee_base ( + emp_id INTEGER NOT NULL REFERENCES employee ON DELETE CASCADE, + base_id INTEGER NOT NULL REFERENCES base ON DELETE SET NULL, + PRIMARY KEY (emp_id, base_id) +); +``` + +Это устраняет транзитивную зависимость от base_id в таблице сотрудников и улучшает 3NF. + +4. Нормальная форма Бойса-Кодда (BCNF) +Таблица находится в BCNF, если: +Она находится в 3NF. +Для каждой нетривиальной функциональной зависимости детерминант является кандидатом в ключи. +Таблица employee_base, описанная выше, поможет обеспечить отсутствие нетривиальных функциональных зависимостей, нарушающих BCNF. + +После этого, давайте подумаем о том, какие запросы будут наиболее часто используемыми (востребованными) для этой базы данных. В моем случае, я думаю, что нам обычно потребуется: +- Извлекать данные о сотрудниках, обновлять их и получать информацию о здоровье сотрудников, +- знать о различных миссиях, доходах и клиентах, +- а также о состоянии нашего транспорта. +Основываясь на этом, давайте используем некоторые полезные методы для повышения производительности этой базы данных: + +### Временные структуры и представления, способы валидации запросов +- Представления (Views) и Материализованные представления (Materialized Views) +
Материализованное представление в базе данных функционирует аналогично обычному представлению, но с одним важным отличием: оно кэширует результат запроса представления, +
сохраняя его в виде физической таблицы. Это означает, что в отличие от стандартного представления, при котором базовый запрос выполняется при каждом обращении к нему, +
материализованное представление отображает сохраненные данные до тех пор, пока они не будут обновлены. Это может значительно повысить производительность сложных запросов, для которых не требуются данные в реальном времени. + +### Индексы (Indexes) +Сначала я проверила часто используемые столбцы и создала выборку некоторых индексов, которые, по моему мнению, будут важны для повышения производительности базы данных. +Отметим, что я выбрала хэш-индекс для столбцов, основанных на идентификаторах, потому что: +1. Хэш-индексы(hash index) оптимизированы для поиска равенства (например, WHERE id = ?), что характерно для столбцов ID. +2. Хэш-индексы обеспечивают быстрое время поиска при средней временной сложности O(1), что делает их подходящими для первичных ключей и уникальных идентификаторов. +
Поскольку столбцы идентификаторов часто используются в точных совпадениях (=), хэш-индексы могут эффективно обрабатывать эти запросы. +
С другой стороны:
+1. Индексы B-дерева(b-tree) подходят для запросов диапазона (например, WHERE column > ? или WHERE column BETWEEN ? И ?), что характерно для столбцов без идентификатора. +2. Индексы B-дерева поддерживают эффективную сортировку и упорядочение, что делает их идеальными для столбцов, используемых в предложениях ORDER BY и GROUP BY. +3. Индексы B-дерева могут выполнять поиск как по равенству(=), так и по диапазону(<...>), что делает их хорошим выбором для столбцов с различными шаблонами запросов. +
Иногда индексы не всегда работают должным образом. Или мы можем создавать индексы, которые на самом деле не важны для базы данных. Итак, я решила провести некоторые проверки, которые позволили бы удалить неиспользуемые индексы. +```sql +SELECT relname , indexrelname , idx_scan , idx_tup_read , idx_tup_fetch +FROM pg_stat_user_indexes +WHERE schemaname = 'public' and + relname in ('campaign'); +``` +### Общие табличные выражения (CTE) и Временные таблицы (Temporary Tables) +CTE и временные таблицы имеют общие цели. Обе они генерируют промежуточные результаты для запроса, +
не оставляя постоянных объектов в базе данных; это экономит место для хранения. Но между ними есть важные различия: для CTE повторное использование кода ограничено одним запросом.
С другой стороны, данные, хранящиеся во временной таблице, могут многократно использоваться в различных запросах. Ключевым требованием является то, что эти запросы выполняются в рамках одного и того же подключения к базе данных (сеанса). +Я выбрала (CTE) для сводки (по состоянию здоровья сотрудников и доступности транспорта), потому что это упрощает запрос и делает его более читаемым. +
Они также позволяют разделить сложную логику на более мелкие и управляемые части. +
С другой стороны, я выбрал временную таблицу для определения наличия рабочих мест, поскольку она позволяет хранить промежуточные результаты и манипулировать ими, +
которые можно использовать несколько раз в запросе (например, в течение дня, если где-то требуется дополнительный сотрудник) или в нескольких запросах. + +- EXPLAIN ANALYZE +
EXPLAIN: предоставляет вам подробный план запроса, который показывает, как PostgreSQL планирует выполнить ваш SQL-запрос. +
Можно запустить EXPLAIN перед запросом SELECT, чтобы увидеть, какие шаги предпримет PostgreSQL для извлечения данных (например, использует ли он индекс, выполняет ли последовательное сканирование, объединяет и т.д.). +
EXPLAIN ANALYZE: фактически запускает запрос и показывает реальное время выполнения, помогая вам понять, насколько эффективно выполняется запрос. Он также показывает количество строк, обработанных на каждом шаге. +Пример использования до и после один из индексов. Можно заметить что время выполнения запроса время выполнения уменш.
+EXPLAIN ANALYZE + +- VACUUM +
PostgreSQL использует систему версионирования (MVCC), поэтому, когда строки обновляются или удаляются, +диск не освобождается немедленно. Со временем это может привести к раздуванию таблицы. +Запуск VACUUM помогает вернуть пространство и также может улучшить производительность. +
VACUUM: Освобождает место и анализирует таблицу для обновления статистики. +
VACUUM FULL: Полностью переписывает таблицу, освобождая пространство, но является более затратным и может блокировать таблицу на некоторое время. + +## 4. Триггеры и транзакции diff --git a/Project/data_population.sql b/Project/data_population.sql new file mode 100644 index 0000000..f2244ce --- /dev/null +++ b/Project/data_population.sql @@ -0,0 +1,201 @@ +-- -- Insert into base +-- INSERT INTO base (location, status) VALUES +-- ('Base A', 'OPEN'), +-- ('Base B', 'OPEN'), +-- ('Base C', 'CLOSED'), +-- ('Base D', 'OPEN'), +-- ('Base Burny', 'OPEN'), +-- ('Base Alfa', 'CLOSED'), +-- ('Base Dunky', 'OPEN'), +-- ('Base Elf', 'OPEN'); +-- +-- -- Insert into mre +-- INSERT INTO mre (breakfast, lunch, dinner, food_additives, kkal, proteins, fats, carbohydrate) VALUES +-- ('Oatmeal', 'Chicken Stew', 'Beef Stew', 'None', 3500, 100, 60, 300), +-- ('Pasta', 'Tuna Salad', 'Vegetable Curry', 'Spices', 4000, 120, 70, 350), +-- ('Rice', 'Beef Stroganoff', 'Chicken Curry', 'None', 4500, 110, 80, 320), +-- ('Cereal', 'Vegetable Soup', 'Pork Chops', 'None', 3700, 90, 50, 280), +-- ('Granola', 'Fish Tacos', 'Chili', 'Hot Sauce', 3900, 95, 55, 310); +-- +-- -- Insert into equipment +-- INSERT INTO equipment (camouflage, communication, intelligence, medical, mre_id, extra) VALUES +-- ('Woodland', 'Radio', 'Drones', 'First Aid Kit', 1, 'Extra Batteries'), +-- ('Desert', 'Satellite Phone', 'Recon', 'Medical Supplies', 2, 'GPS Device'), +-- ('Urban', 'Walkie Talkie', 'Surveillance', 'Trauma Kit', 3, 'Night Vision Goggles'), +-- ('Jungle', 'Signal Flare', 'Recon', 'First Aid Kit', 4, 'Binoculars'), +-- ('Snow', 'Radio', 'Drones', 'Medical Supplies', 5, 'Thermal Blanket'); +-- +-- -- Insert into position +-- INSERT INTO position (name, salary_rub, rank, equip_id, forces) VALUES +-- ('Infantry Soldier', 60000, 'Private', 1, 'GF'), +-- ('Sniper', 80000, 'Corporal', 2, 'NAVY'), +-- ('Medic', 70000, 'Sergeant', 3, 'AF'), +-- ('Engineer', 90000, 'Lieutenant', 4, 'GF'), +-- ('Pilot', 120000, 'Captain', 5, 'NAVY'); +-- +-- -- Insert into employee +-- INSERT INTO employee (name, surname, date_of_birth, education, pos_id, is_married, base_id) VALUES +-- ('John', 'Doe', '1990-01-15', 'High School', 1, TRUE, 1), +-- ('Jane', 'Smith', '1985-05-20', 'Bachelor', 2, FALSE, 1), +-- ('Alice', 'Johnson', '1992-03-10', 'Bachelor', 3, TRUE, 2), +-- ('Bob', 'Brown', '1988-07-25', 'Master', 4, FALSE, 2), +-- ('Charlie', 'Davis', '1980-12-30', 'PhD', 5, TRUE, 3), +-- ('David', 'Wilson', '1995-11-11', 'High School', 1, FALSE, 3), +-- ('Eva', 'Garcia', '1987-09-09', 'Bachelor', 2, TRUE, 4), +-- ('Frank', 'Martinez', '1993-04-04', 'Master', 3, FALSE, 4), +-- ('Grace', 'Hernandez', '1982-06-06', 'PhD', 4, TRUE, 5), +-- ('Henry', 'Lopez', '1991-08-08', 'High School', 5, FALSE, 5), +-- ('Isabella', 'Gonzalez', '1989-02-02', 'Bachelor', 1, TRUE, 1), +-- ('Jack', 'Wilson', '1986-10-10', 'Master', 2, FALSE, 1), +-- ('Liam', 'Anderson', '1994-12-12', 'PhD', 3, TRUE, 2), +-- ('Mia', 'Thomas', '1983-03-03', 'High School', 4, FALSE, 2), +-- ('Noah', 'Taylor', '1990-05-05', 'Bachelor', 5, TRUE, 3), +-- ('Olivia', 'Moore', '1988-07-07', 'Master', 1, FALSE, 3), +-- ('Paul', 'Jackson ', '1992-08-08', 'PhD', 2, TRUE, 4), +-- ('Quinn', 'White', '1985-09-09', 'High School', 3, FALSE, 4), +-- ('Riley', 'Harris', '1993-10-10', 'Bachelor', 4, TRUE, 5), +-- ('Sophia', 'Martin', '1981-11-11', 'Master', 5, FALSE, 5), +-- ('Thomas', 'Thompson', '1990-12-12', 'PhD', 1, TRUE, 1), +-- ('Uma', 'Garcia', '1986-01-01', 'High School', 2, FALSE, 1), +-- ('Victor', 'Martinez', '1994-02-02', 'Bachelor', 3, TRUE, 2), +-- ('Wendy', 'Robinson', '1989-03-03', 'Master', 4, FALSE, 2), +-- ('Xander', 'Clark', '1982-04-04', 'PhD', 5, TRUE, 3), +-- ('Yara', 'Rodriguez', '1991-05-05', 'High School', 1, FALSE, 3), +-- ('Zoe', 'Lewis', '1987-06-06', 'Bachelor', 2, TRUE, 4); +-- +-- -- Insert into medical_card +-- INSERT INTO medical_card (emp_id, height_cm, weight_kg, diseases, blood, gender) VALUES +-- (1, 180, 75, 'Diabet', 'A', 'M'), +-- (2, 165, 60, 'None', 'B', 'F'), +-- (3, 170, 65, 'Infulenza', 'O', 'F'), +-- (4, 175, 80, 'Malaria, Diabet', 'AB', 'M'), +-- (5, 185, 90, 'None', 'AO', 'M'), +-- (6, 160, 55, 'None', 'BO', 'F'), +-- (7, 178, 70, 'None', 'A', 'M'), +-- (8, 172, 68, 'Influenza, Gripp', 'B', 'F'), +-- (9, 169, 62, 'None', 'O', 'F'), +-- (10, 182, 85, 'None', 'AB', 'M'), +-- (11, 177, 72, 'Gripp', 'AO', 'F'), +-- (12, 165, 58, 'None', 'BO', 'F'), +-- (13, 180, 78, 'None', 'A', 'M'), +-- (14, 173, 66, 'None', 'B', 'F'), +-- (15, 168, 64, 'Malaria, Gripp', 'O', 'F'), +-- (16, 184, 88, 'None', 'AB', 'M'), +-- (17, 176, 74, 'None', 'AO', 'F'), +-- (18, 162, 57, 'Typhoid', 'BO', 'F'), +-- (19, 179, 77, 'None', 'A', 'M'), +-- (20, 171, 69, 'None', 'B', 'F'), +-- (21, 167, 63, 'Tyhoid, Malaria', 'O', 'F'), +-- (22, 183, 86, 'None', 'AB', 'M'), +-- (23, 175, 73, 'None', 'AO', 'F'), +-- (24, 164, 59, 'None', 'BO', 'F'), +-- (25, 181, 82, 'None', 'A', 'M'); +-- +-- -- Insert into weapon +-- INSERT INTO weapon (name, type, caliber, rate_of_fire, sighting_range_m) VALUES +-- ('AK-47', 'Assault Rifle', 7.62, 600, 400), +-- ('M4 Carbine', 'Assault Rifle', 5.56, 700, 500), +-- ('M16', 'Assault Rifle', 5.56, 800, 600), +-- ('Glock 17', 'Pistol', 9.0, 1200, 50), +-- ('M249 SAW', 'Light Machine Gun', 5.56, 1000, 800); +-- +-- -- Insert into campaign +-- INSERT INTO campaign (name, customer, earning, spending, execution_status) VALUES +-- ('Operation Alpha', 'Department of Defense', 100762500, 500000, 'STARTED'), +-- ('Operation Bravo', 'NATO', 2000000, 1500000, 'ON_GOING'), +-- ('Operation Charlie 1', 'Private Contractor', 1500860, 800000, 'PENDING'), +-- ('Operation Bravo', 'Private Agency', 2000000, 1500000, 'ON_GOING'), +-- ('Operation Camar', 'Private Contractor', 1500000, 800000, 'PENDING'), +-- ('Operation Bravo', 'NATO', 2000000, 500000, 'ON_GOING'), +-- ('Operation Charlie 2', 'Private Contractor', 33300000, 800000, 'PENDING'), +-- ('Operation Bravo 1', 'NATO', 2000000, 150000, 'ON_GOING'), +-- ('Operation Eagle', 'Private Contractor', 21500000, 800000, 'PENDING'), +-- ('Operation Delta', 'Local Government', '500000', '200000', 'FINISHED'), +-- ('Operation Echo', 'Private Contractor', 750000, 300000, 'NOT_STARTED'); +-- +-- -- Insert into mission +-- INSERT INTO mission (camp_id, start_date_and_time, end_date_and_time, legal_status, departure_location, arrival_location, enemies) VALUES +-- (1, '2023-01-01 08:00:00', '2023-01-10 18:00:00', TRUE, 'Base A', 'Base B', 'Enemy Forces A'), +-- (2, '2023-02-01 09:00:00', '2023-02-15 17:00:00', TRUE, 'Base B', 'Base C', 'Enemy Forces B'), +-- (3, '2023-03-01 10:00:00', '2023-03-20 16:00:00', FALSE, 'Base C', 'Base D', 'Enemy Forces C'), +-- (4, '2023-04-01 11:00:00', '2023-04-25 15:00:00', TRUE, 'Base D', 'Base E', 'Enemy Forces D'), +-- (5, '2023-05-01 12:00:00', '2023-05-30 14:00:00', TRUE, 'Base E', 'Base A', 'Enemy Forces E'); +-- +-- -- Insert into transport +-- INSERT INTO transport (name, type, status) VALUES +-- ('Transport Vehicle 1', 'Truck', 'VERIFIED'), +-- ('Transport Vehicle 2', 'Helicopter', 'NEED_VERIFICATION'), +-- ('Transport Vehicle 3', 'Boat', 'MAINTAINED'), +-- ('Transport Vehicle 4', 'Tank', 'NOT_MAINTAINED'), +-- ('Transport Vehicle 5', 'Drone', 'OUT_OF_USE'); +-- +-- -- Insert into equip_weapon +-- INSERT INTO equip_weapon (equip_id, weapon_id) VALUES +-- (1, 1), +-- (2, 2), +-- (3, 3), +-- (4, 4), +-- (5, 5); +-- +-- -- Insert into missions_transport +-- INSERT INTO missions_transport (miss_id, trans_id) VALUES +-- (1, 1), +-- (2, 2), +-- (3, 3), +-- (4, 4), +-- (5, 5); +-- +-- -- Insert into inspection +-- INSERT INTO inspection (emp_id, trans_id, service_date) VALUES +-- (1, 1, '2023-01-05'), +-- (2, 2, '2023-02-05'), +-- (3, 3, '2023-03-05'), +-- (4, 4, '2023-04-05'), +-- (5, 5, '2023-05-05'); +-- +-- -- Insert into missions_emp +-- INSERT INTO missions_emp (miss_id, emp_id) VALUES +-- (1, 1), +-- (1, 2), +-- (2, 3), +-- (2, 4), +-- (3, 5), +-- (4, 1), +-- (4, 3), +-- (5, 2), +-- (5, 4), +-- (5, 5); +-- Insert into transport (additional entries) +-- select * from mission; +-- INSERT INTO transport (name, type, status) VALUES +-- ('Transport Vehicle 6', 'Armored Personnel Carrier', 'VERIFIED'), +-- ('Transport Vehicle 7', 'Cargo Plane', 'NEED_VERIFICATION'), +-- ('Transport Vehicle 8', 'Submarine', 'MAINTAINED'), +-- ('Transport Vehicle 9', 'ATV', 'NOT_MAINTAINED'), +-- ('Transport Vehicle 10', 'Helicopter', 'OUT_OF_USE'), +-- ('Transport Vehicle 11', 'Military Truck', 'VERIFIED'), +-- ('Transport Vehicle 12', 'Fast Boat', 'MAINTAINED'), +-- ('Transport Vehicle 13', 'Recon Drone', 'NEED_VERIFICATION'), +-- ('Transport Vehicle 14', 'Mobile Command Center', 'OUT_OF_USE'), +-- ('Transport Vehicle 15', 'Transport Helicopter', 'MAINTAINED'); +-- -- Insert into weapon (additional entries) +-- INSERT INTO weapon (name, type, caliber, rate_of_fire, sighting_range_m) VALUES +-- ('FN SCAR', 'Assault Rifle', 7.62, 600, 600), +-- ('MP5', 'Submachine Gun', 9.0, 800, 200), +-- ('Desert Eagle', 'Pistol', 0.50, 300, 50), +-- ('M240', 'Machine Gun', 7.62, 1000, 800), +-- ('RPG-7', 'Rocket Launcher', 40.0, 0, 300), +-- ('Tavor X95', 'Assault Rifle', 5.56, 900, 500), +-- ('M1014', 'Shotgun', 12.0, 1200, 50), +-- ('M14', 'Battle Rifle', 7.62, 700, 800), +-- ('SIG P226', 'Pistol', 9.0, 1000, 50), +-- ('M107', 'Sniper Rifle', 12.7, 1, 2000); +-- +-- -- Insert into mission (additional entries) +-- INSERT INTO mission (camp_id, start_date_and_time, end_date_and_time, legal_status, departure_location, arrival_location, enemies) VALUES +-- (6, '2023-06-01 08:59:00', '2023-06-10 18:30:00', TRUE, 'Base A', 'Base C', 'Enemy Forces F'), +-- (7, '2023-07-01 09:05:00', '2023-07-15 17:00:00', FALSE, 'Base B', 'Base D', 'Enemy Forces G'), +-- (8, '2023-08-01 10:08:00', '2023-08-20 16:00:00', TRUE, 'Base C', 'Base E', 'Enemy Forces H'), +-- (9, '2023-09-01 11:10:00', '2023-09-25 15:20:00', TRUE, 'Base D', 'Base A', 'Enemy Forces I'), +-- (10, '2023-10-01 12:00:00', '2023-10-30 14:00:00', FALSE, 'Base E', 'Base B', 'Enemy Forces J'), +-- (11, '2023-11-01 08:00:00', '2023-11-10 18:03:00', TRUE, 'Base A', 'Base D', 'Enemy Forces K'); \ No newline at end of file diff --git a/Project/part1.sql b/Project/part1.sql index da95c54..d04a461 100644 --- a/Project/part1.sql +++ b/Project/part1.sql @@ -53,10 +53,10 @@ CREATE TYPE force_category AS ENUM ('GF', 'NAVY', 'AF'); CREATE TABLE position ( pos_id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, + name VARCHAR(255) NOT NULL, salary_rub NUMERIC(10, 2) NOT NULL CHECK (salary_rub >= 50000), rank VARCHAR(100), - equip_id INTEGER REFERENCES equipment ON DELETE SET NULL, --foreign key + equip_id INTEGER REFERENCES equipment ON DELETE SET NULL, --foreign key forces force_category ); diff --git a/Project/part2.sql b/Project/part2.sql new file mode 100644 index 0000000..066bc64 --- /dev/null +++ b/Project/part2.sql @@ -0,0 +1,253 @@ +-- Data normalization +-- Step 1: `employee_base` +CREATE TABLE employee_base ( + emp_id INTEGER NOT NULL REFERENCES employee ON DELETE CASCADE, + base_id INTEGER NOT NULL REFERENCES base ON DELETE SET NULL, + PRIMARY KEY (emp_id, base_id) +); +-- Step 2: Migrate existing data (if `base_id` column in `employee` is populated).. миграция данных из employee +INSERT INTO employee_base (emp_id, base_id) +SELECT emp_id, base_id +FROM employee +WHERE base_id IS NOT NULL; +-- Step 3: Remove the `base_id` column from the `employee` table +ALTER TABLE employee +DROP COLUMN base_id; +-- проверка если миграция успешна пошла +SELECT * FROM employee +LIMIT 2; + +-- VIEWS +CREATE VIEW employee_details AS -- вернет список сотрудников вместе с информацией о связанных с ними должностях (имя, зарплата и т.д.). +SELECT + e.emp_id, + e.name AS employee_name, + e.surname, + e.date_of_birth, + e.education, + e.hiring_date, + p.name AS position_name, + p.salary_rub, + p.rank +FROM employee e +JOIN position p ON e.pos_id = p.pos_id; +--bпровепка +SELECT * FROM employee_details +WHERE salary_rub > 80000 +ORDER BY salary_rub +DESC +LIMIT 5; + +CREATE VIEW mission_summary AS --вернет краткую информацию о каждой миссии, связанном с ней названии кампании, клиенте и используемом транспорте (если таковой имеется). +SELECT + m.miss_id, + m.start_date_and_time, + m.end_date_and_time, + c.name AS campaign_name, + c.customer, + t.name AS transport_name +FROM + mission m +JOIN + campaign c ON m.camp_id = c.camp_id +LEFT JOIN + missions_transport mt ON m.miss_id = mt.miss_id +LEFT JOIN + transport t ON mt.trans_id = t.trans_id; +-- ПРОВЕРКА +SELECT * FROM mission_summary +LIMIT 5; + +-- MATERIALIZED VIEW +CREATE MATERIALIZED VIEW campaign_profit AS --сохраняет результат запроса, который вычисляет прибыль от каждой кампании (доходы - расходы). +SELECT + camp_id, + name, + customer, + earning, + spending, + earning - spending AS profit +FROM campaign; +--обновляет данные в материализованном представлении campaign_profit. +REFRESH MATERIALIZED VIEW campaign_profit; +-- Проверка +SELECT * FROM campaign_profit +ORDER BY profit +DESC +LIMIT 2; + +-- CTE +WITH employee_health_summary AS ( -- извлекает основные медицинские данные (рост, вес, группа крови) из таблиц employee и medical_card, объединяя их с помощью emp_id. + SELECT + e.emp_id, + e.name, + e.surname, + m.height_cm, + m.weight_kg, + m.diseases, + m.gender, + m.blood + FROM employee e + JOIN medical_card m ON e.emp_id = m.emp_id +) +SELECT + name, + surname, + height_cm, + weight_kg, + blood, + diseases, + gender, + CASE + WHEN height_cm < 160 THEN 'Short' + WHEN height_cm BETWEEN 160 AND 180 THEN 'Average' + WHEN height_cm > 180 THEN 'Tall' + END AS height_category, + CASE + WHEN weight_kg < 60 THEN 'Underweight' + WHEN weight_kg BETWEEN 60 AND 150 THEN 'Normal weight' + WHEN weight_kg > 150 THEN 'Overweight' + END AS weight_category +FROM employee_health_summary; + +WITH transport_availability AS ( -- возвращает список названий транспортных средств со статусом их доступности + SELECT + t.trans_id, + t.name AS transport_name, + t.status, + m.start_date_and_time, + CURRENT_TIMESTAMP AS "current_time" + FROM transport t + LEFT JOIN missions_transport mt ON t.trans_id = mt.trans_id + LEFT JOIN mission m ON mt.miss_id = m.miss_id +) +SELECT + transport_name, + CASE + WHEN status = 'OUT_OF_USE' OR status = 'NOT_MAINTAINED' THEN 'Not Available' + WHEN start_date_and_time > CURRENT_TIMESTAMP THEN 'Available' + ELSE 'In Use' + END AS availability_status +FROM transport_availability; + +-- TEMP_TABLE +CREATE TEMPORARY TABLE temp_employee_availability AS -- Временная таблица для проверки доступности сотрудников в зависимости от их назначенных заданий. Может использоваться для просмотра того, кто может быть назначен на новое задание. +SELECT + e.emp_id, + e.name AS employee_name, + e.surname, + CASE + WHEN m.miss_id IS NULL THEN 'Available' + ELSE 'Not Available' + END AS availability_status +FROM employee e +LEFT JOIN missions_emp me ON e.emp_id = me.emp_id +LEFT JOIN mission m ON me.miss_id = m.miss_id +WHERE m.end_date_and_time <= CURRENT_TIMESTAMP OR m.miss_id IS NULL; +-- проверка +SELECT * FROM temp_employee_availability +LIMIT 5; + +-- RECURSIVE +WITH RECURSIVE employee_mission_hierarchy AS ( --извлекает все задания, назначенные конкретному сотруднику (начиная с emp_id сотрудника = 1). + -- Base case: Start from a specific employee + SELECT + e.emp_id, + e.name AS employee_name, + e.surname AS employee_surname, + m.miss_id, + m.start_date_and_time, + m.end_date_and_time + FROM employee e + JOIN missions_emp me ON e.emp_id = me.emp_id + JOIN mission m ON me.miss_id = m.miss_id + WHERE e.emp_id = 1 -- Start with an employee (e.g., employee_id = 1) + UNION ALL + -- Recursive case: Find all missions this employee was assigned to + SELECT + e.emp_id, + e.name AS employee_name, + e.surname AS employee_surname, + m.miss_id, + m.start_date_and_time, + m.end_date_and_time + FROM employee e + JOIN missions_emp me ON e.emp_id = me.emp_id + JOIN mission m ON me.miss_id = m.miss_id + JOIN employee_mission_hierarchy emh ON m.miss_id = emh.miss_id -- Recursively find missions +) +SELECT + employee_name, + employee_surname, + miss_id, + start_date_and_time, + end_date_and_time +FROM employee_mission_hierarchy; + +WITH RECURSIVE weapon_mission_path AS ( -- отслеживает использование определенного оружия (начиная с weapon_id = 1) в миссиях. + -- Base case: Start from a specific weapon + SELECT + w.weapon_id, + w.name AS weapon_name, + m.miss_id, + m.start_date_and_time, + m.end_date_and_time + FROM weapon w + JOIN equip_weapon ew ON w.weapon_id = ew.weapon_id + JOIN missions_emp me ON ew.equip_id = me.emp_id + JOIN mission m ON me.miss_id = m.miss_id + WHERE w.weapon_id = 1 -- For example, start from a specific weapon + UNION ALL + -- Recursive case: Find the next mission where this weapon is used + SELECT + w.weapon_id, + w.name AS weapon_name, + m.miss_id, + m.start_date_and_time, + m.end_date_and_time + FROM weapon w + JOIN equip_weapon ew ON w.weapon_id = ew.weapon_id + JOIN missions_emp me ON ew.equip_id = me.emp_id + JOIN mission m ON me.miss_id = m.miss_id + JOIN weapon_mission_path wmp ON m.miss_id = wmp.miss_id -- Recursively find related missions +) +SELECT + weapon_name, + miss_id, + start_date_and_time, + end_date_and_time +FROM weapon_mission_path; + +-- INDEXES +CREATE INDEX idx_employee_base_emp_id ON employee_base USING HASH(emp_id); --hash index +CREATE INDEX idx_employee_base_base_id ON employee_base USING HASH(base_id); +CREATE INDEX idx_employee_pos_id ON employee USING HASH(pos_id); +CREATE INDEX idx_employee_emp_id ON employee USING HASH(emp_id); +CREATE INDEX idx_employee_emp_name ON employee(name); --b-tree index by default +CREATE INDEX idx_employee_emp_surname ON employee(surname); +CREATE INDEX idx_position_pos_id ON position USING HASH(pos_id); +CREATE INDEX idx_mission_camp_id ON mission USING HASH(camp_id); +CREATE INDEX idx_mission_start_date ON mission(start_date_and_time); +CREATE INDEX idx_mission_end_date ON mission(end_date_and_time); +CREATE INDEX idx_mission_miss_id ON mission USING HASH(miss_id); +CREATE INDEX idx_campaign_camp_id ON campaign USING HASH(camp_id); +CREATE INDEX idx_missions_transport_miss_id ON missions_transport USING HASH(miss_id); +CREATE INDEX idx_transport_trans_id ON transport USING HASH(trans_id); +CREATE INDEX idx_medical_card_emp_id ON medical_card USING HASH(emp_id); +CREATE INDEX idx_equip_weapon_weapon_id ON equip_weapon USING HASH(weapon_id); +CREATE INDEX idx_employee_weapon_name ON weapon(name); + +--EXPLAIN ANALYZE +EXPLAIN ANALYZE SELECT * FROM employee_details +WHERE salary_rub > 80000 +ORDER BY salary_rub +DESC +LIMIT 5; + +-- index usage per table//отслеживает использование определенного оружия (начиная с weapon_id = 1) в миссиях. +SELECT relname , indexrelname , idx_scan , idx_tup_read , idx_tup_fetch +FROM pg_stat_user_indexes +WHERE schemaname = 'public' and + relname in ('campaign'); + +VACUUM;