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

Если создан составной индекс по трем полям, будет ли он использоваться при выполнении запроса, который включает только второе поле из этого индекса

1.0 Junior🔥 121 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Использование составного индекса при запросе только ко второму полю

Это классический вопрос о внутреннем устройстве индексов базы данных. Ответ зависит от левого префикса индекса (left prefix rule), который применяется в большинстве SQL БД.

Краткий ответ: НЕ будет использован

Если создан составной индекс по полям (A, B, C), то индекс будет использован в следующих случаях:

  • Поиск по A
  • Поиск по A и B
  • Поиск по A, B и C
  • Поиск по A с ORDER BY B

Однако индекс НЕ будет использован для:

  • Поиска только по B
  • Поиска только по C
  • Поиска по B и C (без A)

Почему работает левый префикс?

Составной индекс в базе данных устроен как B-tree структура, где сначала сортируются по первому полю, потом внутри каждого значения первого поля - по второму, и так далее:

Индекс (user_id, status, created_at):

user_id=1
  ├─ status='active'
  │   ├─ created_at='2024-01-01'
  │   ├─ created_at='2024-01-02'
  │   └─ created_at='2024-01-03'
  └─ status='inactive'
      ├─ created_at='2024-02-01'
      └─ created_at='2024-02-02'

user_id=2
  ├─ status='active'
  └─ status='inactive'

Примеры с SQL

-- Создаём составной индекс
CREATE INDEX idx_user_status_date ON orders (user_id, status, created_at);

-- БУДЕТ использован индекс (поиск по user_id)
SELECT * FROM orders WHERE user_id = 5;
-- ✓ Index seek/scan по user_id

-- БУДЕТ использован индекс (поиск по user_id и status)
SELECT * FROM orders WHERE user_id = 5 AND status = 'active';
-- ✓ Index seek по user_id, потом фильтр по status

-- НЕ будет использован индекс (поиск только по status)
SELECT * FROM orders WHERE status = 'active';
-- ✗ Full table scan (индекс не помогает)

-- НЕ будет использован индекс (поиск по 2-му и 3-му полю)
SELECT * FROM orders WHERE status = 'active' AND created_at > '2024-01-01';
-- ✗ Full table scan

-- БУДЕТ использован индекс (поиск по первому полю с сортировкой)
SELECT * FROM orders WHERE user_id = 5 ORDER BY status, created_at;
-- ✓ Index seek + используем индекс для сортировки

Примеры с Java/JPA

@Entity
@Table(name = "orders")
@Index(name = "idx_user_status_date", columnList = "user_id, status, created_at")
public class Order {
    @Id
    private Long id;
    
    @Column(name = "user_id")
    private Long userId;
    
    @Column(name = "status")
    private String status;
    
    @Column(name = "created_at")
    private LocalDateTime createdAt;
}

// Repository с методами
public interface OrderRepository extends JpaRepository<Order, Long> {
    // ✓ Использует индекс (LEFT PREFIX)
    List<Order> findByUserId(Long userId);
    
    // ✓ Использует индекс
    List<Order> findByUserIdAndStatus(Long userId, String status);
    
    // ✓ Использует индекс
    List<Order> findByUserIdAndStatusAndCreatedAtAfter(
        Long userId, String status, LocalDateTime createdAt);
    
    // ✗ НЕ использует индекс (нарушает левый префикс)
    List<Order> findByStatus(String status);
    
    // ✗ НЕ использует индекс
    List<Order> findByStatusAndCreatedAtAfter(String status, LocalDateTime createdAt);
}

Проверка в реальной БД (PostgreSQL)

// Получение плана запроса
public class QueryPlanAnalyzer {
    public void analyzeQueryPlan(DataSource dataSource) throws SQLException {
        try (Connection conn = dataSource.getConnection();
             Statement stmt = conn.createStatement()) {
            
            // Запрос, который использует индекс
            String query1 = "EXPLAIN SELECT * FROM orders WHERE user_id = 5";
            ResultSet rs1 = stmt.executeQuery(query1);
            System.out.println("Query 1 (с user_id):";
            while (rs1.next()) {
                System.out.println(rs1.getString(1)); // Index Scan или Seq Scan
            }
            
            // Запрос, который НЕ использует индекс
            String query2 = "EXPLAIN SELECT * FROM orders WHERE status = 'active'";
            ResultSet rs2 = stmt.executeQuery(query2);
            System.out.println("\nQuery 2 (только status):";
            while (rs2.next()) {
                System.out.println(rs2.getString(1)); // Seq Scan (full table)
            }
        }
    }
}

Практические следствия

public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    // ХОРОШО - индекс работает
    public List<Order> getActiveOrders(Long userId) {
        return orderRepository.findByUserIdAndStatus(userId, "active");
    }
    
    // ПЛОХО - индекс не работает, нужен отдельный индекс по status
    public List<Order> getAllActiveOrders() {
        return orderRepository.findByStatus("active");
    }
    
    // ХОРОШО - если часто ищешь по status, создай отдельный индекс
    // @Index(name = "idx_status", columnList = "status")
}

Оптимизация

Если часто ищешь только по второму полю, нужно создать отдельный индекс:

-- Исходный составной индекс
CREATE INDEX idx_user_status_date ON orders (user_id, status, created_at);

-- Дополнительный индекс для поиска по status
CREATE INDEX idx_status ON orders (status);

-- Теперь оба запроса используют индексы
SELECT * FROM orders WHERE user_id = 5;      -- ✓ idx_user_status_date
SELECT * FROM orders WHERE status = 'active'; -- ✓ idx_status

Итоговая таблица

Составной индексЗапросИспользуется?Примечание
(A, B, C)WHERE ALEFT PREFIX
(A, B, C)WHERE A AND BLEFT PREFIX
(A, B, C)WHERE A AND B AND CLEFT PREFIX
(A, B, C)WHERE BНарушен префикс
(A, B, C)WHERE B AND CНарушен префикс
(A, B, C)WHERE CНарушен префикс
(A, B, C)WHERE A AND C⚠️Частично (только по A)

Вывод

Составной индекс работает по принципу левого префикса: для его использования запрос ДОЛЖЕН содержать условия, начиная с первого поля индекса. Если запрос содержит только второе поле, индекс не будет использован и произойдёт полный scan таблицы. При разработке индексной стратегии важно понимать, какие запросы будут наиболее часто выполняться, и создавать индексы с учётом этого.

Если создан составной индекс по трем полям, будет ли он использоваться при выполнении запроса, который включает только второе поле из этого индекса | PrepBro