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


ОТВЕТЫ НА ТЕОРЕТИЧЕСКИЕ ВОПРОСЫ ИЗ СТАРЫХ БИЛЕТОВ

Файл сделан для быстрого копирования.
Все ответы написаны в простом стиле и с примерами.


1. Элементы и описание нотации IDEF1X.

IDEF1X - нотация для моделирования данных. Она используется, чтобы описать сущности, атрибуты, ключи и связи между сущностями.

Основные элементы IDEF1X:
1. сущность - объект предметной области, который потом обычно становится таблицей;
2. атрибут - свойство сущности, которое потом становится столбцом;
3. первичный ключ - набор атрибутов, который уникально определяет строку;
4. внешний ключ - ссылка на ключ другой сущности;
5. связь - линия между сущностями;
6. идентифицирующая связь - ключ родителя входит в ключ потомка;
7. неидентифицирующая связь - потомок имеет свой ключ, а ключ родителя хранится как внешний ключ.

Пример:
Есть сущность aircrafts с ключом aircraft_code и сущность flights с ключом flight_id. В flights есть aircraft_code как внешний ключ. Это связь один ко многим: один самолет может выполнять много рейсов.

В SQL это выглядит так:

CREATE TABLE aircrafts_data (
    aircraft_code bpchar PRIMARY KEY,
    model jsonb,
    range integer
);

CREATE TABLE flights (
    flight_id integer PRIMARY KEY,
    aircraft_code bpchar REFERENCES aircrafts_data(aircraft_code)
);

Главная идея IDEF1X - показать структуру данных до создания таблиц в СУБД.


2. Особенности построения индексных структур: B-деревья, битовые карты и другие.

Индекс - дополнительная структура данных, которая ускоряет поиск строк в таблице. Индекс занимает место и замедляет изменение данных, потому что при INSERT, UPDATE и DELETE его тоже нужно обновлять.

Основной индекс в PostgreSQL - B-tree. Он похож на сбалансированное сильно ветвящееся дерево. В нем данные упорядочены, поэтому можно быстро искать значения и диапазоны.

B-tree хорошо подходит для:
1. равенства;
2. сравнений больше или меньше;
3. BETWEEN;
4. ORDER BY;
5. поиска максимума и минимума.

Пример:

CREATE INDEX ticket_flights_amount_idx
ON ticket_flights(amount);

SELECT *
FROM ticket_flights
WHERE amount > 200000;

Если условие выбирает небольшую часть таблицы, индекс обычно выгоден. Если выбирается почти вся таблица, планировщик может выбрать Seq Scan.

Bitmap-подход:
Сначала по индексу строится битовая карта подходящих строк, потом таблица читается по этой карте. Это удобно, когда строк достаточно много, но не вся таблица.

Пример плана:
Bitmap Index Scan ищет строки в индексе.
Bitmap Heap Scan читает строки из таблицы.

Другие индексы:
Hash - для равенства.
GIN - для массивов, jsonb, полнотекстового поиска.
GiST - для геометрии и специальных типов.
BRIN - для очень больших таблиц, где данные физически упорядочены.


3. Синтаксис, описание и примеры использования хранимых процедур и функций.

Функция - объект БД, который принимает параметры, выполняет код и возвращает значение или таблицу. Ее можно использовать в SELECT.

Общий синтаксис функции:

CREATE OR REPLACE FUNCTION name(param type)
RETURNS type AS $$
BEGIN
    ...
    RETURN ...;
END;
$$ LANGUAGE plpgsql;

Пример функции:

CREATE OR REPLACE FUNCTION get_bookings_by_passenger(p_passenger_id varchar)
RETURNS TABLE(book_ref bpchar, book_date timestamptz, total_amount numeric) AS $$
BEGIN
    RETURN QUERY
    SELECT DISTINCT b.book_ref, b.book_date, b.total_amount
    FROM bookings b
    JOIN tickets t ON t.book_ref = b.book_ref
    WHERE t.passenger_id = p_passenger_id;
