Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Подзапрос в БД
Подзапрос (Subquery) — это SQL-запрос, вложенный в другой SQL-запрос. Подзапрос выполняется в первую очередь, а его результат используется во внешнем запросе. Подзапросы позволяют выполнять сложные задачи поиска и фильтрации данных, разделяя логику на несколько шагов.
Типы подзапросов
1. Подзапрос в WHERE (фильтрация)
// Найти всех сотрудников с зарплатой больше средней
String sql = "SELECT * FROM employees WHERE salary > (SELECT AVG(salary) FROM employees)";
Пример на Java с JDBC:
public List<Employee> getEmployeesAboveAverage(DataSource ds) throws SQLException {
List<Employee> employees = new ArrayList<>();
String sql = "SELECT id, name, salary FROM employees WHERE salary > " +
"(SELECT AVG(salary) FROM employees)";
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
employees.add(new Employee(
rs.getLong("id"),
rs.getString("name"),
rs.getBigDecimal("salary")
));
}
}
return employees;
}
2. Подзапрос в FROM (виртуальная таблица)
// Найти средюю зарплату по каждому отделу, затем отфильтровать
String sql = "SELECT dept_id, AVG(avg_salary) as avg_dept_salary FROM " +
"(SELECT dept_id, AVG(salary) as avg_salary FROM employees GROUP BY dept_id) " +
"subquery GROUP BY dept_id";
На Java:
public Map<String, BigDecimal> getAvgSalaryByDept(DataSource ds) throws SQLException {
Map<String, BigDecimal> result = new HashMap<>();
String sql = "SELECT dept_id, AVG(salary) as avg_salary FROM employees GROUP BY dept_id";
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
result.put(rs.getString("dept_id"), rs.getBigDecimal("avg_salary"));
}
}
return result;
}
3. Подзапрос в SELECT (скалярный подзапрос)
// Вывести каждого сотрудника и количество его проектов
String sql = "SELECT name, (SELECT COUNT(*) FROM projects WHERE projects.employee_id = employees.id) as project_count " +
"FROM employees";
4. Подзапрос с IN / NOT IN
// Найти сотрудников, которые работают над проектом с ID = 5
String sql = "SELECT * FROM employees WHERE id IN (SELECT employee_id FROM project_employees WHERE project_id = 5)";
На Java:
public List<Employee> getEmployeesByProject(DataSource ds, Long projectId) throws SQLException {
List<Employee> employees = new ArrayList<>();
String sql = "SELECT e.* FROM employees e WHERE e.id IN " +
"(SELECT pe.employee_id FROM project_employees pe WHERE pe.project_id = ?)";
try (Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setLong(1, projectId);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
employees.add(mapEmployee(rs));
}
}
}
return employees;
}
5. Коррелирующий подзапрос
Подзапрос, который ссылается на данные из внешнего запроса:
// Найти сотрудников, зарплата которых выше, чем средняя по их отделу
String sql = "SELECT * FROM employees e1 WHERE salary > " +
"(SELECT AVG(salary) FROM employees e2 WHERE e1.dept_id = e2.dept_id)";
Подзапрос vs JOIN
JOIN обычно быстрее:
// С подзапросом
String sqlSubquery = "SELECT * FROM employees WHERE dept_id IN (SELECT id FROM departments WHERE city = 'New York')";
// С JOIN (обычно быстрее)
String sqlJoin = "SELECT e.* FROM employees e JOIN departments d ON e.dept_id = d.id WHERE d.city = 'New York'";
Плюсы и минусы
Плюсы:
- Логичность: сложная логика разбита на части
- Читаемость: понятнее, что делает каждый уровень
- Переиспользование: можно применить знакомые структуры
Минусы:
- Производительность: часто медленнее, чем JOIN
- Коррелирующие подзапросы выполняются для каждой строки (O(n))
- Сложность оптимизации для базы данных
Подзапросы важны для Java-разработчика, работающего с базами данных, особенно при использовании JDBC, JPA и других технологий для доступа к данным.