Базы данных | Задания для практического занятия 11
Создание ИНДЕКСА в SQL.
Вариант 1: Платформа электронной коммерции
Сценарий: Вы работаете над базой данных для платформы электронной коммерции. Производительность критически важна для пользовательского опыта, особенно при поиске товаров и просмотре истории заказов. Вам необходимо добавить соответствующие индексы для оптимизации часто выполняемых запросов.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS OrderItems;
DROP TABLE IF EXISTS Orders;
DROP TABLE IF EXISTS Products;
-- Создаем таблицу Products
CREATE TABLE Products (
product_id SERIAL PRIMARY KEY,
sku VARCHAR(50) UNIQUE NOT NULL, -- Артикул (единица складского учета)
name VARCHAR(200) NOT NULL,
category VARCHAR(100),
price NUMERIC(10, 2) CHECK (price >= 0),
stock_quantity INT DEFAULT 0 CHECK (stock_quantity >= 0),
date_added DATE DEFAULT CURRENT_DATE
);
-- Создаем таблицу Orders
CREATE TABLE Orders (
order_id SERIAL PRIMARY KEY,
customer_id INT NOT NULL, -- Предполагается, что отдельная таблица Customers существует где-то еще
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(20) DEFAULT 'pending', -- например, 'pending' (в ожидании), 'processing' (в обработке), 'shipped' (отправлен), 'delivered' (доставлен), 'cancelled' (отменен)
total_amount NUMERIC(12, 2)
);
-- Создаем таблицу OrderItems (связывающую Orders и Products)
CREATE TABLE OrderItems (
order_item_id SERIAL PRIMARY KEY,
order_id INT NOT NULL REFERENCES Orders(order_id),
product_id INT NOT NULL REFERENCES Products(product_id),
quantity INT NOT NULL CHECK (quantity > 0),
unit_price NUMERIC(10, 2) NOT NULL -- Цена на момент заказа
);
-- Вставляем примерные данные
INSERT INTO Products (sku, name, category, price, stock_quantity, date_added) VALUES
('BK-ADV-01', 'Advanced SQL Guide', 'Books', 49.99, 150, '2023-01-10'),
('EL-LAP-X1', 'UltraSlim Laptop X1', 'Electronics', 1299.00, 50, '2023-02-20'),
('KT-MIX-S2', 'Super Mixer Pro', 'Home Goods', 89.50, 200, '2023-03-15'),
('BK-PYT-BEG', 'Python for Beginners', 'Books', 29.99, 300, '2023-04-01'),
('EL-CAM-Z5', 'Digital Camera Z5', 'Electronics', 450.00, 75, '2023-05-10');
INSERT INTO Orders (customer_id, order_date, status, total_amount) VALUES
(101, '2023-11-01 10:00:00', 'shipped', 179.49),
(102, '2023-11-05 14:30:00', 'processing', 1299.00),
(101, '2023-11-10 09:15:00', 'pending', 79.98),
(103, '2023-11-12 16:45:00', 'delivered', 450.00),
(104, '2023-11-15 11:00:00', 'processing', 89.50);
INSERT INTO OrderItems (order_id, product_id, quantity, unit_price) VALUES
(1, 1, 1, 49.99), (1, 4, 1, 29.99), (1, 3, 1, 89.50), -- Ошибка исправлена: итоговая сумма соответствует заказу 1
(2, 2, 1, 1299.00),
(3, 4, 2, 29.99), (3, 1, 1, 49.99), -- Требуются скорректированные количества/цены, если итоговая сумма должна быть ровно 79.98. Предположим, что итоговая сумма заказа была приблизительной.
(4, 5, 1, 450.00),
(5, 3, 1, 89.50);
Задания:
- Базовый индекс: Создайте индекс для таблицы
Products
, чтобы ускорить поиск поname
товара. - Индекс по внешнему ключу: Создайте индекс для таблицы
Orders
, чтобы быстро находить все заказы, сделанные определеннымcustomer_id
. - Составной (многоколоночный) индекс: Создайте составной индекс для таблицы
Products
для оптимизации запросов, которые фильтруют поcategory
, а затем сортируют или фильтруют поprice
. Убедитесь, чтоcategory
является ведущим столбцом. - Индекс по внешнему ключу (связь многие-ко-многим): Создайте индекс для таблицы
OrderItems
, чтобы эффективно находить позиции, принадлежащие определенномуorder_id
. - Индекс по внешнему ключу (связь многие-ко-многим): Создайте еще один индекс для таблицы
OrderItems
, чтобы эффективно находить все заказы, включающие определенныйproduct_id
. - Перестроение индексов таблицы: Перестройте все существующие индексы таблицы
Products
, чтобы оптимизировать их структуру после возможных изменений данных. - Частичный индекс: Создайте частичный индекс для таблицы
Orders
по столбцуorder_date
, но включите в него только заказы соstatus
‘processing’ или ‘pending’. Это поможет оптимизировать запросы для поиска активных, неотправленных заказов. - Индекс по выражению: Создайте индекс для таблицы
Products
на основе значения столбцаname
в нижнем регистре, чтобы облегчить поиск без учета регистра. - Вывод списка индексов: Используйте запрос к системному каталогу (
pg_indexes
), чтобы вывести список всех индексов, определенных для таблицыOrderItems
. - Удаление индекса: Удалите индекс по выражению (
LOWER(name)
дляProducts
), созданный в шаге 8.
Вариант 2: Система управления библиотекой
Сценарий: Вы проектируете базу данных для библиотечной системы. Эффективный поиск книг, авторов и отслеживание выданных книг имеет решающее значение для библиотекарей и читателей. Вам необходимо реализовать индексы для оптимизации этих операций.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS BorrowingRecords;
DROP TABLE IF EXISTS Books;
DROP TABLE IF EXISTS Authors;
-- Создаем таблицу Authors (Авторы)
CREATE TABLE Authors (
author_id SERIAL PRIMARY KEY,
first_name VARCHAR(100),
last_name VARCHAR(100) NOT NULL,
birth_year INT
);
-- Создаем таблицу Books (Книги)
CREATE TABLE Books (
book_id SERIAL PRIMARY KEY,
isbn VARCHAR(17) UNIQUE NOT NULL, -- Международный стандартный книжный номер
title VARCHAR(255) NOT NULL,
publication_year INT,
genre VARCHAR(50),
author_id INT REFERENCES Authors(author_id),
copies_available INT DEFAULT 1 CHECK (copies_available >= 0)
);
-- Создаем таблицу BorrowingRecords (Записи о выдаче)
CREATE TABLE BorrowingRecords (
record_id SERIAL PRIMARY KEY,
book_id INT NOT NULL REFERENCES Books(book_id),
member_id INT NOT NULL, -- Предполагается, что отдельная таблица Members (Читатели) существует где-то еще
borrow_date DATE NOT NULL DEFAULT CURRENT_DATE,
due_date DATE NOT NULL,
return_date DATE -- NULL, если книга еще не возвращена
);
-- Вставляем пример данных
INSERT INTO Authors (first_name, last_name, birth_year) VALUES
('George', 'Orwell', 1903),
('Jane', 'Austen', 1775),
('Isaac', 'Asimov', 1920),
('J.R.R.', 'Tolkien', 1892);
INSERT INTO Books (isbn, title, publication_year, genre, author_id, copies_available) VALUES
('978-0451524935', 'Nineteen Eighty-Four', 1949, 'Dystopian', 1, 3),
('978-0141439518', 'Pride and Prejudice', 1813, 'Romance', 2, 5),
('978-0553293378', 'Foundation', 1951, 'Science Fiction', 3, 2),
('978-0547928227', 'The Hobbit', 1937, 'Fantasy', 4, 4),
('978-0451524900', 'Animal Farm', 1945, 'Allegory', 1, 3);
INSERT INTO BorrowingRecords (book_id, member_id, borrow_date, due_date, return_date) VALUES
(1, 501, '2024-03-01', '2024-03-15', '2024-03-14'),
(3, 502, '2024-03-05', '2024-03-19', NULL), -- Не возвращена
(2, 501, '2024-03-10', '2024-03-24', NULL), -- Не возвращена
(5, 503, '2024-03-12', '2024-03-26', '2024-03-20'),
(1, 502, '2024-03-15', '2024-03-29', NULL); -- Не возвращена
Задания:
- Базовый индекс: Создайте индекс для таблицы
Authors
, чтобы ускорить поиск поlast_name
(фамилии) автора. - Базовый индекс: Создайте индекс для таблицы
Books
, чтобы обеспечить более быстрый поиск поtitle
(названию). - Объяснение уникального индекса: Таблица
Books
имеет ограничениеUNIQUE
для столбцаisbn
. Объясните, какие последствия это имеет для индексирования этого столбца. - Индекс по внешнему ключу: Создайте индекс для таблицы
Books
, чтобы оптимизировать запросы, соединяющие (JOIN
) таблицыBooks
иAuthors
(т.е. проиндексируйте внешний ключauthor_id
). - Составной индекс: Создайте составной (многоколоночный) индекс для таблицы
Books
, чтобы повысить производительность запросов, которые ищут книги по определенномуgenre
(жанру) иpublication_year
(году публикации).genre
должен быть первым столбцом в индексе. - Индекс по внешнему ключу (Таблица отслеживания): Создайте индекс для таблицы
BorrowingRecords
, чтобы быстро находить все записи о выдаче, связанные с определеннымbook_id
. - Индекс по внешнему ключу (Таблица отслеживания): Создайте индекс для таблицы
BorrowingRecords
, чтобы быстро находить все книги, взятые определеннымmember_id
(читателем). - Частичный индекс: Создайте частичный индекс для таблицы
BorrowingRecords
, охватывающий столбецdue_date
, но только для тех записей, гдеreturn_date
равенNULL
. Это поможет эффективно находить просроченные книги или текущие (невозвращенные) выдачи. - Индекс по выражению: Создайте индекс для таблицы
Books
на основе столбцаpublication_year
, но сохраняющий только десятилетие (например, 1950 для 1951, 1810 для 1813). Используйте целочисленное деление или другое подходящее выражение. Это может оптимизировать поиск по десятилетиям.CREATE INDEX idx_books_pub_decade ON Books (((publication_year / 10) * 10));
- Просмотр и удаление индекса: Используйте мета-команду
\d Books
(в psql или в SQL-панели pgAdmin Query Tool), чтобы просмотреть индексы таблицыBooks
. Затем удалите индекс для столбцаtitle
, созданный в шаге 2.
Вариант 3: Инструмент управления проектами
Сценарий: Вы являетесь администратором базы данных для приложения по управлению проектами. Пользователи часто ищут проекты, фильтруют задачи по статусу, исполнителю и приоритету, а также просматривают списки задач. Оптимизация этих запросов с помощью индексов важна для отзывчивости приложения.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS TaskAssignments; -- Скорректировано для ясности
DROP TABLE IF EXISTS Tasks;
DROP TABLE IF EXISTS Projects;
DROP TABLE IF EXISTS Users;
-- Создаем таблицу Users
CREATE TABLE Users (
user_id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
full_name VARCHAR(150)
);
-- Создаем таблицу Projects
CREATE TABLE Projects (
project_id SERIAL PRIMARY KEY,
project_code VARCHAR(20) UNIQUE NOT NULL, -- например, 'ALPHA', 'OMEGA-2'
name VARCHAR(200) NOT NULL,
start_date DATE,
deadline DATE,
status VARCHAR(20) DEFAULT 'planning' -- 'planning' (планирование), 'active' (активен), 'completed' (завершен), 'on_hold' (приостановлен)
);
-- Создаем таблицу Tasks
CREATE TABLE Tasks (
task_id SERIAL PRIMARY KEY,
project_id INT NOT NULL REFERENCES Projects(project_id),
title VARCHAR(255) NOT NULL,
description TEXT,
priority INT DEFAULT 3, -- например, 1 (Высокий) до 5 (Низкий)
status VARCHAR(20) DEFAULT 'todo', -- 'todo' (сделать), 'in_progress' (в работе), 'review' (на проверке), 'done' (выполнено), 'blocked' (заблокировано)
due_date DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Создаем таблицу TaskAssignments (связывает Задачи и Пользователей)
CREATE TABLE TaskAssignments (
assignment_id SERIAL PRIMARY KEY,
task_id INT NOT NULL REFERENCES Tasks(task_id) ON DELETE CASCADE,
user_id INT NOT NULL REFERENCES Users(user_id),
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (task_id, user_id) -- Гарантирует, что пользователь не назначен на одну и ту же задачу дважды
);
-- Вставляем примерные данные
INSERT INTO Users (username, email, full_name) VALUES
('jdoe', 'john.doe@company.com', 'John Doe'),
('asmith', 'alice.smith@company.com', 'Alice Smith'),
('bwhite', 'bob.white@company.com', 'Bob White');
INSERT INTO Projects (project_code, name, start_date, deadline, status) VALUES
('PHOENIX', 'Project Phoenix Rebirth', '2024-01-15', '2024-07-31', 'active'),
('NEBULA', 'Nebula Cloud Migration', '2024-03-01', '2024-12-31', 'planning'),
('TITAN', 'Titan Platform Upgrade', '2023-11-01', '2024-05-31', 'active');
INSERT INTO Tasks (project_id, title, priority, status, due_date) VALUES
(1, 'Define Core Modules', 1, 'done', '2024-02-15'),
(1, 'Develop UI Mockups', 2, 'in_progress', '2024-04-30'),
(1, 'Setup Development Environment', 3, 'todo', '2024-04-10'),
(3, 'Analyze Current Platform', 1, 'in_progress', '2024-04-20'),
(3, 'Draft Upgrade Plan', 2, 'review', '2024-05-05'),
(2, 'Gather Cloud Requirements', 1, 'todo', '2024-04-15');
INSERT INTO TaskAssignments (task_id, user_id) VALUES
(1, 1), -- Задача 1 назначена John Doe
(2, 2), -- Задача 2 назначена Alice Smith
(3, 1), -- Задача 3 назначена John Doe
(4, 3), -- Задача 4 назначена Bob White
(5, 2), -- Задача 5 назначена Alice Smith
(4, 1); -- Задача 4 также назначена John Doe
Задания:
- Базовый индекс: Создайте индекс для таблицы
Projects
, чтобы быстро находить проекты по ихstatus
. - Объяснение уникального индекса: Таблица
Users
имеет ограниченияUNIQUE
на столбцыusername
иemail
. Что это означает относительно автоматически создаваемых индексов для этих столбцов? - Индекс для внешнего ключа: Создайте индекс для таблицы
Tasks
, чтобы эффективно извлекать все задачи, принадлежащие определенномуproject_id
. - Индекс для внешнего ключа (Таблица назначений): Создайте индекс для таблицы
TaskAssignments
, чтобы быстро находить все назначения (и, следовательно, задачи) для заданногоuser_id
. - Индекс для внешнего ключа (Таблица назначений): Создайте индекс для таблицы
TaskAssignments
, чтобы быстро находить всех исполнителей для заданнойtask_id
. - Составной индекс: Создайте составной (многоколоночный) индекс для таблицы
Tasks
, чтобы оптимизировать фильтрацию задач сначала поproject_id
, а затем поstatus
. - Составной индекс (Назначения): Создайте составной индекс для таблицы
TaskAssignments
, чтобы оптимизировать запросы, ищущие задачи, назначенные конкретному пользователю (user_id
), упорядоченные по временной меткеassigned_at
. - Частичный индекс: Создайте частичный индекс для таблицы
Tasks
, охватывающий столбецdue_date
, но только для задач, чейstatus
не равен ‘done’ или ‘blocked’. Это поможет находить предстоящие сроки выполнения для активных задач. - Индекс по выражению: Создайте индекс для таблицы
Tasks
на основе функцииUPPER()
, примененной к столбцуtitle
, для поддержки поиска по названию без учета регистра. - Вывод списка и удаление индекса: Используйте системное представление
pg_indexes
, чтобы вывести список всех индексов таблицыTasks
. Определите индекс, созданный на шаге 9 (UPPER(title)
), а затем удалите его.
Вариант 4: Платформа онлайн-форума
Сценарий: Вы отвечаете за производительность базы данных растущего онлайн-форума. Пользователям необходимо быстро находить сообщения по теме, автору и искать по содержимому. Модераторам также нужны эффективные способы для просмотра сообщений, на которые поступили жалобы. Индексирование является ключом к поддержанию хорошего пользовательского опыта.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS Posts;
DROP TABLE IF EXISTS Topics;
DROP TABLE IF EXISTS Users;
-- Создаем таблицу Users
CREATE TABLE Users (
user_id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
join_date DATE DEFAULT CURRENT_DATE,
post_count INT DEFAULT 0
);
-- Создаем таблицу Topics
CREATE TABLE Topics (
topic_id SERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
description TEXT,
created_by INT REFERENCES Users(user_id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Создаем таблицу Posts
CREATE TABLE Posts (
post_id SERIAL PRIMARY KEY,
topic_id INT NOT NULL REFERENCES Topics(topic_id),
user_id INT NOT NULL REFERENCES Users(user_id),
title VARCHAR(255) NOT NULL,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_reported BOOLEAN DEFAULT FALSE,
last_edited_at TIMESTAMP NULL
);
-- Вставляем пример данных
INSERT INTO Users (username, email, join_date) VALUES
('forumFan', 'fan@example.com', '2023-01-15'),
('topicMaster', 'master@example.com', '2023-02-20'),
('postGuru', 'guru@example.com', '2023-03-10');
INSERT INTO Topics (name, description, created_by, created_at) VALUES
('Database Performance', 'Discussions about optimizing database queries.', 1, '2023-05-01 10:00:00'),
('Web Development Trends', 'Latest news and trends in web development.', 2, '2023-06-15 14:30:00'),
('General Chat', 'Off-topic discussions.', 1, '2023-01-20 09:00:00');
INSERT INTO Posts (topic_id, user_id, title, content, created_at, is_reported) VALUES
(1, 1, 'Indexing Strategies in PostgreSQL', 'Let us discuss B-Trees, Hash indexes...', '2023-05-02 11:00:00', FALSE),
(1, 2, 'Re: Indexing Strategies', 'Partial indexes are great for specific subsets!', '2023-05-03 09:20:00', FALSE),
(2, 3, 'Rise of Serverless', 'Is serverless the future?', '2023-06-16 10:00:00', FALSE),
(2, 1, 'Re: Rise of Serverless', 'Depends on the use case...', '2023-06-17 15:00:00', TRUE), -- Сообщение, на которое поступила жалоба
(3, 2, 'Favorite weekend hobbies?', 'What do you do on weekends?', '2023-01-21 11:00:00', FALSE),
(1, 3, 'Full-Text Search Tips', 'Using tsvector and tsquery...', '2023-07-01 14:00:00', FALSE);
Задачи:
- Базовый индекс: Создайте индекс для таблицы
Users
, чтобы ускорить поиск поjoin_date
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Posts
, чтобы эффективно находить все сообщения, сделанные конкретнымuser_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Posts
, чтобы эффективно извлекать все сообщения, принадлежащие конкретномуtopic_id
. - Многоколоночный индекс: Создайте многоколоночный индекс для таблицы
Posts
для оптимизации просмотра сообщений в рамкахtopic_id
, упорядоченных по временной меткеcreated_at
. Убедитесь, чтоtopic_id
является ведущей колонкой. - Частичный индекс: Создайте частичный индекс для таблицы
Posts
по столбцуcreated_at
, включающий только те записи, гдеis_reported
имеет значение TRUE. Это для очереди просмотра модераторов. - Индекс по выражению: Создайте индекс для таблицы
Posts
, используя функциюLOWER()
для столбцаtitle
, чтобы обеспечить быстрый поиск по заголовку сообщения без учета регистра. - Пояснение к уникальному индексу: Таблица
Topics
имеет ограничениеUNIQUE
для столбцаname
. Что это ограничение подразумевает для индексирования этого столбца? Нужен ли отдельныйCREATE INDEX
для базового поиска поname
? - Список индексов: Используйте мета-команду
\d Posts
(в psql или панели SQL Query Tool в pgAdmin), чтобы отобразить все индексы, связанные с таблицейPosts
. - Переиндексация таблицы: Перестройте все индексы для таблицы
Posts
, предполагая, что могли происходить частые правки и удаления. - Удаление индекса: Удалите индекс по выражению, созданный на шаге 6 (
LOWER(title)
для таблицыPosts
).
Вариант 5: Система бронирования авиабилетов
Сценарий: Вы разрабатываете бэкенд базы данных для системы бронирования авиабилетов. Производительность критически важна для поиска доступных рейсов между пунктами назначения, проверки деталей конкретных рейсов и управления бронированиями пассажиров. Индексы необходимы для обеспечения быстрого ответа на запросы.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS Bookings;
DROP TABLE IF EXISTS Flights;
DROP TABLE IF EXISTS Airlines;
DROP TABLE IF EXISTS Airports;
-- Создаем таблицу Airports (Аэропорты)
CREATE TABLE Airports (
airport_code CHAR(3) PRIMARY KEY, -- например, 'JFK', 'LAX'
name VARCHAR(100) NOT NULL,
city VARCHAR(100),
country VARCHAR(100)
);
-- Создаем таблицу Airlines (Авиакомпании)
CREATE TABLE Airlines (
airline_id SERIAL PRIMARY KEY,
iata_code VARCHAR(3) UNIQUE NOT NULL, -- например, 'AA', 'UA'
name VARCHAR(100) NOT NULL
);
-- Создаем таблицу Flights (Рейсы)
CREATE TABLE Flights (
flight_id SERIAL PRIMARY KEY,
flight_number VARCHAR(10) NOT NULL,
airline_id INT NOT NULL REFERENCES Airlines(airline_id),
departure_airport CHAR(3) NOT NULL REFERENCES Airports(airport_code),
arrival_airport CHAR(3) NOT NULL REFERENCES Airports(airport_code),
departure_time TIMESTAMP WITH TIME ZONE NOT NULL,
arrival_time TIMESTAMP WITH TIME ZONE NOT NULL,
base_price NUMERIC(10, 2) CHECK (base_price >= 0),
total_seats INT,
seats_booked INT DEFAULT 0
);
-- Создаем таблицу Bookings (Бронирования)
CREATE TABLE Bookings (
booking_id SERIAL PRIMARY KEY,
flight_id INT NOT NULL REFERENCES Flights(flight_id),
passenger_name VARCHAR(150) NOT NULL,
seat_number VARCHAR(4), -- например, '12A', '30F'
booking_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(20) DEFAULT 'confirmed' -- 'confirmed' (подтверждено), 'cancelled' (отменено)
);
-- Вставляем примерные данные
INSERT INTO Airports (airport_code, name, city, country) VALUES
('JFK', 'John F. Kennedy International', 'New York', 'USA'),
('LAX', 'Los Angeles International', 'Los Angeles', 'USA'),
('LHR', 'London Heathrow', 'London', 'UK'),
('CDG', 'Charles de Gaulle', 'Paris', 'France');
INSERT INTO Airlines (iata_code, name) VALUES
('AA', 'American Airlines'),
('BA', 'British Airways'),
('DL', 'Delta Air Lines');
INSERT INTO Flights (flight_number, airline_id, departure_airport, arrival_airport, departure_time, arrival_time, base_price, total_seats) VALUES
('AA101', 1, 'JFK', 'LAX', '2024-07-15 09:00:00-04', '2024-07-15 12:00:00-07', 350.00, 180),
('BA286', 2, 'LHR', 'LAX', '2024-07-15 11:30:00+01', '2024-07-15 14:45:00-07', 680.00, 220),
('DL405', 3, 'JFK', 'CDG', '2024-07-16 18:00:00-04', '2024-07-17 07:30:00+02', 550.00, 200),
('AA202', 1, 'LAX', 'JFK', '2024-07-18 14:00:00-07', '2024-07-18 22:30:00-04', 360.00, 180);
INSERT INTO Bookings (flight_id, passenger_name, seat_number, status) VALUES
(1, 'Alice Wonderland', '15A', 'confirmed'),
(1, 'Bob The Builder', '15B', 'confirmed'),
(2, 'Charlie Chaplin', '22F', 'confirmed'),
(3, 'Diana Prince', '10C', 'cancelled'),
(4, 'Edward Scissorhands', '5A', 'confirmed');
-- Обновляем seats_booked на основе вставленных данных
UPDATE Flights SET seats_booked = 2 WHERE flight_id = 1;
UPDATE Flights SET seats_booked = 1 WHERE flight_id = 2;
UPDATE Flights SET seats_booked = 0 WHERE flight_id = 3; -- Отмененное бронирование не учитывается
UPDATE Flights SET seats_booked = 1 WHERE flight_id = 4;
Задания:
- Базовый индекс: Создайте индекс для таблицы
Airports
для быстрого поиска аэропортов, расположенных в определенномcity
. - Базовый индекс: Создайте индекс для таблицы
Flights
для быстрого поиска рейсов по ихflight_number
. - Составной индекс (основной поиск): Создайте ключевой составной индекс для таблицы
Flights
для оптимизации самого частого поиска: нахождения рейсов поdeparture_airport
,arrival_airport
иdeparture_time
. Убедитесь, что столбцы указаны в этом порядке. - Индекс по внешнему ключу: Создайте индекс для таблицы
Flights
для эффективного поиска всех рейсов, выполняемых определеннымairline_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Bookings
для быстрого извлечения всех бронирований, связанных с определеннымflight_id
. - Частичный индекс (доступность): Создайте частичный индекс для таблицы
Flights
, охватывающийdeparture_time
, но только для тех рейсов, гдеseats_booked < total_seats
. Это оптимизирует поиск рейсов, на которых еще есть свободные места. - Индекс по выражению (поиск по дате): Создайте индекс для таблицы
Flights
на основе части даты столбцаdeparture_time
(используйтеCAST(departure_time AS DATE)
илиDATE(departure_time)
). Это ускорит запросы для поиска всех рейсов, вылетающих в определенную дату, независимо от времени. - Уникальный индекс (назначение мест): Создайте уникальный индекс для таблицы
Bookings
, охватывающийflight_id
иseat_number
, чтобы предотвратить назначение одного и того же места на одном и том же рейсе нескольким пассажирам. Разрешите значения NULL вseat_number
, если назначение места может быть необязательным на начальном этапе (хотя текущая схема подразумевает, что оно обычно присутствует). - Вывод списка индексов: Используйте системное представление каталога
pg_indexes
, чтобы вывести список всех индексов, определенных в настоящее время для таблицыFlights
, показав их имена и определения. - Удаление индекса: Удалите базовый индекс, созданный в шаге 2, для столбца
flight_number
таблицыFlights
.
Вариант 6: Система регистрации на курсы университета
Сценарий: Вы управляете базой данных системы регистрации на курсы университета. Студентам, преподавателям и администраторам требуется быстрый доступ к информации о курсах, расписаниям студентов, спискам студентов в курсах и спискам кафедр. Эффективное индексирование жизненно важно для отзывчивости системы, особенно в периоды пиковой нагрузки при регистрации.
Схема базы данных:
-- Удалить таблицы, если они существуют, для чистого старта
DROP TABLE IF EXISTS Enrollments;
DROP TABLE IF EXISTS Courses;
DROP TABLE IF EXISTS Departments;
DROP TABLE IF EXISTS Students;
-- Create the Students table
CREATE TABLE Students (
student_id SERIAL PRIMARY KEY,
student_uid VARCHAR(10) UNIQUE NOT NULL, -- Идентификатор университета, например, 'u1234567'
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
enrollment_year INT,
major VARCHAR(100)
);
-- Create the Departments table
CREATE TABLE Departments (
dept_id SERIAL PRIMARY KEY,
dept_code VARCHAR(10) UNIQUE NOT NULL, -- например, 'CSCI', 'MATH'
name VARCHAR(150) NOT NULL
);
-- Create the Courses table
CREATE TABLE Courses (
course_id SERIAL PRIMARY KEY,
course_code VARCHAR(10) NOT NULL, -- например, '101', '315W'
title VARCHAR(200) NOT NULL,
credits INT CHECK (credits > 0),
dept_id INT NOT NULL REFERENCES Departments(dept_id),
UNIQUE (dept_id, course_code) -- Код курса уникален в рамках кафедры
);
-- Create the Enrollments table
CREATE TABLE Enrollments (
enrollment_id SERIAL PRIMARY KEY,
student_id INT NOT NULL REFERENCES Students(student_id),
course_id INT NOT NULL REFERENCES Courses(course_id),
semester VARCHAR(20) NOT NULL, -- например, 'Fall 2024', 'Spring 2025'
registration_date DATE DEFAULT CURRENT_DATE,
grade CHAR(2) NULL, -- например, 'A', 'B+', 'P', 'F', NULL если в процессе
UNIQUE (student_id, course_id, semester) -- Студент не может записаться на один и тот же курс несколько раз за один семестр
);
-- Insert sample data
INSERT INTO Students (student_uid, first_name, last_name, enrollment_year, major) VALUES
('u7654321', 'Hermione', 'Granger', 2021, 'Magical Law'),
('u1122334', 'Ron', 'Weasley', 2021, 'Defense Against Dark Arts'),
('u9876543', 'Harry', 'Potter', 2021, 'Auror Training');
INSERT INTO Departments (dept_code, name) VALUES
('CSCI', 'Computer Science'),
('MATH', 'Mathematics'),
('PHYS', 'Physics');
INSERT INTO Courses (course_code, title, credits, dept_id) VALUES
('101', 'Intro to Programming', 4, 1),
('210', 'Data Structures', 4, 1),
('151', 'Calculus I', 4, 2),
('201', 'Classical Mechanics', 3, 3);
INSERT INTO Enrollments (student_id, course_id, semester, grade) VALUES
(1, 1, 'Fall 2023', 'A'),
(1, 3, 'Fall 2023', 'A-'),
(2, 1, 'Fall 2023', 'C+'),
(3, 1, 'Spring 2024', NULL), -- В процессе
(1, 2, 'Spring 2024', NULL), -- В процессе
(2, 3, 'Spring 2024', NULL); -- В процессе
Задания:
- Базовый индекс: Создайте индекс на таблице
Students
для эффективного поиска поlast_name
. - Индекс для внешнего ключа: Создайте индекс на таблице
Courses
для быстрого поиска всех курсов, предлагаемых определеннымdept_id
. - Индекс для внешнего ключа (
Enrollments
): Создайте индекс на таблицеEnrollments
для эффективного получения всех курсов, на которые записан или которые прошел определенныйstudent_id
. - Индекс для внешнего ключа (
Enrollments
): Создайте индекс на таблицеEnrollments
для быстрого формирования списка студентов для определенногоcourse_id
. - Составной индекс (Расписание студента): Создайте составной индекс на таблице
Enrollments
для оптимизации получения расписания определенного студента за заданныйsemester
. Индекс должен включатьstudent_id
иsemester
, именно в таком порядке. - Составной индекс (Список студентов курса): Создайте составной индекс на таблице
Enrollments
для оптимизации получения списка студентов для определенногоcourse_id
за конкретныйsemester
. Индекс должен включатьcourse_id
иsemester
, именно в таком порядке. - Частичный индекс (В процессе): Создайте частичный индекс на таблице
Enrollments
, включающий столбецcourse_id
, но только для строк, гдеgrade
имеет значение NULL. Это помогает быстро найти студентов, текущих записанных на курс (без оценки). - Индекс по выражению (Нечувствительный к регистру поиск по специальности): Создайте индекс на таблице
Students
, основанный на применении функцииLOWER()
к столбцуmajor
, для поддержки поиска студентов по специальности без учета регистра. - Перечисление индексов: Используйте метакоманду
\d Enrollments
для вывода всех индексов, связанных в данный момент с таблицейEnrollments
. Убедитесь, что индексы, созданные на предыдущих шагах, присутствуют. - Переиндексирование и удаление: Перестройте все индексы на таблице
Students
. После этого удалите индекс, созданный на шаге 1 (last_name
наStudents
).
Вариант 7: Система записи на прием к врачу
Сценарий: Вы отвечаете за управление базой данных системы записи на прием в клинике. Быстрый доступ к информации о пациентах, доступным временным слотам и истории записей критически важен для работы клиники. Вам необходимо добавить индексы для оптимизации частых операций поиска и фильтрации.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS Appointments;
DROP TABLE IF EXISTS Patients;
DROP TABLE IF EXISTS Doctors;
-- Создаем таблицу Doctors (Врачи)
CREATE TABLE Doctors (
doctor_id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
specialty VARCHAR(100),
license_number VARCHAR(50) UNIQUE NOT NULL
);
-- Создаем таблицу Patients (Пациенты)
CREATE TABLE Patients (
patient_id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
date_of_birth DATE,
contact_phone VARCHAR(20) UNIQUE, -- Может быть NULL, но должно быть уникальным, если указано
registration_date DATE DEFAULT CURRENT_DATE
);
-- Создаем таблицу Appointments (Записи на прием)
CREATE TABLE Appointments (
appointment_id SERIAL PRIMARY KEY,
patient_id INT NOT NULL REFERENCES Patients(patient_id),
doctor_id INT NOT NULL REFERENCES Doctors(doctor_id),
appointment_datetime TIMESTAMP NOT NULL, -- Дата и время приема
duration_minutes INT DEFAULT 30 CHECK (duration_minutes > 0),
status VARCHAR(20) DEFAULT 'scheduled', -- например, 'scheduled' (запланирован), 'completed' (завершен), 'cancelled' (отменен), 'no_show' (неявка)
reason TEXT -- Причина визита
);
-- Вставляем примеры данных
INSERT INTO Doctors (first_name, last_name, specialty, license_number) VALUES
('Alice', 'Chen', 'Cardiology', 'LIC1001'),
('Bob', 'Miller', 'Pediatrics', 'LIC1002'),
('Carol', 'Davis', 'General Practice', 'LIC1003');
INSERT INTO Patients (first_name, last_name, date_of_birth, contact_phone, registration_date) VALUES
('Mark', 'Johnson', '1985-06-15', '555-1234', '2023-01-10'),
('Sarah', 'Williams', '1992-09-22', '555-5678', '2023-02-20'),
('David', 'Brown', '1978-03-01', NULL, '2023-05-15'),
('Emily', 'Smith', '2018-11-12', '555-9900', '2023-06-01');
INSERT INTO Appointments (patient_id, doctor_id, appointment_datetime, duration_minutes, status, reason) VALUES
(1, 1, '2024-05-10 10:00:00', 45, 'completed', 'Annual Checkup'),
(2, 3, '2024-05-10 11:00:00', 30, 'scheduled', 'Consultation'),
(1, 1, '2024-05-17 14:00:00', 45, 'scheduled', 'Follow-up'),
(4, 2, '2024-05-18 09:30:00', 30, 'scheduled', 'Vaccination'),
(2, 3, '2024-04-25 15:00:00', 30, 'cancelled', 'Feeling better'),
(3, 3, '2024-05-20 16:00:00', 30, 'scheduled', 'New Patient Visit');
Задания:
- Базовый индекс: Создайте индекс для таблицы
Patients
для ускорения поиска по фамилии пациента (last_name
). - Уникальный индекс: Создайте уникальный индекс для таблицы
Patients
по столбцуcontact_phone
, чтобы обеспечить уникальность для НЕ-NULL номеров телефонов. - Индекс по внешнему ключу: Создайте индекс для таблицы
Appointments
для эффективного получения всех записей на прием для конкретногоpatient_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Appointments
для эффективного поиска всех записей на прием, запланированных к конкретномуdoctor_id
. - Составной индекс: Создайте составной индекс для таблицы
Appointments
для оптимизации запросов, фильтрующих сначала поdoctor_id
, а затем поappointment_datetime
. - Индекс по выражению: Создайте индекс для таблицы
Patients
на основе года (YEAR
), извлеченного изdate_of_birth
, для оптимизации запросов поиска пациентов, родившихся в определенном году.CREATE INDEX idx_patients_birth_year ON Patients (EXTRACT(YEAR FROM date_of_birth));
- Частичный индекс: Создайте частичный индекс для таблицы
Appointments
по столбцуappointment_datetime
, включающий только те записи на прием, у которыхstatus
равен ‘scheduled’. Это поможет оптимизировать запросы для поиска предстоящих, активных записей на прием. - Переиндексировать таблицу: Перестройте все существующие индексы для таблицы
Appointments
. - Показать индексы: Используйте мета-команду
\d Appointments
(в psql или на панели SQL Query Tool в pgAdmin), чтобы просмотреть индексы таблицыAppointments
. - Удалить индекс: Удалите индекс, созданный в шаге 6 (
idx_patients_birth_year
).
Вариант 8: Система учёта складских запасов
Сценарий: Вы управляете базой данных для системы учёта складских запасов. Система должна отслеживать товары, их местоположение на различных складах и текущие уровни запасов. Эффективный поиск информации о наличии товаров, уровнях запасов и содержимом складов имеет решающее значение.
Схема базы данных:
-- Удаляем таблицы, если они существуют, для чистого старта
DROP TABLE IF EXISTS InventoryLevels;
DROP TABLE IF EXISTS Products;
DROP TABLE IF EXISTS Warehouses;
-- Создаем таблицу Warehouses (Склады)
CREATE TABLE Warehouses (
warehouse_id SERIAL PRIMARY KEY,
code VARCHAR(10) UNIQUE NOT NULL, -- например, 'NYC-01', 'LAX-02'
city VARCHAR(100) NOT NULL,
capacity_sqft INT, -- Вместимость в кв. футах
is_refrigerated BOOLEAN DEFAULT FALSE -- Требуется ли охлаждение
);
-- Создаем таблицу Products (Товары)
CREATE TABLE Products (
product_id SERIAL PRIMARY KEY,
sku VARCHAR(50) UNIQUE NOT NULL, -- Артикул (единица складского учёта)
name VARCHAR(200) NOT NULL,
category VARCHAR(100),
unit_cost NUMERIC(10, 2), -- Себестоимость единицы
supplier_info JSONB -- Хранение имени поставщика, контактов и т.д.
);
-- Создаем таблицу InventoryLevels (Уровни запасов), связывающую Products и Warehouses
CREATE TABLE InventoryLevels (
inventory_id SERIAL PRIMARY KEY,
product_id INT NOT NULL REFERENCES Products(product_id),
warehouse_id INT NOT NULL REFERENCES Warehouses(warehouse_id),
quantity INT NOT NULL CHECK (quantity >= 0), -- Количество
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- Время последнего обновления
reorder_level INT DEFAULT 10, -- Уровень для повторного заказа
UNIQUE (product_id, warehouse_id) -- Товар может находиться на складе только в одной записи
);
-- Вставляем пример данных
INSERT INTO Warehouses (code, city, capacity_sqft, is_refrigerated) VALUES
('NYC-01', 'New York', 50000, false),
('LAX-02', 'Los Angeles', 75000, true),
('CHI-01', 'Chicago', 60000, false);
INSERT INTO Products (sku, name, category, unit_cost, supplier_info) VALUES
('ELE-TV-55', '55-inch 4K TV', 'Electronics', 350.00, '{"name": "PanelCorp", "contact": "sales@panelcorp.com"}'),
('FUR-DSK-WD', 'Wooden Desk', 'Furniture', 120.50, '{"name": "WoodWorks Ltd.", "contact": "orders@woodworksltd.net"}'),
('GRC-APL-FU', 'Fuji Apples (Box)', 'Grocery', 15.75, '{"name": "FreshFarms Inc.", "region": "WA"}'),
('ELE-CAM-DL', 'Digital SLR Camera', 'Electronics', 899.00, '{"name": "LensMasters", "contact": "info@lensmasters.com"}');
INSERT INTO InventoryLevels (product_id, warehouse_id, quantity, reorder_level) VALUES
(1, 1, 50, 10), -- Телевизор в NYC
(1, 3, 75, 15), -- Телевизор в Chicago
(2, 1, 120, 20), -- Стол в NYC
(3, 2, 200, 50), -- Яблоки в LA (требуется охлаждение)
(4, 1, 25, 5), -- Камера в NYC
(4, 3, 30, 5); -- Камера в Chicago
Задания:
- Базовый индекс: Создайте индекс для таблицы
Products
, чтобы ускорить поиск поname
(названию) товара. - Базовый индекс: Создайте индекс для таблицы
Warehouses
, чтобы ускорить поиск поcity
(городу). - Индекс по внешнему ключу: Создайте индекс для таблицы
InventoryLevels
, чтобы эффективно запрашивать уровни запасов для конкретногоproduct_id
на всех складах. - Индекс по внешнему ключу: Создайте индекс для таблицы
InventoryLevels
, чтобы быстро находить все товары, хранящиеся на конкретномwarehouse_id
(складе). - Составной индекс: Таблица
InventoryLevels
уже имеет ограничение уникальности (и, следовательно, соответствующий индекс) по полям(product_id, warehouse_id)
. Объясните, почему создание другого индексаON InventoryLevels (warehouse_id, product_id)
все еще может быть полезным для определенных запросов. - Частичный индекс: Создайте частичный индекс для таблицы
InventoryLevels
по столбцуquantity
, но включайте в него только те строки, гдеquantity
меньшеreorder_level
. Это поможет эффективно выявлять товары, требующие пополнения запасов.CREATE INDEX idx_inventory_low_stock ON InventoryLevels (product_id, warehouse_id) WHERE quantity < reorder_level;
- Индекс по выражению: Создайте индекс для таблицы
Products
на основе значения столбцаcategory
в нижнем регистре, чтобы обеспечить поиск по категории без учета регистра. - Индекс GIN (JSONB): Создайте индекс GIN для столбца
supplier_info
в таблицеProducts
, чтобы эффективно выполнять запросы по ключам или значениям в данных JSONB (например, для поиска товаров от конкретного поставщика по его имени).CREATE INDEX idx_products_supplier_info_gin ON Products USING gin (supplier_info);
- Список индексов: Используйте системное представление
pg_indexes
, чтобы вывести список всех индексов, связанных с таблицейInventoryLevels
. - Удаление индекса: Удалите индекс по выражению, созданный в шаге 7 (для
LOWER(category)
в таблицеProducts
).
Вариант 9: Блог-платформа
Сценарий: Вы являетесь администратором базы данных для блог-платформы. Пользователям необходимо иметь возможность быстро находить посты по автору, тегу или дате, а также просматривать комментарии, связанные с постами. Вы должны добавить индексы для эффективной поддержки этих функций.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS PostTags;
DROP TABLE IF EXISTS Comments;
DROP TABLE IF EXISTS Tags;
DROP TABLE IF EXISTS Posts;
DROP TABLE IF EXISTS Users;
-- Создаем таблицу Users
CREATE TABLE Users (
user_id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
join_date DATE DEFAULT CURRENT_DATE
);
-- Создаем таблицу Posts
CREATE TABLE Posts (
post_id SERIAL PRIMARY KEY,
author_id INT NOT NULL REFERENCES Users(user_id),
title VARCHAR(255) NOT NULL,
content TEXT,
published_at TIMESTAMP, -- NULL, если черновик
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(15) DEFAULT 'draft' -- например, 'draft', 'published', 'archived'
);
-- Создаем таблицу Comments
CREATE TABLE Comments (
comment_id SERIAL PRIMARY KEY,
post_id INT NOT NULL REFERENCES Posts(post_id) ON DELETE CASCADE,
user_id INT NOT NULL REFERENCES Users(user_id), -- Автор комментария
comment_text TEXT NOT NULL,
commented_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Создаем таблицу Tags
CREATE TABLE Tags (
tag_id SERIAL PRIMARY KEY,
tag_name VARCHAR(50) UNIQUE NOT NULL
);
-- Создаем таблицу PostTags (связь Многие-ко-Многим)
CREATE TABLE PostTags (
post_id INT NOT NULL REFERENCES Posts(post_id) ON DELETE CASCADE,
tag_id INT NOT NULL REFERENCES Tags(tag_id) ON DELETE CASCADE,
PRIMARY KEY (post_id, tag_id) -- Гарантирует, что тег применяется к посту только один раз
);
-- Вставляем пример данных
INSERT INTO Users (username, email) VALUES
('blogger_a', 'a@example.com'),
('reader_b', 'b@example.com'),
('editor_c', 'c@example.com');
INSERT INTO Posts (author_id, title, content, published_at, status) VALUES
(1, 'My First Post', 'This is the content of my first post.', '2024-01-15 10:00:00', 'published'),
(1, 'Advanced SQL Tricks', 'Let us talk about window functions...', '2024-02-20 11:30:00', 'published'),
(3, 'Editing Guidelines', 'Please follow these guidelines...', '2024-03-01 09:00:00', 'published'),
(1, 'Draft Post Idea', 'Something about databases...', NULL, 'draft');
INSERT INTO Comments (post_id, user_id, comment_text) VALUES
(1, 2, 'Great first post!'),
(2, 2, 'Very useful SQL tips, thanks!'),
(2, 3, 'Well written, approved.'),
(1, 1, 'Thanks for the feedback!'); -- Автор отвечает
INSERT INTO Tags (tag_name) VALUES
('introduction'), ('sql'), ('databases'), ('guide'), ('meta');
INSERT INTO PostTags (post_id, tag_id) VALUES
(1, 1), -- Пост 1 с тегом 'introduction'
(2, 2), -- Пост 2 с тегом 'sql'
(2, 3), -- Пост 2 с тегом 'databases'
(3, 4), -- Пост 3 с тегом 'guide'
(3, 5); -- Пост 3 с тегом 'meta'
Задания:
- Базовый индекс: Создайте индекс для таблицы
Posts
, чтобы быстро находить все посты, написанные конкретнымauthor_id
. - Базовый индекс: Создайте индекс для таблицы
Comments
, чтобы быстро извлекать все комментарии, оставленные конкретнымuser_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Comments
, чтобы эффективно извлекать все комментарии, принадлежащие конкретномуpost_id
. - Индекс по внешнему ключу (M2M): Создайте индекс для таблицы
PostTags
, чтобы эффективно находить все теги, связанные с заданнымpost_id
. (Примечание: Первичный ключ уже покрывает(post_id, tag_id)
, но индекс конкретно поpost_id
может использоваться планировщиком иначе). - Индекс по внешнему ключу (M2M): Создайте индекс для таблицы
PostTags
, чтобы эффективно находить все посты, связанные с заданнымtag_id
. - Многоколоночный индекс: Создайте многоколоночный индекс для таблицы
Posts
, чтобы оптимизировать запросы, ищущие посты по конкретномуauthor_id
с одновременной фильтрацией поstatus
(например, найти все ‘published’ посты автора X). - Индекс по выражению: Создайте индекс для таблицы
Posts
, используя функциюDATE()
для столбцаpublished_at
, чтобы оптимизировать запросы, фильтрующие посты, опубликованные в конкретную дату (игнорируя компонент времени).CREATE INDEX idx_posts_publish_date ON Posts (DATE(published_at));
- Частичный индекс: Создайте частичный индекс для таблицы
Posts
по столбцуpublished_at
, но включайте только те строки, гдеstatus
равен ‘published’. Это полезно для эффективного выполнения запросов или сортировки только опубликованных постов по дате. - Список индексов: Используйте мета-команду
\d PostTags
, чтобы просмотреть индексы (включая индекс первичного ключа) для таблицыPostTags
. - Удаление индекса: Удалите индекс, созданный в шаге 7 (
idx_posts_publish_date
).
Вариант 10: Система бронирования столиков в ресторане
Сценарий: Вы управляете базой данных для онлайн-платформы бронирования ресторанов. Эффективный поиск ресторанов, проверка доступности столиков и управление бронированиями клиентов являются ключевыми функциями. Вам необходимо добавить индексы для оптимизации производительности запросов.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS Reservations;
DROP TABLE IF EXISTS RestaurantTables;
DROP TABLE IF EXISTS Customers;
DROP TABLE IF EXISTS Restaurants;
-- Создаем таблицу Restaurants
CREATE TABLE Restaurants (
restaurant_id SERIAL PRIMARY KEY,
name VARCHAR(150) NOT NULL,
cuisine_type VARCHAR(50),
city VARCHAR(100),
rating NUMERIC(3, 2) CHECK (rating >= 0 AND rating <= 5), -- например, 4.50
phone_number VARCHAR(20) UNIQUE
);
-- Создаем таблицу RestaurantTables
CREATE TABLE RestaurantTables (
table_id SERIAL PRIMARY KEY,
restaurant_id INT NOT NULL REFERENCES Restaurants(restaurant_id),
table_number VARCHAR(10), -- например, 'T1', 'P2' (Патио 2)
capacity INT NOT NULL CHECK (capacity > 0),
is_available BOOLEAN DEFAULT TRUE
);
-- Создаем таблицу Customers
CREATE TABLE Customers (
customer_id SERIAL PRIMARY KEY,
first_name VARCHAR(100),
last_name VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
phone VARCHAR(20)
);
-- Создаем таблицу Reservations
CREATE TABLE Reservations (
reservation_id SERIAL PRIMARY KEY,
customer_id INT NOT NULL REFERENCES Customers(customer_id),
table_id INT NOT NULL REFERENCES RestaurantTables(table_id),
reservation_time TIMESTAMP NOT NULL,
party_size INT NOT NULL CHECK (party_size > 0),
status VARCHAR(20) DEFAULT 'confirmed', -- 'confirmed' (подтверждено), 'cancelled' (отменено), 'completed' (завершено), 'no-show' (неявка)
special_requests TEXT
);
-- Вставляем пример данных
INSERT INTO Restaurants (name, cuisine_type, city, rating, phone_number) VALUES
('The Gourmet Place', 'French', 'Metropolis', 4.80, '555-0101'),
('Pasta Paradise', 'Italian', 'Metropolis', 4.50, '555-0102'),
('Sushi Central', 'Japanese', 'Gotham', 4.70, '555-0201'),
('Taco Town', 'Mexican', 'Metropolis', 4.20, '555-0103');
INSERT INTO RestaurantTables (restaurant_id, table_number, capacity, is_available) VALUES
(1, 'T1', 2, TRUE), (1, 'T2', 4, TRUE), (1, 'T3', 4, FALSE),
(2, 'P1', 4, TRUE), (2, 'P2', 6, TRUE),
(3, 'S1', 2, TRUE), (3, 'S2', 2, TRUE), (3, 'B1', 8, TRUE), -- Суши-бар
(4, 'M1', 4, TRUE);
INSERT INTO Customers (first_name, last_name, email, phone) VALUES
('Clark', 'Kent', 'clark.k@dailyplanet.com', '555-1111'),
('Lois', 'Lane', 'lois.l@dailyplanet.com', '555-2222'),
('Bruce', 'Wayne', 'bruce@waynecorp.com', '555-3333');
INSERT INTO Reservations (customer_id, table_id, reservation_time, party_size, status) VALUES
(1, 2, '2024-06-15 19:00:00', 2, 'confirmed'), -- Кларк в Gourmet Place, столик T2
(2, 4, '2024-06-16 20:00:00', 4, 'confirmed'), -- Лоис в Pasta Paradise, столик P1
(3, 8, '2024-06-17 18:30:00', 6, 'confirmed'), -- Брюс в Sushi Central, столик B1
(1, 5, '2024-06-18 19:30:00', 5, 'cancelled'); -- Кларк в Pasta Paradise, столик P2 (отменено)
Задания:
- Базовый индекс: Создайте индекс для таблицы
Restaurants
, чтобы ускорить поиск поname
ресторана. - Базовый индекс: Создайте индекс для таблицы
Customers
, чтобы быстро находить клиентов по ихlast_name
. - Индекс по внешнему ключу: Создайте индекс для таблицы
RestaurantTables
, чтобы эффективно находить все столики, принадлежащие определенномуrestaurant_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Reservations
, чтобы быстро находить бронирования, сделанные определеннымcustomer_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Reservations
, чтобы быстро находить все бронирования, связанные с определеннымtable_id
. - Составной индекс: Создайте составной индекс для таблицы
Restaurants
, чтобы оптимизировать поиск с фильтрацией сначала поcity
, а затем поcuisine_type
. - Составной индекс: Создайте составной индекс для таблицы
Reservations
, чтобы эффективно запрашивать бронирования на основеreservation_time
иstatus
. Поместитеreservation_time
первым в определении индекса. - Частичный индекс: Создайте частичный индекс для таблицы
Reservations
по столбцуreservation_time
, но включайте только бронирования со статусом ‘confirmed’. Это поможет эффективно находить активные предстоящие бронирования. - Индекс по выражению: Создайте индекс для таблицы
Customers
, используя функциюLOWER()
для столбцаemail
, чтобы поддерживать поиск по email без учета регистра (несмотря на наличие ограничения уникальности, это поддерживает запросы с использованиемLOWER()
). - Просмотр и удаление: Используйте мета-команду
\d Reservations
, чтобы вывести список индексов таблицыReservations
. Затем удалите индекс, созданный в шаге 8 (частичный индекс поreservation_time
для статуса ‘confirmed’).
Вариант 11: Система продажи билетов на мероприятия
Сценарий: Вы разрабатываете базу данных для онлайн-платформы продажи билетов на мероприятия. Пользователям необходимо быстро искать мероприятия, просматривать наличие и цены билетов, а также управлять своими покупками. Индексирование критически важно для обработки высокой нагрузки во время продаж билетов на популярные мероприятия.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS Purchases;
DROP TABLE IF EXISTS TicketTypes;
DROP TABLE IF EXISTS Events;
DROP TABLE IF EXISTS Venues;
DROP TABLE IF EXISTS Users; -- Предполагается наличие общей таблицы Users (Пользователи)
-- Создаем таблицу Venues (Места проведения)
CREATE TABLE Venues (
venue_id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
city VARCHAR(100),
capacity INT
);
-- Создаем таблицу Events (Мероприятия)
CREATE TABLE Events (
event_id SERIAL PRIMARY KEY,
venue_id INT REFERENCES Venues(venue_id),
name VARCHAR(255) NOT NULL,
category VARCHAR(50), -- например, 'Музыка', 'Спорт', 'Театр', 'Конференция'
event_start_time TIMESTAMP NOT NULL,
event_end_time TIMESTAMP,
status VARCHAR(20) DEFAULT 'scheduled' -- 'запланировано', 'отменено', 'перенесено', 'завершено'
);
-- Создаем таблицу TicketTypes (Типы билетов) (определяет уровни цен/секции для мероприятия)
CREATE TABLE TicketTypes (
ticket_type_id SERIAL PRIMARY KEY,
event_id INT NOT NULL REFERENCES Events(event_id),
description VARCHAR(100), -- например, 'Входной (без места)', 'VIP', 'Балкон, сектор А'
price NUMERIC(10, 2) NOT NULL CHECK (price >= 0),
total_quantity INT NOT NULL CHECK (total_quantity >= 0), -- Общее количество доступных билетов этого типа
available_quantity INT CHECK (available_quantity >= 0 AND available_quantity <= total_quantity)
);
-- Создаем таблицу Users (Пользователи) (упрощенная)
CREATE TABLE Users (
user_id SERIAL PRIMARY KEY,
email VARCHAR(150) UNIQUE NOT NULL,
name VARCHAR(150)
);
-- Создаем таблицу Purchases (Покупки)
CREATE TABLE Purchases (
purchase_id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES Users(user_id),
ticket_type_id INT NOT NULL REFERENCES TicketTypes(ticket_type_id),
quantity INT NOT NULL CHECK (quantity > 0),
purchase_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
total_cost NUMERIC(12, 2),
confirmation_code VARCHAR(32) UNIQUE NOT NULL -- Уникальный код для данной транзакции покупки
);
-- Вставляем примерные данные
INSERT INTO Venues (name, city, capacity) VALUES
('Arena Central', 'Metropolis', 15000),
('Grand Theater', 'Gotham', 2500),
('Open Air Park', 'Metropolis', 30000);
INSERT INTO Events (venue_id, name, category, event_start_time, status) VALUES
(1, 'Rock Legends Concert', 'Music', '2024-08-15 20:00:00', 'scheduled'),
(2, 'Shakespeare''s Hamlet', 'Theater', '2024-09-10 19:30:00', 'scheduled'),
(1, 'Tech Conference 2024', 'Conference', '2024-10-01 09:00:00', 'scheduled'),
(3, 'Summer Music Festival', 'Music', '2024-07-20 12:00:00', 'completed');
INSERT INTO TicketTypes (event_id, description, price, total_quantity, available_quantity) VALUES
(1, 'Floor Standing', 75.00, 5000, 3500), (1, 'Lower Bowl Seat', 120.00, 8000, 6000), (1, 'VIP Box', 300.00, 100, 20),
(2, 'Orchestra', 90.00, 500, 450), (2, 'Mezzanine', 65.00, 1000, 800),
(3, 'Standard Pass', 499.00, 2000, 1500), (3, 'Workshop Addon', 150.00, 500, 300);
INSERT INTO Users (email, name) VALUES
('fan1@email.com', 'Alex Fan'),
('theater_lover@email.com', 'Bella Critic'),
('techguru@email.com', 'Charlie Dev');
INSERT INTO Purchases (user_id, ticket_type_id, quantity, total_cost, confirmation_code) VALUES
(1, 1, 2, 150.00, 'CONF-ROCK-12345ABC'), -- Алекс покупает 2 билета в фан-зону (Floor Standing) на Rock Legends
(2, 4, 1, 90.00, 'CONF-HAMLET-67890DEF'), -- Белла покупает 1 билет в партер (Orchestra) на Гамлета
(1, 2, 4, 480.00, 'CONF-ROCK-11223XYZ'), -- Алекс покупает 4 билета на нижний ярус трибун (Lower Bowl) на Rock Legends
(3, 6, 1, 499.00, 'CONF-TECH-44556UVW'); -- Чарли покупает 1 стандартный пропуск (Standard Pass) на Tech Conference
Задания:
- Базовый индекс: Создайте индекс для таблицы
Events
для быстрого поиска мероприятий поname
. - Базовый индекс: Создайте индекс для таблицы
Venues
для оптимизации поиска поcity
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Events
для эффективного поиска всех мероприятий, проходящих в определенномvenue_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
TicketTypes
для быстрого получения всех типов билетов, связанных с конкретнымevent_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Purchases
для быстрого поиска всех покупок, сделанных конкретнымuser_id
. - Составной индекс: Создайте составной индекс для таблицы
Events
для оптимизации запросов, фильтрующих сначала поcategory
, а затем поevent_start_time
. - Составной индекс: Создайте составной индекс для таблицы
TicketTypes
для поддержки эффективных запросов, фильтрующих поevent_id
и сортирующих/фильтрующих поprice
. - Частичный индекс: Создайте частичный индекс для таблицы
Events
по столбцуevent_start_time
, но включайте только те мероприятия, у которых статус (status
) равен ‘scheduled’ или ‘postponed’. Это поможет быстро находить предстоящие или перенесенные мероприятия. - Перестроение индексов таблицы: Перестройте все индексы для таблицы
Purchases
. Это может потребоваться периодически, если данные о покупках часто изменяются (например, возвраты, вызывающие удаления/обновления). - Вывод списка и удаление: Используйте системное представление
pg_indexes
, чтобы вывести список всех индексов таблицыEvents
. Затем удалите индекс, созданный на шаге 8 (частичный индекс поevent_start_time
).
Вариант 12: Платформа доски объявлений о вакансиях
Сценарий: Вы отвечаете за базу данных веб-сайта доски объявлений о вакансиях. Быстрый поиск вакансий по местоположению, категории и ключевым словам имеет ключевое значение, так же как и эффективное извлечение заявок (откликов) для конкретных вакансий или кандидатов. Индексирование жизненно важно для производительности.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS Applications;
DROP TABLE IF EXISTS Jobs;
DROP TABLE IF EXISTS Companies;
DROP TABLE IF EXISTS Candidates;
-- Создаем таблицу Companies
CREATE TABLE Companies (
company_id SERIAL PRIMARY KEY,
name VARCHAR(150) NOT NULL UNIQUE,
industry VARCHAR(100),
headquarters_location VARCHAR(150)
);
-- Создаем таблицу Jobs
CREATE TABLE Jobs (
job_id SERIAL PRIMARY KEY,
company_id INT NOT NULL REFERENCES Companies(company_id),
title VARCHAR(200) NOT NULL,
job_description TEXT,
category VARCHAR(100), -- например, 'Инженерное дело', 'Маркетинг', 'Продажи', 'Дизайн'
location VARCHAR(150), -- Может быть 'Удаленно' или город/регион
salary_range VARCHAR(50), -- например, '$80k-$100k', 'Конкурентная'
date_posted DATE DEFAULT CURRENT_DATE,
is_active BOOLEAN DEFAULT TRUE
);
-- Создаем таблицу Candidates
CREATE TABLE Candidates (
candidate_id SERIAL PRIMARY KEY,
first_name VARCHAR(100),
last_name VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
phone VARCHAR(20),
primary_skill VARCHAR(100), -- например, 'Python', 'Управление проектами', 'Графический дизайн'
resume_url VARCHAR(255) -- Ссылка на сохраненный файл резюме
);
-- Создаем таблицу Applications
CREATE TABLE Applications (
application_id SERIAL PRIMARY KEY,
job_id INT NOT NULL REFERENCES Jobs(job_id),
candidate_id INT NOT NULL REFERENCES Candidates(candidate_id),
application_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status VARCHAR(30) DEFAULT 'submitted', -- 'подана', 'просмотрена', 'собеседование', 'предложение', 'отклонена', 'отозвана'
cover_letter TEXT,
UNIQUE (job_id, candidate_id) -- Предотвращение дублирования заявок
);
-- Вставляем пример данных
INSERT INTO Companies (name, industry, headquarters_location) VALUES
('Innovate Solutions', 'Technology', 'San Francisco, CA'),
('Global Marketing Inc.', 'Marketing', 'New York, NY'),
('BuildWell Construction', 'Construction', 'Austin, TX');
INSERT INTO Jobs (company_id, title, category, location, salary_range, is_active) VALUES
(1, 'Senior Software Engineer', 'Engineering', 'Remote', '$120k-$160k', TRUE),
(1, 'Backend Developer', 'Engineering', 'San Francisco, CA', '$100k-$130k', TRUE),
(2, 'Digital Marketing Manager', 'Marketing', 'New York, NY', '$90k-$110k', TRUE),
(3, 'Project Manager', 'Construction', 'Austin, TX', '$85k-$105k', TRUE),
(1, 'UI/UX Designer', 'Design', 'Remote', null, FALSE); -- Неактивная вакансия
INSERT INTO Candidates (first_name, last_name, email, primary_skill) VALUES
('Sarah', 'Chen', 'sarah.c@email.com', 'Python'),
('Michael', 'Bolton', 'm.bolton@email.com', 'JavaScript'),
('Priya', 'Sharma', 'priya.s@email.com', 'Digital Marketing'),
('David', 'Lee', 'david.lee@email.com', 'Project Management');
INSERT INTO Applications (job_id, candidate_id, status) VALUES
(1, 1, 'submitted'), -- Сара откликнулась на Senior Software Engineer
(2, 2, 'submitted'), -- Майкл откликнулся на Backend Developer
(3, 3, 'interviewing'), -- Прия откликнулась на Digital Marketing Manager
(1, 2, 'viewed'), -- Майкл также откликнулся на Senior Software Engineer
(4, 4, 'submitted'); -- Дэвид откликнулся на Project Manager
Задачи:
- Базовый индекс: Создайте индекс для таблицы
Jobs
, чтобы ускорить поиск поtitle
(названию) вакансии. - Базовый индекс: Создайте индекс для таблицы
Companies
, чтобы обеспечить более быстрый поиск поname
(названию) компании. - Индекс по внешнему ключу: Создайте индекс для таблицы
Jobs
, чтобы эффективно находить все вакансии, опубликованные конкретнойcompany_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Applications
, чтобы быстро извлекать все заявки (отклики), поданные на конкретнуюjob_id
. - Индекс по внешнему ключу: Создайте индекс для таблицы
Applications
, чтобы быстро находить все вакансии, на которые откликнулся конкретныйcandidate_id
. - Многоколоночный индекс: Создайте многоколоночный индекс для таблицы
Jobs
, чтобы оптимизировать запросы, фильтрующие сначала поlocation
, а затем поcategory
. - Многоколоночный индекс: Создайте многоколоночный индекс для таблицы
Applications
, чтобы эффективно запрашивать заявки (отклики) на основеjob_id
иapplication_date
. Поместитеjob_id
первым. - Частичный индекс: Создайте частичный индекс для таблицы
Jobs
по столбцуdate_posted
, но только для тех вакансий, гдеis_active
равно TRUE. Это поможет эффективно находить текущие активные вакансии, отсортированные по дате публикации. - Индекс по выражению: Создайте индекс для таблицы
Jobs
, используя функциюLOWER()
для столбцаtitle
, чтобы поддерживать поиск по названию без учета регистра. - Вывод списка и удаление: Используйте системное представление
pg_indexes
, чтобы вывести список всех индексов, связанных с таблицейApplications
. Затем удалите многоколоночный индекс, созданный на шаге 7 (поjob_id
,application_date
).
Вариант 13: Музыкальный стриминговый сервис
Сценарий: Вы управляете базой данных музыкального стримингового сервиса. Пользователям необходимо иметь возможность быстро искать исполнителей, альбомы и треки, а также эффективно управлять своими плейлистами. Для обеспечения бесперебойной работы пользователей (user experience) требуются индексы.
Схема базы данных:
-- Удаляем таблицы, если они существуют, для чистого старта
DROP TABLE IF EXISTS PlaylistTracks;
DROP TABLE IF EXISTS UserPlaylists;
DROP TABLE IF EXISTS Tracks;
DROP TABLE IF EXISTS Albums;
DROP TABLE IF EXISTS Artists;
-- Создаем таблицу Artists
CREATE TABLE Artists (
artist_id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
genre VARCHAR(100),
country VARCHAR(100)
);
-- Создаем таблицу Albums
CREATE TABLE Albums (
album_id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
artist_id INT NOT NULL REFERENCES Artists(artist_id),
release_year INT CHECK (release_year > 1800),
record_label VARCHAR(150)
);
-- Создаем таблицу Tracks
CREATE TABLE Tracks (
track_id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
album_id INT NOT NULL REFERENCES Albums(album_id),
duration_seconds INT CHECK (duration_seconds > 0),
track_number INT -- Позиция трека в альбоме
);
-- Создаем таблицу UserPlaylists
CREATE TABLE UserPlaylists (
playlist_id SERIAL PRIMARY KEY,
user_id INT NOT NULL, -- Предполагается, что таблица Users существует где-то еще
name VARCHAR(150) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Создаем таблицу PlaylistTracks (связывающую UserPlaylists и Tracks)
CREATE TABLE PlaylistTracks (
playlist_track_id SERIAL PRIMARY KEY,
playlist_id INT NOT NULL REFERENCES UserPlaylists(playlist_id) ON DELETE CASCADE,
track_id INT NOT NULL REFERENCES Tracks(track_id) ON DELETE CASCADE,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (playlist_id, track_id) -- Трек может находиться в конкретном плейлисте только один раз
);
-- Вставляем примерные данные
INSERT INTO Artists (name, genre, country) VALUES
('Queen', 'Rock', 'UK'),
('Daft Punk', 'Electronic', 'France'),
('Miles Davis', 'Jazz', 'USA'),
('Taylor Swift', 'Pop', 'USA');
INSERT INTO Albums (title, artist_id, release_year, record_label) VALUES
('A Night at the Opera', 1, 1975, 'EMI'),
('Discovery', 2, 2001, 'Virgin'),
('Kind of Blue', 3, 1959, 'Columbia'),
('1989', 4, 2014, 'Big Machine');
INSERT INTO Tracks (title, album_id, duration_seconds, track_number) VALUES
('Bohemian Rhapsody', 1, 355, 11),
('Love of My Life', 1, 219, 9),
('One More Time', 2, 320, 1),
('Digital Love', 2, 301, 3),
('So What', 3, 562, 1),
('All Blues', 3, 693, 4),
('Shake It Off', 4, 219, 6),
('Blank Space', 4, 231, 2);
INSERT INTO UserPlaylists (user_id, name) VALUES
(101, 'Workout Mix'),
(102, 'Chill Vibes');
INSERT INTO PlaylistTracks (playlist_id, track_id) VALUES
(1, 1), (1, 3), (1, 7),
(2, 2), (2, 4), (2, 5), (2, 6);
Задания:
- Базовый индекс: Создайте индекс для таблицы
Artists
, чтобы оптимизировать поиск исполнителей поname
. - Базовый индекс: Создайте индекс для таблицы
Albums
, чтобы ускорить поиск поtitle
альбома. - Индекс по внешнему ключу: Создайте индекс для столбца
artist_id
таблицыAlbums
, чтобы эффективно находить все альбомы конкретного исполнителя. - Индекс по внешнему ключу: Создайте индекс для столбца
album_id
таблицыTracks
, чтобы быстро извлекать все треки, принадлежащие определенному альбому. - Многоколоночный индекс: Создайте многоколоночный индекс для таблицы
Tracks
, используя(album_id, track_number)
, чтобы оптимизировать извлечение треков для альбома в их правильном порядке. - Индекс по внешнему ключу (связующая таблица): Создайте индекс для столбца
playlist_id
таблицыPlaylistTracks
, чтобы быстро находить все треки в конкретном плейлисте. - Индекс по внешнему ключу (связующая таблица): Создайте индекс для столбца
track_id
таблицыPlaylistTracks
, чтобы быстро определять, какие плейлисты содержат конкретный трек. - Частичный индекс: Создайте частичный индекс для столбца
release_year
таблицыAlbums
, индексируя только альбомы, выпущенные после 2000 года (включительно). Это ускоряет запросы, ориентированные на современную музыку. - Индекс по выражению: Создайте индекс для таблицы
Tracks
, чтобы поддерживать поиск названий треков без учета регистра, используяLOWER(title)
. - Вывод списка и удаление: Используйте системное представление
pg_indexes
, чтобы вывести список всех индексов, связанных с таблицейTracks
. Затем удалите индекс для поиска без учета регистра по названию, созданный на предыдущем шаге.
Вариант 14: Платформа для объявлений о недвижимости
Сценарий: Вы разрабатываете базу данных для веб-сайта по недвижимости. Пользователям необходимо искать объекты недвижимости по местоположению, цене, размеру и характеристикам. Агенты размещают объявления об объектах недвижимости, и производительность является ключевым фактором для функциональности поиска.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS PropertyFeatures;
DROP TABLE IF EXISTS Listings;
DROP TABLE IF EXISTS Properties;
DROP TABLE IF EXISTS Agents;
-- Создаем таблицу Agents
CREATE TABLE Agents (
agent_id SERIAL PRIMARY KEY,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
agency VARCHAR(150),
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(20)
);
-- Создаем таблицу Properties
CREATE TABLE Properties (
property_id SERIAL PRIMARY KEY,
address VARCHAR(255) NOT NULL,
city VARCHAR(100) NOT NULL,
state VARCHAR(50) NOT NULL,
zip_code VARCHAR(10) NOT NULL,
property_type VARCHAR(50), -- например, 'Частный дом', 'Квартира', 'Таунхаус'
beds INT CHECK (beds >= 0),
baths NUMERIC(3,1) CHECK (baths >= 0), -- Позволяет указывать половинные санузлы (например, 2.5)
square_footage INT CHECK (square_footage > 0),
year_built INT
);
-- Создаем таблицу Listings
CREATE TABLE Listings (
listing_id SERIAL PRIMARY KEY,
property_id INT NOT NULL UNIQUE REFERENCES Properties(property_id), -- Гарантирует, что у объекта недвижимости может быть только одно активное объявление одновременно
agent_id INT NOT NULL REFERENCES Agents(agent_id),
list_price NUMERIC(14, 2) NOT NULL CHECK (list_price > 0),
listing_date DATE NOT NULL DEFAULT CURRENT_DATE,
status VARCHAR(20) DEFAULT 'active' -- например, 'активно', 'в ожидании', 'продано', 'снято с продажи'
);
-- Создаем таблицу PropertyFeatures (связь многие-ко-многим для характеристик)
CREATE TABLE PropertyFeatures (
feature_id SERIAL PRIMARY KEY,
property_id INT NOT NULL REFERENCES Properties(property_id) ON DELETE CASCADE,
feature_name VARCHAR(100) NOT NULL, -- например, 'Бассейн', 'Гараж', 'Камин', 'Вид на воду'
UNIQUE (property_id, feature_name)
);
-- Вставляем пример данных
INSERT INTO Agents (first_name, last_name, agency, email, phone) VALUES
('Samantha', 'Green', 'Elite Homes', 'sam.g@elite.com', '555-1111'),
('David', 'Cho', 'Premier Properties', 'd.cho@premier.com', '555-2222'),
('Maria', 'Garcia', 'Elite Homes', 'maria.g@elite.com', '555-3333');
INSERT INTO Properties (address, city, state, zip_code, property_type, beds, baths, square_footage, year_built) VALUES
('123 Oak St', 'Maplewood', 'CA', '90210', 'Single Family', 4, 2.5, 2400, 1995),
('456 Pine Ave', 'Maplewood', 'CA', '90211', 'Condo', 2, 2, 1200, 2010),
('789 Elm Rd', 'Springfield', 'IL', '62704', 'Single Family', 3, 2, 1800, 1980),
('101 Maple Dr', 'Maplewood', 'CA', '90210', 'Townhouse', 3, 2.5, 1600, 2005);
INSERT INTO Listings (property_id, agent_id, list_price, listing_date, status) VALUES
(1, 1, 850000.00, '2024-02-15', 'active'),
(2, 2, 450000.00, '2024-03-01', 'active'),
(3, 1, 320000.00, '2024-01-20', 'pending'),
(4, 3, 510000.00, '2024-03-10', 'active');
INSERT INTO PropertyFeatures (property_id, feature_name) VALUES
(1, 'Pool'), (1, 'Garage'),
(2, 'Garage'), (2, 'Gym Access'),
(3, 'Fireplace'), (3, 'Large Yard'),
(4, 'Garage');
Задания:
- Базовый индекс: Создайте базовый индекс для таблицы
Properties
, чтобы ускорить поиск по столбцуcity
. - Многоколоночный индекс: Создайте многоколоночный индекс для таблицы
Properties
по столбцам(state, city)
, чтобы оптимизировать поиск с фильтрацией сначала по штату, затем по городу. - Индекс для внешнего ключа: Создайте индекс для таблицы
Listings
по столбцуagent_id
, чтобы эффективно находить все объявления, управляемые конкретным агентом. - Индекс для ограничения UNIQUE: Индекс автоматически создается для ограничения
UNIQUE
наListings.property_id
. Тем не менее, создайте явный индекс для этого столбца дополнительно (некоторые администраторы баз данных предпочитают это для ясности или специфической настройки производительности, хотя это и избыточно при наличии индекса от ограничения). Назовите егоidx_listings_property_explicit
. - Многоколоночный индекс: Создайте многоколоночный индекс для таблицы
Listings
по столбцам(status, list_price)
, чтобы оптимизировать поиск объявлений по статусу (например, ‘active’), а затем фильтрацию или сортировку по цене. - Базовый индекс: Создайте индекс для таблицы
Properties
по столбцуproperty_type
. - Частичный индекс: Создайте частичный индекс для таблицы
Listings
, включающий только строки, гдеstatus = 'active'
. Этот индекс должен охватывать столбецlist_price
для оптимизации поиска активных объявлений в заданном ценовом диапазоне. - Индекс по выражению: Создайте индекс для таблицы
Properties
, используя выражениеUPPER(zip_code)
, чтобы обеспечить поиск по почтовому индексу без учета регистра (хотя почтовые индексы обычно единообразны, это демонстрирует концепцию). - Индекс для внешнего ключа (Многие-ко-многим): Создайте индекс для таблицы
PropertyFeatures
по столбцуproperty_id
, чтобы быстро находить все характеристики, связанные с объектом недвижимости. - Переиндексация и отображение: Перестройте все индексы для таблицы
Properties
. Затем используйте команду\d Listings
(в psql или в панели SQL редактора запросов pgAdmin), чтобы отобразить структуру и индексы таблицыListings
.
Вариант 15: Система отслеживания ошибок
Сценарий: Вы являетесь администратором системы отслеживания ошибок программного обеспечения. Разработчикам и QA-инженерам (инженерам по тестированию) необходимо эффективно искать, фильтровать и обновлять информацию об ошибках на основе проекта, статуса, приоритета и исполнителя. Индексы критически важны для поддержания быстродействия системы.
Схема базы данных:
-- Удаляем таблицы, если они существуют, чтобы начать с чистого листа
DROP TABLE IF EXISTS BugComments;
DROP TABLE IF EXISTS Bugs;
DROP TABLE IF EXISTS Projects;
DROP TABLE IF EXISTS Users;
-- Создаем таблицу Users (упрощенная версия)
CREATE TABLE Users (
user_id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
full_name VARCHAR(150),
email VARCHAR(100) UNIQUE NOT NULL
);
-- Создаем таблицу Projects
CREATE TABLE Projects (
project_id SERIAL PRIMARY KEY,
project_name VARCHAR(100) UNIQUE NOT NULL,
description TEXT
);
-- Создаем таблицу Bugs
CREATE TABLE Bugs (
bug_id SERIAL PRIMARY KEY,
project_id INT NOT NULL REFERENCES Projects(project_id),
title VARCHAR(255) NOT NULL,
description TEXT,
reported_by_user_id INT NOT NULL REFERENCES Users(user_id),
assigned_to_user_id INT REFERENCES Users(user_id), -- Может быть NULL, если исполнитель не назначен
status VARCHAR(20) NOT NULL DEFAULT 'new', -- например, 'new', 'assigned', 'in_progress', 'resolved', 'closed', 'reopened'
priority VARCHAR(15) NOT NULL DEFAULT 'medium', -- например, 'low', 'medium', 'high', 'critical'
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Создаем таблицу BugComments
CREATE TABLE BugComments (
comment_id SERIAL PRIMARY KEY,
bug_id INT NOT NULL REFERENCES Bugs(bug_id) ON DELETE CASCADE,
user_id INT NOT NULL REFERENCES Users(user_id),
comment_text TEXT NOT NULL,
commented_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Вставляем пример данных
INSERT INTO Users (username, full_name, email) VALUES
('dev1', 'Alice Developer', 'alice.dev@company.com'),
('qa1', 'Bob Tester', 'bob.qa@company.com'),
('mgr1', 'Charlie Manager', 'charlie.mgr@company.com');
INSERT INTO Projects (project_name, description) VALUES
('WebApp V2', 'Main customer-facing web application'),
('MobileApp', 'iOS and Android companion app'),
('Backend API', 'Internal API services');
INSERT INTO Bugs (project_id, title, description, reported_by_user_id, assigned_to_user_id, status, priority, created_at, updated_at) VALUES
(1, 'Login button unresponsive on Firefox', 'Users on Firefox cannot click the login button.', 2, 1, 'assigned', 'high', '2024-03-01 10:00:00', '2024-03-01 11:30:00'),
(1, 'Incorrect currency symbol on checkout page', 'Shows USD instead of EUR for European users.', 2, 1, 'in_progress', 'medium', '2024-03-05 14:15:00', '2024-03-06 09:00:00'),
(2, 'App crashes when opening profile screen', 'Occurs on Android 13 devices.', 2, NULL, 'new', 'critical', '2024-03-10 16:00:00', '2024-03-10 16:00:00'),
(3, 'API endpoint /users returns 500 error', 'The /users endpoint fails intermittently.', 1, 1, 'resolved', 'high', '2024-02-20 09:30:00', '2024-03-08 17:00:00'),
(1, 'Typo in footer text', 'Copyright year is wrong.', 3, NULL, 'new', 'low', '2024-03-12 11:00:00', '2024-03-12 11:00:00');
INSERT INTO BugComments (bug_id, user_id, comment_text, commented_at) VALUES
(1, 1, 'Подтверждено. Изучаю проблему с CSS.', '2024-03-01 11:35:00'),
(2, 1, 'Работаю над исправлением. Связано с логикой локализации.', '2024-03-06 09:05:00'),
(4, 1, 'Исправление развернуто. Закрываю эту задачу.', '2024-03-08 17:00:00'),
(1, 2, 'Все еще наблюдаю эту проблему в Firefox 110.', '2024-03-09 10:00:00');
Задания:
- Базовый индекс: Создайте базовый индекс для столбца
status
в таблицеBugs
, так как фильтрация по статусу очень распространена. - Индекс по внешнему ключу: Создайте индекс для столбца
project_id
в таблицеBugs
, чтобы быстро находить все ошибки, связанные с конкретным проектом. - Индекс по внешнему ключу: Создайте индекс для столбца
reported_by_user_id
в таблицеBugs
. - Индекс по внешнему ключу (допускающий NULL): Создайте индекс для столбца
assigned_to_user_id
в таблицеBugs
. Обратите внимание, что этот столбец может содержать значения NULL. - Составной индекс: Создайте составной индекс для столбцов
(project_id, status)
в таблицеBugs
, чтобы эффективно фильтровать ошибки внутри конкретного проекта по их статусу. - Составной индекс: Создайте еще один составной индекс для столбцов
(assigned_to_user_id, status)
в таблицеBugs
, чтобы помочь пользователям быстро находить назначенные им ошибки, отфильтрованные по статусу. - Частичный индекс: Создайте частичный индекс для столбца
updated_at
в таблицеBugs
, но только для тех ошибок, которые не находятся в статусе ‘resolved’ или ‘closed’ (т. е. для активных ошибок). Это оптимизирует запросы на поиск недавно обновленных активных задач.WHERE status NOT IN ('resolved', 'closed')
- Индекс по внешнему ключу (Комментарии): Создайте индекс для столбца
bug_id
в таблицеBugComments
, чтобы быстро извлекать все комментарии для конкретной ошибки. - Объяснение уникальных индексов: Таблица
Users
имеет ограниченияUNIQUE
для столбцовusername
иemail
. Объясните, почему обычно не требуется вручную создавать базовые B-tree индексы для этих конкретных столбцов. - Вывод списка и удаление индекса: Используйте системное представление каталога
pg_indexes
, чтобы вывести список всех индексов, определенных в настоящее время для таблицыBugs
. Найдите, а затем удалите базовый индекс, созданный для столбцаstatus
в шаге 1.
Инструкции по сдаче
- Создайте новый Google Doc. Пример
- Добавьте ссылку на ваш Google Doc здесь: Google Таблица