Базы данных
Транзакции, уровни изоляции, JOIN, индексы и план запроса.
19 вопросов
JuniorТеорияОчень частоКакие бывают типы JOIN и чем они отличаются?
Какие бывают типы JOIN и чем они отличаются?
INNER JOIN оставляет лишь строки, совпавшие в обеих таблицах. LEFT/RIGHT OUTER JOIN берут все строки одной стороны с NULL для отсутствующей. FULL OUTER JOIN хранит несовпавшие строки обеих сторон. Условие — в ON или USING.
Типичные ошибки
- ✗Думать, что
INNER JOINоставляет все строки левой таблицы - ✗Считать, что
LEFT JOINотбрасывает несовпавшие левые строки вместоNULL - ✗Полагать, что
FULL OUTER JOINвозвращает только совпадающие строки
Уточняющие вопросы
- →Когда вместо
ONвJOINможно использоватьUSING? - →Что делает self-join и когда он полезен?
JuniorТеорияЧастоЧто такое курсор базы данных и зачем он нужен?
Что такое курсор базы данных и зачем он нужен?
Курсор — это указатель на строку результата запроса, позволяющий брать строки по одной, а не все сразу. Он полезен для огромных результатов, но построчная обработка медленнее множественного SQL, поэтому без нужды его лучше избегать.
Типичные ошибки
- ✗Путать курсор БД с указателем мыши в интерфейсе
- ✗Считать курсор быстрее обычного множественного
SELECT - ✗Забывать, что построчная обработка медленна и её лучше избегать
Уточняющие вопросы
- →Чем серверный курсор отличается от клиентского?
- →Почему курсор может снизить расход памяти на очень большом результате?
JuniorКодЧастоЗапрос клиентов, не сделавших ни одного заказа
Запрос клиентов, не сделавших ни одного заказа
Сделайте left join с заказами и оставьте несопоставленные строки: SELECT c.id, c.name FROM Customers c LEFT JOIN Orders o ON c.id = o.customer_id WHERE o.customer_id IS NULL. Анти-join LEFT JOIN ... IS NULL оставляет ровно тех клиентов, у кого нет подходящего заказа.
Типичные ошибки
- ✗Использовать
!=в условии join в смысле «нет совпадения» - ✗Доверять
NOT IN, когда подзапрос может содержать NULL - ✗Делать inner join (который выбрасывает клиентов без заказов), затем считать ноль
Уточняющие вопросы
- →Почему
NOT INможет дать неверный результат, когда подзапрос возвращает NULL? - →Как
NOT EXISTSвыражает тот же анти-join безопасно?
JuniorКодЧастоНапишите запрос для поиска повторяющихся email
Напишите запрос для поиска повторяющихся email
Сгруппируйте по столбцу и оставьте группы размером > 1: SELECT email FROM Person GROUP BY email HAVING COUNT(*) > 1. Фильтровать агрегат нужно через HAVING, а не WHERE — WHERE вычисляется до группировки строк, поэтому не видит COUNT(*).
Типичные ошибки
- ✗Ставить
COUNT(*)вWHEREвместоHAVING - ✗Думать, что
DISTINCTнаходит дубликаты, а не убирает их - ✗Ожидать, что наивный self-join выделит только дубликаты
Уточняющие вопросы
- →Почему
WHEREне может фильтровать по агрегату вродеCOUNT(*)? - →Как заодно вернуть, сколько раз встречается каждый повторяющийся email?
JuniorТеорияЧастоЧто такое транзакция, и что означает ACID?
Что такое транзакция, и что означает ACID?
Транзакция — это последовательность операций БД как одна единица. ACID: атомарность (всё или ничего, иначе откат), согласованность (только корректные состояния), изоляция (транзакции не мешают), долговечность (зафиксированные данные переживут сбой).
Типичные ошибки
- ✗Считать транзакцию одним SQL-оператором, а не группой операций
- ✗Путать согласованность с изоляцией или смысл каждой буквы
ACID - ✗Считать, что долговечность — это шифрование, а не выживание после сбоя
Уточняющие вопросы
- →Какое свойство
ACIDнапрямую обеспечиваетROLLBACK? - →Как БД гарантирует долговечность после потери питания?
JuniorТеорияЧастоКакие команды управления транзакциями ты знаешь?
Какие команды управления транзакциями ты знаешь?
COMMIT сохраняет изменения навсегда, ROLLBACK отменяет их, SAVEPOINT отмечает точку частичного отката, а SET TRANSACTION настраивает свойства. Они применяются к DML (INSERT/UPDATE/DELETE), а не к DDL вроде CREATE.
Типичные ошибки
- ✗Менять местами смысл
COMMITиROLLBACK - ✗Думать, что
SAVEPOINTфиксирует, а не отмечает точку отката - ✗Считать, что эти команды управляют DDL-изменениями схемы, а не DML
Уточняющие вопросы
- →Вызывает ли DDL-оператор
CREATE TABLEнеявныйCOMMIT? - →Как откатиться к
SAVEPOINT, не теряя всю транзакцию?
MiddleКодЧастоПодсчёт сотрудников по отделам, включая пустые
Подсчёт сотрудников по отделам, включая пустые
Сделайте left join от отделов и посчитайте соединённый ключ: SELECT d.name, COUNT(e.id) FROM Departments d LEFT JOIN Employee e ON e.dept_id = d.id GROUP BY d.id, d.name. Используйте COUNT(e.id) (а не COUNT(*)), чтобы пустой отдел показал 0 — COUNT(*) посчитал бы одну строку с NULL как 1.
Типичные ошибки
- ✗Использовать
COUNT(*)наLEFT JOIN, считая NULL-строку за 1 - ✗Делать inner join, который вовсе выбрасывает пустые отделы
- ✗Агрегировать только
Employee, из-за чего отделы без штата не появятся
Уточняющие вопросы
- →Почему
COUNT(e.id)возвращает 0, аCOUNT(*)— 1 для пустого отдела? - →Что должно быть в
GROUP BY, когда вы выбираетеd.nameрядом со счётчиком?
MiddleКодЧастоНапишите запрос для второй по величине зарплаты
Напишите запрос для второй по величине зарплаты
Возьмите максимум зарплаты строго ниже общего максимума: SELECT MAX(salary) FROM Employee WHERE salary < (SELECT MAX(salary) FROM Employee). Это аккуратно возвращает NULL, когда второй зарплаты нет. Альтернатива: ORDER BY salary DESC LIMIT 1 OFFSET 1 по DISTINCT зарплатам.
Типичные ошибки
- ✗Забывать
DISTINCT, из-за чего повторяющиеся верхние зарплаты ломают подход со смещением - ✗Ставить агрегат вроде
MAX()прямо в условиеWHERE - ✗Путать значение зарплаты с её рангом/порядковым номером
Уточняющие вопросы
- →Как обобщить это до N-й по величине зарплаты?
- →Почему версия с подзапросом возвращает
NULL, а не пустой результат при единственной зарплате?
MiddleКодЧастоЗапрос самого высокооплачиваемого в каждом отделе
Запрос самого высокооплачиваемого в каждом отделе
Проранжируйте внутри каждого отдела и оставьте ранг 1: SELECT * FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn FROM Employee) t WHERE rn = 1. PARTITION BY dept_id перезапускает ранжирование на каждый отдел — каноничный паттерн top-N в группе.
Типичные ошибки
- ✗Выбирать неагрегированное
nameрядом сMAX(salary)вGROUP BY - ✗Применять глобальный
LIMITвместо ранга по разделу - ✗Фильтровать по максимуму всей компании, а не по отделу
Уточняющие вопросы
- →Как вернуть топ-3 по зарплате в каждом отделе вместо только первого?
- →Почему
ROW_NUMBERпо разделу лучше коррелированного подзапроса здесь?
JuniorКодИногдаЗапрос сотрудников, зарабатывающих больше руководителей
Запрос сотрудников, зарабатывающих больше руководителей
Сделайте self-join по связи с руководителем и сравните зарплаты: SELECT e.name FROM Employee e JOIN Employee m ON e.manager_id = m.id WHERE e.salary > m.salary. Таблица соединяется сама с собой: одна копия — сотрудник, другая — руководитель, через manager_id → id.
Типичные ошибки
- ✗Сравнивать со средним по компании, а не с конкретным руководителем
- ✗Соединять по отделу вместо связи
manager_id → id - ✗Забывать отдельно алиасить две копии таблицы
Уточняющие вопросы
- →Как заодно показать сотрудников без руководителя (NULL в
manager_id)? - →Почему обе стороны self-join должны нести разные алиасы таблицы?
MiddleТеорияИногдаВ чём разница между EXPLAIN и EXPLAIN ANALYZE?
В чём разница между EXPLAIN и EXPLAIN ANALYZE?
EXPLAIN показывает выбранный план с оценочной стоимостью, не выполняя запрос. EXPLAIN ANALYZE реально выполняет его и сообщает фактические тайминги и число строк — включая побочные эффекты, поэтому INSERT/UPDATE/DELETE оборачивай в транзакцию с откатом.
Типичные ошибки
- ✗Путать, какая команда оценивает, а какая реально выполняет
- ✗Думать, что обе только оценивают и не выполняют запрос
- ✗Забывать, что
EXPLAIN ANALYZEвыполняет запись и требует отката
Уточняющие вопросы
- →Что означают
BUFFERSиcostв выводеEXPLAIN? - →Почему оценка строк планировщика может резко расходиться с фактической?
MiddleТеорияИногдаЧто такое уровни изоляции транзакций?
Что такое уровни изоляции транзакций?
Они меняют изоляцию на параллелизм, задавая допустимые аномалии: Read Uncommitted (грязное чтение), Read Committed (без него), Repeatable Read (без неповторяющегося), Serializable (без аномалий). Высокие уровни медленнее.
Типичные ошибки
- ✗Считать, что существует только один уровень изоляции
- ✗Думать, что Serializable — самый слабый уровень, а не самый строгий
- ✗Полагать, что высокая изоляция повышает пропускную способность
Уточняющие вопросы
- →Какой уровень изоляции в
PostgreSQLиспользуется по умолчанию? - →Чем Repeatable Read на практике отличается от настоящего снимка?
MiddleКодИногдаНапишите запрос для N-й по величине зарплаты с одинаковыми
Напишите запрос для N-й по величине зарплаты с одинаковыми
Проранжируйте зарплаты оконной функцией и отфильтруйте по рангу: SELECT salary FROM (SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk FROM Employee) t WHERE rnk = :N. DENSE_RANK даёт равным зарплатам одинаковый ранг без пропусков в нумерации.
Типичные ошибки
- ✗Использовать
ROW_NUMBER, где равные должны делить ранг (нуженDENSE_RANK) - ✗Полагаться на
OFFSETбезDISTINCT, из-за чего равенства сдвигают позицию - ✗Путать
RANK(с пропусками) иDENSE_RANK(без пропусков)
Уточняющие вопросы
- →Когда для этой задачи выбрать
RANK, а неDENSE_RANK? - →Чем
ROW_NUMBERведёт себя иначе, чемDENSE_RANK, при равных зарплатах?
MiddleТеорияИногдаЧем отличаются PostgreSQL и MySQL?
Чем отличаются PostgreSQL и MySQL?
PostgreSQL — объектно-реляционная, ближе к стандартам и богаче (богатые типы, CTE, оконные запросы, серверные курсоры). MySQL предлагает подключаемые движки хранения вроде InnoDB и исторически заточена под скорость чтения и поиск по ключу.
Типичные ошибки
- ✗Утверждать, что
MySQLполностью соответствует стандартам, аPostgreSQLнет - ✗Считать, что у
PostgreSQLнет транзакций - ✗Полагать, что у обеих идентичный набор возможностей
Уточняющие вопросы
- →Что позволяет выбрать на уровне таблицы модель подключаемых движков
MySQL? - →Какие возможности
PostgreSQLделают её привлекательной для аналитики?
MiddleКодИногдаВычислите нарастающий итог зарплат по дате найма
Вычислите нарастающий итог зарплат по дате найма
Используйте оконный SUM с упорядоченной рамкой: SELECT id, name, salary, SUM(salary) OVER (ORDER BY hire_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total FROM Employee. Рамка накапливает от первой строки до текущей.
Типичные ошибки
- ✗Использовать
GROUP BY(схлопывающий строки) вместо оконной функции - ✗Опускать
ORDER BYв окне, из-за чегоSUMвозвращает общий итог на строку - ✗Браться за self-join, когда оконная рамка куда проще
Уточняющие вопросы
- →Почему пропуск
ORDER BYв окне превращает нарастающий итог в общий? - →Как
PARTITION BY dept_idменяет поведение нарастающего итога?
MiddleТеорияРедкоСуществуют ли в стандартном SQL вложенные транзакции и как ведёт себя COMMIT?
Существуют ли в стандартном SQL вложенные транзакции и как ведёт себя COMMIT?
В стандартном SQL/PostgreSQL настоящих вложенных транзакций нет: транзакция, начатая внутри активной, входит в ту же внешнюю транзакцию. COMMIT фиксирует всю внешнюю транзакцию сразу; реальная вложенность эмулируется через SAVEPOINT, позволяющий частичный откат.
Типичные ошибки
- ✗Думать, что внутренний откат оставляет внешние изменения зафиксированными
- ✗Считать, что каждый уровень фиксируется полностью и независимо
- ✗Считать, что вложенный
BEGINзапускает настоящую независимую транзакцию, а не входит во внешнюю
Уточняющие вопросы
- →Как
SAVEPOINTэмулирует вложенные транзакции вPostgreSQL? - →Что станет с внутренней работой, если откатится сама внешняя транзакция?
MiddleТеорияРедкоЧто делает VACUUM в PostgreSQL?
Что делает VACUUM в PostgreSQL?
При MVCC обновления и удаления оставляют старые версии строк («мёртвые кортежи»), не удаляя их сразу. VACUUM освобождает это место и обновляет видимость и статистику, поэтому его надо запускать периодически, особенно на изменяемых таблицах.
Типичные ошибки
- ✗Думать, что
VACUUMудаляет живые пользовательские данные - ✗Считать, что он дефрагментирует диск на уровне ОС
- ✗Полагать, что мёртвые кортежи удаляются мгновенно без
VACUUM
Уточняющие вопросы
- →Чем
VACUUM FULLотличается от обычногоVACUUM? - →Что делает autovacuum и когда он срабатывает?
SeniorТеорияРедкоКакие аномалии параллелизма предотвращают уровни изоляции?
Какие аномалии параллелизма предотвращают уровни изоляции?
Грязное чтение (видны незафиксированные данные) — блокируется с Read Committed. Неповторяющееся чтение (повтор видит изменённую строку) — с Repeatable Read. Фантомное чтение (повтор запроса видит новые строки) — на Serializable, через 2PL или SSI.
Типичные ошибки
- ✗Думать, что Read Committed предотвращает фантомные чтения
- ✗Считать, что Serializable всё ещё допускает грязное чтение
- ✗Полагать, что все аномалии исчезают на Read Committed
Уточняющие вопросы
- →Чем SSI отличается от классической двухфазной блокировки?
- →Что такое аномалия write skew и какой уровень её предотвращает?
SeniorТеорияРедкоКак MVCC обеспечивает снимочную изоляцию?
Как MVCC обеспечивает снимочную изоляцию?
MVCC хранит несколько версий строк; каждая транзакция читает из снимка на момент своего старта, поэтому читатели не блокируют писателей и наоборот, давая повторяемое чтение без блокировок. Старые версии — это мёртвые кортежи, которые убирает VACUUM.
Типичные ошибки
- ✗Считать, что
MVCCблокирует каждую строку при чтении - ✗Думать, что хранится одна версия, поэтому читатели блокируют писателей
- ✗Полагать, что мусорные строки не возникают и
VACUUMне нужен
Уточняющие вопросы
- →Как
xminиxmaxзадают видимость версии строки вPostgreSQL? - →Почему долгие транзакции вызывают раздувание таблиц при
MVCC?