← Назад к вопросам
Как количество полей в запросе SELECT * влияет на производительность
1.0 Junior🔥 121 комментариев
#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Влияние SELECT * на производительность
Вопрос о SELECT * — это классический вопрос оптимизации баз данных. Количество полей в запросе существенно влияет на производительность, и это влияние идёт дальше, чем просто объём передаваемых данных.
1. Прямое влияние на производительность
Объём данных, передаваемых из БД
-- ПЛОХО: SELECT * — может быть 50+ полей
SELECT * FROM users WHERE age > 18;
-- Может передать 500KB данных
-- ХОРОШО: выбираем только нужные поля
SELECT id, name, email FROM users WHERE age > 18;
-- Передает только 50KB данных
Влияние:
- Больший объём данных = больше сетевых затрат
- Медленнее заполняется буфер результатов
- Больше нагрузка на сетевой канал между БД и приложением
I/O на диске
-- SELECT * может заставить прочитать больше блоков диска
SELECT * FROM orders; -- 100 полей, может потребовать чтение 50 блоков
-- SELECT с нужными полями
SELECT order_id, total_amount FROM orders; -- 2 поля, может потребовать 5 блоков
Это особенно критично для больших таблиц с широким набором столбцов.
2. Влияние на кэширование
// В памяти приложения
List<User> users = findAllUsers(); // SELECT *
// Каждый объект User занимает больше памяти
List<UserDTO> users = findUsersNameOnly(); // SELECT id, name
// Каждый DTO занимает меньше памяти
// Если кэш ограничен (например, 100MB):
// SELECT * может вместить только 10000 записей
// SELECT с 3 полями может вместить 50000 записей
3. Влияние на выполнение запроса
Индексы и Query Planner
-- SELECT * может помешать использованию covering index
CREATE INDEX idx_users_name ON users(name);
SELECT * FROM users WHERE name = John;
-- БД должна искать в индексе, затем читать весь row из основной таблицы
-- Это называется bookmark lookup (две операции)
SELECT id, name FROM users WHERE name = John;
-- БД может использовать covering index и не читать основную таблицу
-- Это index-only scan (одна операция, намного быстрее)
Statistics и оптимизатор
-- Оптимизатор может выбрать неправильный план для SELECT *
EXPLAIN SELECT * FROM large_table WHERE status = active;
-- Может выбрать full table scan, потому что вернёт много данных
EXPLAIN SELECT id FROM large_table WHERE status = active;
-- Может выбрать индекс, потому что меньше данных
4. Практический пример с Java и JDBC
// ПЛОХО: SELECT *
public List<User> findAllUsers() throws SQLException {
String sql = "SELECT * FROM users"; // 25 полей
ResultSet rs = statement.executeQuery(sql);
List<User> users = new ArrayList<>();
while (rs.next()) {
User user = new User(
rs.getLong("id"),
rs.getString("name"),
rs.getString("email"),
rs.getString("phone"),
rs.getString("address"),
// ... еще 20 полей которые нам не нужны
);
users.add(user);
}
return users;
} // Время: 2500ms, память: 500MB для 10000 записей
// ХОРОШО: только нужные поля
public List<UserDTO> findUsersForListing() throws SQLException {
String sql = "SELECT id, name, email FROM users"; // 3 поля
ResultSet rs = statement.executeQuery(sql);
List<UserDTO> users = new ArrayList<>();
while (rs.next()) {
UserDTO user = new UserDTO(
rs.getLong("id"),
rs.getString("name"),
rs.getString("email")
);
users.add(user);
}
return users;
} // Время: 300ms, память: 50MB для 10000 записей
5. Влияние на сериализацию в REST API
// ПЛОХО: возвращаем весь объект
@GetMapping("/users")
public List<User> getUsers() {
return userRepository.findAll(); // SELECT *
}
// JSON ответ: 50KB на запись (25 полей × 2KB)
// ХОРОШО: возвращаем только нужные поля
@GetMapping("/users")
public List<UserListDTO> getUsers() {
return userRepository.findAllDTO(); // SELECT id, name, email
}
// JSON ответ: 2KB на запись (3 поля × 0.6KB)
// Экономия: 96% трафика!
6. Рекомендации для конкретных сценариев
Listing (список пользователей)
-- ПЛОХО
SELECT * FROM users LIMIT 50;
-- ХОРОШО
SELECT id, name, email, created_at FROM users LIMIT 50;
Detail (подробная информация об одном пользователе)
-- SELECT * может быть оправдан, если нужны все поля
SELECT * FROM users WHERE id = 123;
-- Но лучше быть явным
SELECT id, name, email, phone, created_at FROM users WHERE id = 123;
Aggregation (статистика)
-- ПЛОХО
SELECT u.* FROM users u
JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
-- Вернёт весь User, но GroupBy может быть неопределён
-- ХОРОШО
SELECT u.id, COUNT(o.id) as order_count
FROM users u
JOIN orders o ON u.id = o.user_id
GROUP BY u.id;
7. ORM и SELECT *
// JPA/Hibernate с SELECT *
@Entity
public class User {
@Id
private Long id;
private String name;
private String email;
private String phone;
@Lob
private String biography; // 10KB TEXT field
@Lob
private String preferences; // 5KB TEXT field
// ... ещё много полей
}
// Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// ПЛОХО: будет SELECT * (все поля, включая LOB)
List<User> findAll(); // загрузит biography и preferences
// ХОРОШО: используем projection
@Query("SELECT u.id, u.name, u.email FROM User u")
List<UserListDTO> findAllList();
}
// DTO для использования
public record UserListDTO(Long id, String name, String email) {}
8. Benchmarking: реальные цифры
public class QueryPerformanceBenchmark {
// Таблица: 1M записей, 30 полей
// Размер row: ~1.5KB
// SELECT * — 30 полей
// Время: 5000ms
// Память: 1.5GB
// Объём: 1.5GB
// SELECT 5 ключевых полей
// Время: 1000ms
// Память: 250MB
// Объём: 250MB
// Вывод: экономия в 5 раз по времени и памяти
}
9. Когда SELECT * может быть оправдан
- Backing up data: полное резервирование таблицы
- Migration: перемещение данных полностью
- Single record: SELECT * WHERE id = ?, когда реально нужны все поля
- Dynamic queries: когда заранее неизвестны нужные поля
Итоговые рекомендации
ВСЕГДА указывайте конкретные поля:
- Явность кода
- Лучшая производительность БД
- Меньше памяти в приложении
- Лучше работают индексы (covering index)
- Проще масштабировать при добавлении новых полей
**SELECT *** — признак лени и неоптимизированного кода. Используйте ProjectionDTOs, Specification, QueryDSL для типобезопасности.