← Назад к вопросам

Что такое подзапрос в БД?

1.6 Junior🔥 241 комментариев
#Базы данных и SQL

Комментарии (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 и других технологий для доступа к данным.