END;
$$ LANGUAGE plpgsql;

SELECT *
FROM get_bookings_by_passenger('8149 604011');

Процедура похожа на функцию, но вызывается через CALL и обычно используется для действий с данными. Процедура не обязана возвращать значение.

Пример процедуры:

CREATE OR REPLACE PROCEDURE cancel_flights_by_aircraft(p_aircraft bpchar) AS $$
BEGIN
    UPDATE flights
    SET status = 'Cancelled'
    WHERE aircraft_code = p_aircraft;
END;
$$ LANGUAGE plpgsql;

CALL cancel_flights_by_aircraft('733');

Разница:
Функция чаще используется для вычисления и возврата результата.
Процедура чаще используется для выполнения действия.


4. Описание, синтаксис и примеры использования оконных функций.

Оконная функция считает значение по набору строк, связанному с текущей строкой. При этом строки не сворачиваются, в отличие от GROUP BY.

Синтаксис:

function(...) OVER (
    PARTITION BY ...
    ORDER BY ...
    ROWS BETWEEN ... AND ...
)

PARTITION BY делит данные на группы.
ORDER BY задает порядок внутри группы.
ROWS или RANGE задает рамку окна.

Пример ранжирования самолетов:

SELECT aircraft_code,
       model,
       range,
       rank() OVER (ORDER BY range DESC) AS rnk,
       dense_rank() OVER (ORDER BY range DESC) AS dense_rnk
FROM aircrafts_data;

rank делает ранг с пропусками.
dense_rank делает ранг без пропусков.

Пример накопительной суммы:

SELECT b.book_date::date AS day,
       sum(b.total_amount) AS day_sum,
       sum(sum(b.total_amount)) OVER (
           ORDER BY b.book_date::date
       ) AS running_sum
FROM bookings b
GROUP BY b.book_date::date
ORDER BY day;

Оконные функции удобны для рейтингов, накопительных итогов, сравнения строки со средним или максимумом по группе.


5. Уточнение и обобщение, композиция, агрегирование, суперклассы и подклассы в модели сущность-связь.

Обобщение - объединение нескольких похожих сущностей в одну более общую.
Пример: пассажир и сотрудник могут быть обобщены до сущности Человек.

Уточнение - разбиение общей сущности на более частные.
Пример: билет можно уточнить как билет эконом-класса, бизнес-класса и комфорт-класса.

Суперкласс - общая сущность.
Подкласс - частная сущность, которая наследует свойства суперкласса.

Композиция - сильная связь часть-целое. Часть обычно не существует без целого.
Пример: посадочный талон относится к конкретному билету и рейсу. Без них он не имеет смысла.

Агрегирование - когда связь между сущностями рассматривается как отдельная сущность.
Пример: связь "пассажир летит рейсом" можно представить таблицей ticket_flights. У нее есть свои атрибуты: ticket_no, flight_id, fare_conditions, amount.

Пример из авиаперевозок:
Сущности tickets и flights связаны многие ко многим. Эта связь реализована через ticket_flights. Поэтому ticket_flights можно рассматривать как сущность-пересечение.


6. Изучение предметной области. Формализация, смысловое описание, бизнес-правила.

Изучение предметной области - первый этап проектирования БД. На этом этапе выясняют, какие данные нужно хранить, кто ими пользуется и какие операции нужны.

Смысловое описание - текстовое описание предметной области обычным языком.
Например: "Система хранит информацию о рейсах, пассажирах, билетах, бронированиях и самолетах".

Формализация - перевод смыслового описания в строгую модель: сущности, атрибуты, связи, ключи, ограничения.

Бизнес-правила - правила, которые должны выполняться в предметной области.
Примеры:
1. стоимость билета не может быть отрицательной;
2. рейс должен ссылаться на существующий самолет;
3. один билет может включать несколько перелетов;
4. место в посадочном талоне должно быть связано с конкретным рейсом.

Пример реализации бизнес-правила:

