← Назад к вопросам
Обязательно ли поля в индексе должны быть уникальными в таблице в БД?
1.8 Middle🔥 151 комментариев
#Базы данных и SQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Индексы и уникальность в БД
Краткий ответ
Нет, поля в индексе НЕ обязательно должны быть уникальными. Индексы делятся на два типа: unique индексы (уникальные ключи) и non-unique индексы (обычные индексы). Большинство индексов в реальных приложениях - это non-unique индексы.
Два типа индексов
1. Non-Unique индекс (обычный индекс)
Позволяет одинаковые значения и используется для ускорения поиска:
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(100),
status VARCHAR(50),
created_at TIMESTAMP
);
-- Создаём обычный индекс по status
CREATE INDEX idx_users_status ON users(status);
-- Много пользователей могут иметь status = "ACTIVE"
SELECT * FROM users WHERE status = "ACTIVE";
2. Unique индекс (уникальный индекс)
Гарантирует уникальность значений:
-- Уникальный индекс по email
CREATE UNIQUE INDEX idx_users_email ON users(email);
-- PRIMARY KEY - это тоже UNIQUE индекс
ALTER TABLE users ADD CONSTRAINT pk_users PRIMARY KEY (id);
Составные индексы (Composite Index)
Индекс может содержать несколько полей, и они не должны быть уникальными:
-- Composite non-unique индекс
CREATE INDEX idx_users_status_created
ON users(status, created_at);
-- Запрос будет быстрым
SELECT * FROM users
WHERE status = "ACTIVE"
AND created_at > "2024-01-01";
Порядок полей в индексе
Порядок ОЧЕНЬ важен для составных индексов:
-- ✅ Хорошо: status первый (наиболее селективный)
CREATE INDEX idx_orders_status_date
ON orders(status, created_at);
-- ❌ Плохо: дата первой (много значений, малая селективность)
CREATE INDEX idx_orders_date_status
ON orders(created_at, status);
-- Это объясняется "левым префиксом" (leftmost prefix rule)
-- Индекс (A, B, C) может использоваться для:
-- - WHERE A = ...
-- - WHERE A = ... AND B = ...
-- - WHERE A = ... AND B = ... AND C = ...
-- НО НЕ для WHERE B = ... или WHERE C = ...
Когда использовать индексы
| Сценарий | Тип индекса | Пример |
|---|---|---|
| Очень частый поиск | Non-unique | Индекс по status, email |
| Сортировка | Non-unique | Индекс по created_at |
| Уникальное поле | Unique | email, username |
| Foreign Key | Non-unique | Индекс по user_id |
| Первичный ключ | Unique | PRIMARY KEY |
Практический пример Java + SQL
// Entity в JPA
@Entity
@Table(name = "users", indexes = {
@Index(name = "idx_status", columnList = "status", unique = false),
@Index(name = "idx_email", columnList = "email", unique = true),
@Index(name = "idx_status_created", columnList = "status, created_at")
})
public class User {
@Id
private Long id;
@Column(unique = true)
private String email;
private String status;
private LocalDateTime createdAt;
}
// Репозиторий
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Будет использован idx_status
List<User> findByStatus(String status);
// Будет использован idx_email
Optional<User> findByEmail(String email);
// Будет использован idx_status_created
@Query("SELECT u FROM User u WHERE u.status = :status AND u.createdAt > :date")
List<User> findActiveUsers(@Param("status") String status,
@Param("date") LocalDateTime date);
}
Важные правила
- Не создавай индекс на каждое поле - это замедлит INSERT, UPDATE, DELETE
- Выбирай селективные колонки - индекс на status (2 значения) лучше, чем на gender (2 значения)
- Следи за EXPLAIN PLAN - проверяй, используется ли индекс
- Composite индексы экономят место - вместо 3 отдельных индексов создай 1 составной
- Unique не ускоряет поиск - это просто ограничение целостности данных
EXPLAIN PLAN в PostgreSQL
-- Проверим, используется ли индекс
EXPLAIN ANALYZE
SELECT * FROM users WHERE status = "ACTIVE";
-- Если видишь "Index Scan" - отлично!
-- Если видишь "Seq Scan" - индекс не помогает
Вывод
Индексы - это балансирование между скоростью чтения и стоимостью записи. Unique - это лишь ограничение целостности, не связанное с производительностью индекса.