Найти в Дзене
Записки о Java

15 важнейших вопросов по SQL для Java-разработчика

-- Пример: пользователи и заказы SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id; -- Покажет всех пользователей, даже без заказов Практика в Java: Используйте LEFT JOIN FETCH в JPQL, чтобы избежать N+1 при загрузке связей. Индекс — это структура данных (обычно B-дерево), которая ускоряет поиск по столбцу(ам), подобно оглавлению в книге. Создавайте индекс, если: ⚠️ Не создавайте индексы на маленьких таблицах — полный скан быстрее. EXPLAIN показывает план выполнения запроса — какие индексы используются, сколько строк просканировано, есть ли Seq Scan (полный перебор). EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123; Ищите: 💡 В Java: Логируйте медленные запросы через spring.jpa.properties.hibernate.generate_statistics=true. N+1 — когда вы делаете 1 запрос для получения N записей, а затем N запросов для каждой связи. // Плохо: вызовет N+1 List<User> users = userRepository.findAll(); for (User u : users) { System.out.println(u.getOrders().size()); // laz
Оглавление
Рисунок: вопросы на собеседовании
Рисунок: вопросы на собеседовании

Вопрос 1. В чём разница между INNER JOIN, LEFT JOIN и FULL OUTER JOIN? Приведите пример.

Ответ:

  • INNER JOIN — возвращает только строки, присутствующие в обеих таблицах.
  • LEFT JOIN — возвращает все строки из левой таблицы, даже если нет совпадений справа (правые поля = NULL).
  • FULL OUTER JOIN — возвращает все строки из обеих таблиц, заполняя NULL там, где нет совпадений.

-- Пример: пользователи и заказы

SELECT u.name, o.amount

FROM users u

LEFT JOIN orders o ON u.id = o.user_id;

-- Покажет всех пользователей, даже без заказов

Практика в Java: Используйте LEFT JOIN FETCH в JPQL, чтобы избежать N+1 при загрузке связей.

🔹 Вопрос 2. Что такое индекс? Как он работает? Когда его стоит создавать?

Ответ:

Индекс — это структура данных (обычно B-дерево), которая ускоряет поиск по столбцу(ам), подобно оглавлению в книге.

  • Ускоряет: WHERE, JOIN, ORDER BY.
  • Замедляет: INSERT, UPDATE, DELETE (индекс тоже нужно обновлять).

Создавайте индекс, если:

  • Колонка часто используется в WHERE,
  • Выборка составляет < 10–15% от таблицы,
  • Это внешний ключ (user_id в orders).
⚠️ Не создавайте индексы на маленьких таблицах — полный скан быстрее.

🔹 Вопрос 3. Что такое EXPLAIN и как его использовать для оптимизации запросов?

Ответ:

EXPLAIN показывает план выполнения запроса — какие индексы используются, сколько строк просканировано, есть ли Seq Scan (полный перебор).

EXPLAIN ANALYZE

SELECT * FROM orders WHERE user_id = 123;

Ищите:

  • Index Scan → хорошо,
  • Seq Scan на большой таблице → плохо,
  • высокое значение rows → возможно, не хватает индекса.
💡 В Java: Логируйте медленные запросы через spring.jpa.properties.hibernate.generate_statistics=true.

🔹 Вопрос 4. Что такое N+1 проблема? Как её решить на уровне SQL и Hibernate?

Ответ:

N+1 — когда вы делаете 1 запрос для получения N записей, а затем N запросов для каждой связи.

// Плохо: вызовет N+1

List<User> users = userRepository.findAll();

for (User u : users) {

System.out.println(u.getOrders().size()); // lazy load → N запросов

}

Решение в SQL:

SELECT u.*, o.*

FROM users u

LEFT JOIN orders o ON u.id = o.user_id

WHERE ...;

В Hibernate:

  • JOIN FETCH в JPQL,
  • @EntityGraph,
  • FetchMode.JOIN.
Правило: всегда проверяйте количество SQL-запросов в логах!

В вопрос 5. Что такое транзакция? Какие свойства ACID вы знаете?

Ответ:

Транзакция — это логическая единица работы, которая либо выполняется полностью, либо не выполняется вовсе.

ACID:

  • Atomicity — всё или ничего.
  • Consistency — данные переходят из одного согласованного состояния в другое.
  • Isolation — транзакции не мешают друг другу.
  • Durability — после коммита изменения сохраняются даже при сбое.
💡 В Spring: @Transactional управляет этими свойствами.

🔹 Вопрос 6. Какие уровни изоляции транзакций вы знаете? Чем грозит READ COMMITTED vs SERIALIZABLE?

Ответ:

Уровень

Проблемы

Производительность

READ UNCOMMITTED

Dirty Read

Самая высокая

READ COMMITTED (по умолчанию в PostgreSQL)

Non-repeatable Read

Хорошая

REPEATABLE READ

Phantom Read

Средняя

SERIALIZABLE