ALTER TABLE ticket_flights
ADD CONSTRAINT amount_positive CHECK (amount >= 0);

Пример внешнего ключа:

ALTER TABLE flights
ADD CONSTRAINT flights_aircraft_fkey
FOREIGN KEY (aircraft_code)
REFERENCES aircrafts_data(aircraft_code);


7. Соединения таблиц на основе слияния: особенности реализации, сложность алгоритмов.

Merge Join - соединение на основе слияния. Оно работает, когда оба набора строк отсортированы по ключу соединения.

Алгоритм:
1. первая таблица сортируется по ключу соединения или читается по индексу;
2. вторая таблица тоже сортируется или читается по индексу;
3. СУБД идет по двум отсортированным наборам и соединяет совпадающие ключи.

Сложность:
Если данные уже отсортированы, сложность примерно O(n + m).
Если нужна сортировка, добавляется стоимость сортировки: O(n log n + m log m).

Когда Merge Join выгоден:
1. таблицы большие;
2. данные уже упорядочены индексами;
3. результат нужно вернуть в отсортированном виде.

Пример:

SELECT t.ticket_no, tf.flight_id
FROM tickets t
JOIN ticket_flights tf ON tf.ticket_no = t.ticket_no
ORDER BY t.ticket_no;

Если tickets_pkey и ticket_flights_pkey дают нужный порядок, PostgreSQL может выбрать Merge Join без отдельного Sort.


8. Особенности использования транзакций. Блокировка на уровне строк и таблиц, другой вариант блокировки.

Транзакция - набор действий, который выполняется как единое целое.

Пример:

BEGIN;
UPDATE bookings
SET total_amount = total_amount + 100
WHERE book_ref = '00000F';
COMMIT;

Если нужно отменить изменения:

BEGIN;
UPDATE bookings
SET total_amount = total_amount + 100
WHERE book_ref = '00000F';
ROLLBACK;

Блокировка нужна, чтобы параллельные транзакции не изменяли одни и те же данные некорректно.

Строковая блокировка:

BEGIN;
SELECT *
FROM bookings
WHERE book_ref = '00000F'
FOR UPDATE;
COMMIT;

FOR UPDATE блокирует выбранные строки.

Табличная блокировка:

BEGIN;
LOCK TABLE bookings IN SHARE MODE;
COMMIT;

Другой вариант блокировки - консультативные блокировки advisory locks. Они не привязаны напрямую к строкам таблицы, приложение само решает, что означает такая блокировка.

Пример:

SELECT pg_advisory_lock(100);
SELECT pg_advisory_unlock(100);


9. Соединения таблиц на основе вложенных циклов и хеширования: особенности реализации, сложность алгоритмов.

Nested Loop - соединение вложенными циклами.
Для каждой строки внешней таблицы СУБД ищет подходящие строки во внутренней таблице.

Сложность без индекса примерно O(n * m).
Если есть индекс по ключу внутренней таблицы, может быть намного быстрее.

Пример:

SELECT f.flight_id, a.airport_name
FROM flights f
JOIN airports a ON a.airport_code = f.departure_airport
WHERE f.flight_id = 1;

Hash Join - соединение через хеширование.
Для одной таблицы строится hash-таблица по ключу соединения, затем строки второй таблицы ищутся в ней.

Сложность примерно O(n + m).

Пример:

SELECT f.flight_id, a.airport_name
FROM flights f
JOIN airports a ON a.airport_code = f.arrival_airport;

Nested Loop выгоден для маленьких наборов или при хороших индексах.
Hash Join выгоден для больших неотсортированных таблиц.


10. Способы организации, назначение, характеристики и примеры распределенных СУБД.

Распределенная СУБД хранит данные на нескольких узлах, но предоставляет пользователю единый доступ.

Назначение:
1. масштабирование;
2. отказоустойчивость;
3. распределение нагрузки;
4. хранение данных ближе к пользователям.