Нет проблем

Самая низкая

Dirty Read — чтение незафиксированных данных.
Non-repeatable Read — повторное чтение даёт другой результат.
Phantom Read — новые строки появляются при повторном запросе.

⚠️ В Java: Указывайте уровень явно:
@Transactional(isolation = Isolation.REPEATABLE_READ)

🔹 Вопрос 7. Что такое deadlock? Как его избежать?

Ответ:

Deadlock — циклическое ожидание:
Транзакция A ждёт ресурс у B, B — у A.

Как избежать:

  • Блокировать ресурсы в одинаковом порядке,
  • Использовать таймауты (lock_timeout),
  • Делать транзакции короткими.
💡 В PostgreSQL: автоматически выбирается жертва (deadlock detected), одна транзакция откатывается.

🔹 Вопрос 8. В чём разница между WHERE и HAVING?

Ответ:

  • WHERE фильтрует до группировки.
  • HAVING фильтрует после GROUP BY.

SELECT department, AVG(salary)

FROM employees

WHERE salary > 50000 -- до группировки

GROUP BY department

HAVING AVG(salary) > 70000; -- после группировки

Запомните: HAVING работает только с агрегатными функциями (COUNT, SUM, AVG).

🔹 Вопрос 9. Что такое CTE (Common Table Expression)? Зачем он нужен?

Ответ:

CTE — временный именованный результат, который можно использовать в запросе.

WITH high_salary AS (

SELECT * FROM employees WHERE salary > 100000

)

SELECT name, department

FROM high_salary

WHERE department = 'Engineering';

Преимущества:

  • Улучшает читаемость,
  • Можно рекурсивно вызывать (для иерархий),
  • Оптимизатор может лучше обработать запрос.
💡 В Java: Используйте CTE в native-запросах через @Query.

🔹 Вопрос 10. Как реализовать пагинацию в SQL? Какие есть проблемы?

Ответ:

OFFSET/LIMIT (PostgreSQL, MySQL):

SELECT * FROM orders ORDER BY id LIMIT 20 OFFSET 40;

роблемы:

  • OFFSET медленный на больших значениях (СУБД пропускает N строк),
  • Может пропустить/продублировать записи при изменении данных.

Решение: Keyset pagination (курсорная пагинация):

SELECT * FROM orders

WHERE id > 1000

ORDER BY id

LIMIT 20;

В Spring Data JPA: Pageable использует OFFSET, но для больших данных — пишите кастомный запрос.

🔹 Вопрос 11. Что такое оконные функции (Window Functions)? Приведите пример.

Ответ:

Оконные функции выполняют расчёт по группе строк, не сворачивая их (в отличие от GROUP BY).

SELECT

name,

salary,

AVG(salary) OVER (PARTITION BY department) AS dept_avg

FROM employees;

Популярные функции: ROW_NUMBER(), RANK(), LEAD(), LAG().

💡 Использование: Топ-N в группе, скользящее среднее, сравнение с предыдущим значением.

🔹 Вопрос 12. Что такое нормализация и денормализация? Когда что применять?

Ответ:

  • Нормализация — устранение дублирования данных (до 3NF). Уменьшает аномалии, но требует JOIN’ов.
  • Денормализация — намеренное дублирование для ускорения чтения.

Применяйте денормализацию, если:

  • Чтение >> записи,
  • JOIN’ы слишком дорогие,
  • Вы готовы к усложнению логики обновления.
⚠️ В микросервисах: Денормализация — норма (каждый сервис имеет свою БД).

🔹 Вопрос 13. Как найти дубликаты в таблице?

Ответ:

SELECT email, COUNT(*)

FROM users

GROUP BY email

HAVING COUNT(*) > 1;

Удаление дубликатов (оставить минимальный id):

DELETE FROM users

WHERE id NOT IN (

SELECT MIN(id)

FROM users

GROUP BY email

);

💡 В Java: Всегда добавляйте UNIQUE-ограничение на уровне БД!

🔹 Вопрос 14. Что такое UPSERT? Как его реализовать в PostgreSQL и MySQL?

Ответ:

UPSERT = UPDATE + INSERT — вставить, если нет, обновить, если есть.

PostgreSQL:

INSERT INTO users (id, name) VALUES (1, 'Alice')

ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;

В Spring Data JPA: Используйте save() — он делает merge, но для true upsert — native query.

🔹 Вопрос 15. Как обеспечить целостность данных на уровне приложения (Java) и БД?

Ответ:

На уровне БД (надёжнее!):

  • PRIMARY KEY, FOREIGN KEY,
  • UNIQUE, CHECK constraints,
  • NOT NULL.

На уровне Java:

  • Валидация через javax.validation (@NotNull, @Email),
  • Транзакции через @Transactional,
  • DTO для защиты от overposting.
🔥 Главное правило: Никогда не полагайтесь только на валидацию в коде!
Всегда дублируйте критические ограничения в БД.