Способы организации:
1. репликация - копии данных на разных узлах;
2. шардирование - разделение строк по узлам;
3. фрагментация - разделение таблиц на части;
4. федерация - объединение нескольких баз.

Характеристики:
1. прозрачность расположения данных;
2. устойчивость к отказам;
3. сложность согласованности;
4. сетевые задержки;
5. необходимость распределенных транзакций или eventual consistency.

Примеры:
Cassandra, MongoDB cluster, CockroachDB, PostgreSQL с репликацией и шардингом.

Пример шардирования:
Бронирования можно распределять по узлам по диапазонам book_ref или по хешу book_ref.


11. Описание и примеры использования уровней изоляции транзакций.

Уровень изоляции определяет, какие изменения других транзакций видит текущая транзакция.

READ UNCOMMITTED:
В PostgreSQL фактически работает как READ COMMITTED. Грязное чтение не допускается.

READ COMMITTED:
Каждый оператор видит только зафиксированные данные на момент начала оператора. Повторный SELECT может увидеть новые данные, если другая транзакция сделала COMMIT.

REPEATABLE READ:
Транзакция видит снимок данных на момент начала транзакции. Повторные SELECT дают один и тот же результат.

SERIALIZABLE:
Самый строгий уровень. Результат должен быть таким, как будто транзакции выполнялись последовательно. Возможны ошибки сериализации, тогда транзакцию надо повторить.

Пример:

BEGIN ISOLATION LEVEL REPEATABLE READ;
SELECT total_amount
FROM bookings
WHERE book_ref = '00000F';
COMMIT;

Что важно:
Чем выше уровень изоляции, тем больше защита от аномалий, но тем выше вероятность ожиданий, конфликтов и ошибок сериализации.


12. Основные системы хранения данных и знаний и их особенности.

Основные виды систем:
1. реляционные БД;
2. key-value хранилища;
3. документоориентированные БД;
4. колоночные БД;
5. графовые БД;
6. хранилища знаний.

Реляционные БД:
Данные хранятся в таблицах. Есть SQL, ключи, ограничения, транзакции.
Примеры: PostgreSQL, MySQL, Oracle.

Key-value:
Данные хранятся как ключ и значение. Очень быстрый доступ по ключу.
Примеры: Redis, DynamoDB.

Документоориентированные:
Данные хранятся как документы JSON или BSON.
Примеры: MongoDB, CouchDB.

Колоночные:
Данные хранятся по столбцам. Хорошо для аналитики и больших объемов.
Примеры: ClickHouse, Cassandra.

Графовые:
Данные хранятся как вершины и ребра.
Пример: Neo4j.

Хранилища знаний:
Хранят факты, правила, связи, онтологии.
Примеры: RDF-хранилища, OWL-онтологии.


13. Чтение плана выполнения запроса. EXPLAIN: синтаксис и использование, ключевые параметры.

EXPLAIN показывает план выполнения запроса.

Синтаксис:

EXPLAIN
SELECT ...

EXPLAIN ANALYZE реально выполняет запрос и показывает фактическое время.

EXPLAIN (ANALYZE, BUFFERS)
SELECT *
FROM ticket_flights
WHERE amount > 200000;

Что смотреть:
1. Seq Scan - последовательное чтение таблицы;
2. Index Scan - чтение через индекс;
3. Bitmap Index Scan и Bitmap Heap Scan - поиск через индекс и чтение таблицы по bitmap;
4. Nested Loop, Hash Join, Merge Join - способы соединения;
5. Sort - сортировка;
6. Aggregate, HashAggregate, GroupAggregate - агрегирование;
7. cost - оценочная стоимость;
8. rows - оценка количества строк;
9. actual time - фактическое время;
10. loops - число повторов узла;
11. Buffers - чтение из памяти и с диска.

Пример пояснения:
Если в плане есть Parallel Seq Scan, Gather, Partial Aggregate и Finalize Aggregate, запрос выполняется параллельно. Рабочие процессы считают частичные суммы, а главный процесс собирает итог.


14. Описание, синтаксис и примеры использования сложных подзапросов.

Сложный подзапрос - вложенный запрос, который может возвращать одно значение, строку, столбец или таблицу. Он может быть коррелированным, то есть зависеть от внешнего запроса.

Пример подзапроса в WHERE:

SELECT book_ref, total_amount
FROM bookings
WHERE total_amount > (
    SELECT avg(total_amount)
    FROM bookings
);

Пример IN:

SELECT passenger_name
FROM tickets
WHERE ticket_no IN (
    SELECT ticket_no
    FROM ticket_flights
    WHERE amount > 200000
);

Пример EXISTS:

SELECT t.ticket_no, t.passenger_name
FROM tickets t
WHERE EXISTS (
    SELECT 1
    FROM ticket_flights tf
    WHERE tf.ticket_no = t.ticket_no
);

Пример коррелированного подзапроса:

SELECT a.aircraft_code,
       (
           SELECT count(*)
           FROM flights f
           WHERE f.aircraft_code = a.aircraft_code
       ) AS flight_count
FROM aircrafts_data a;

Минус коррелированного подзапроса:
Он может выполняться много раз, по одному разу для каждой строки внешнего запроса.


15. Способы организации, назначение, характеристики и примеры хранилищ ключ-значение.

Key-value хранилище хранит данные в виде ключ - значение.

Пример:
ключ: passenger:123
значение: Ivan Petrov

Назначение:
1. кеш;
2. сессии пользователей;
3. счетчики;
4. быстрый доступ по идентификатору;
5. временные данные.

Характеристики:
1. очень быстрый доступ по ключу;
2. простая модель данных;
3. хорошее горизонтальное масштабирование;
4. нет сложных JOIN;
5. сложные выборки обычно делает приложение.

Пример Redis:

SET passenger:123 "Ivan Petrov"
GET passenger:123

Плюс:
Очень быстро.

Минус:
Если нужно искать по множеству условий и соединять данные, реляционная БД удобнее.


16. Транзакции. Атомарность, согласованность, изолированность, долговечность. Сериализация транзакций.

Транзакция - логическая единица работы с БД.

ACID:
Atomicity - атомарность. Все действия выполняются полностью или не выполняются.
Consistency - согласованность. После транзакции данные остаются корректными.
Isolation - изолированность. Параллельные транзакции не должны мешать друг другу.
Durability - долговечность. После COMMIT изменения сохраняются.

Пример:

BEGIN;
UPDATE bookings
SET total_amount = total_amount + 100
WHERE book_ref = '00000F';
COMMIT;

Если произошла ошибка:

ROLLBACK;

Сериализация транзакций - свойство, при котором результат параллельного выполнения такой же, как если бы транзакции выполнялись последовательно.

Пример:
Если две транзакции одновременно меняют одну сумму бронирования, СУБД должна не допустить потерянного изменения. Для этого используются изоляция, блокировки и MVCC.


17. Способы организации, назначение, характеристики и примеры документоориентированных баз данных.

Документоориентированная БД хранит данные в виде документов, чаще всего JSON или BSON.

Пример документа:

{
  "ticket_no": "0005432000987",
  "passenger_name": "Ivan Petrov",
  "flights": [
    {"flight_id": 10, "amount": 12500},
    {"flight_id": 11, "amount": 9300}
  ]
}

Назначение:
1. хранение данных с гибкой структурой;
2. хранение вложенных объектов;
3. быстрые веб-приложения;
4. работа с JSON-документами.

Характеристики:
1. нет жесткой схемы как в реляционной БД;
2. данные могут быть вложенными;
3. часто используется денормализация;
4. горизонтальное масштабирование обычно проще;
5. JOIN ограничены или отсутствуют.

Примеры:
MongoDB, CouchDB.

Плюс:
Удобно хранить сложные JSON-объекты.

Минус:
Сложнее поддерживать строгую целостность и сложные связи между данными.