В чем разница между уникальным индексом и первичным ключом?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Различия между уникальным индексом и первичным ключом
Первичный ключ и уникальный индекс — это два разных механизма в базах данных для обеспечения уникальности данных, но они имеют разные свойства и применяются в разных сценариях.
1. Первичный ключ (Primary Key)
Определение: Первичный ключ — это колонка или набор колонок, которые уникально идентифицируют каждую строку в таблице.
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True) # Первичный ключ
name = Column(String)
email = Column(String)
engine = create_engine("postgresql://localhost/db")
Base.metadata.create_all(engine)
Характеристики:
- Не может быть NULL
- Гарантирует уникальность строки
- Может быть только один на таблицу
- Автоматически индексируется
- Используется для связей между таблицами (Foreign Keys)
- Служит идентификатором записи
2. Уникальный индекс (Unique Index/Unique Constraint)
Определение: Уникальный индекс гарантирует, что все значения в колонке (или наборе колонок) уникальны.
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
email = Column(String, unique=True) # Уникальный индекс
phone = Column(String)
# Или явно через UniqueConstraint
from sqlalchemy import UniqueConstraint
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True)
sku = Column(String) # Stock Keeping Unit
__table_args__ = (
UniqueConstraint('sku', name='uq_product_sku'),
)
Характеристики:
- Может быть NULL
- Может быть несколько на таблицу
- Автоматически индексируется
- Только гарантирует уникальность значений
- Не используется для связей между таблицами
3. Сравнение
| Аспект | Primary Key | Unique Index |
|---|---|---|
| NULL значения | Не допускаются | Допускаются |
| Количество | Один на таблицу | Несколько |
| Индексирование | Автоматическое | Автоматическое |
| Foreign Key | Можно использовать | Нельзя |
| Назначение | Идентификатор строки | Гарантия уникальности |
| Изменяемость | Обычно не меняется | Может меняться |
| Производительность | Оптимизирована | Хорошая |
4. Практические примеры
Пример 1: Таблица пользователей
class User(Base):
__tablename__ = "users"
# Primary Key — уникально идентифицирует пользователя
id = Column(Integer, primary_key=True)
# Unique Index — email должен быть уникальным
email = Column(String, unique=True, nullable=False)
# Unique Index — username должен быть уникальным
username = Column(String, unique=True, nullable=False)
name = Column(String, nullable=False)
created_at = Column(DateTime, default=datetime.now)
Пример 2: Таблица продуктов с композитным ключом
from sqlalchemy import UniqueConstraint
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True)
# SKU (Stock Keeping Unit) должен быть уникальным
sku = Column(String, nullable=False)
warehouse_id = Column(Integer, nullable=False)
# Уникальность по двум колонкам одновременно
__table_args__ = (
UniqueConstraint('sku', 'warehouse_id', name='uq_sku_warehouse'),
)
Пример 3: Таблица с несколькими уникальными индексами
class Account(Base):
__tablename__ = "accounts"
id = Column(Integer, primary_key=True) # Primary Key
# Несколько уникальных индексов
email = Column(String, unique=True, nullable=False)
username = Column(String, unique=True, nullable=False)
phone = Column(String, unique=True) # Может быть NULL
api_key = Column(String, unique=True)
5. NULL в уникальном индексе
-- Важный момент: несколько NULL значений в уникальном индексе
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100),
description VARCHAR(500) UNIQUE
);
INSERT INTO products VALUES (1, 'Product 1', NULL);
INSERT INTO products VALUES (2, 'Product 2', NULL);
INSERT INTO products VALUES (3, 'Product 3', NULL);
-- Это будет разрешено!
-- NULL != NULL в SQL, поэтому несколько NULL не нарушает уникальность
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True)
description = Column(String, unique=True) # Может быть несколько NULL
# В БД это будет работать
session.add(Product(id=1, description=None))
session.add(Product(id=2, description=None))
session.add(Product(id=3, description=None))
session.commit() # Успешно!
6. Использование в связях между таблицами
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String, unique=True)
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True)
title = Column(String)
# Foreign Key может ссылаться только на Primary Key
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
# Это работает (user_id -> users.id)
# Но если попытаться сделать Foreign Key на username (unique index):
# post_user_username = Column(String, ForeignKey('users.username'))
# Это тоже будет работать в некоторых БД, но не рекомендуется
7. Индексирование производительности
Primary Key:
-- Быстрый поиск по ID
SELECT * FROM users WHERE id = 123;
-- O(log n) благодаря индексу Primary Key
Unique Index:
-- Быстрый поиск по email
SELECT * FROM users WHERE email = 'user@example.com';
-- O(log n) благодаря уникальному индексу
8. Когда использовать каждый
Primary Key используй для:
- Уникальной идентификации строки
- Связей между таблицами (Foreign Keys)
- Основного способа обращения к записи
- Обычно это ID или UUID
Unique Index используй для:
- Email пользователя
- Username или логин
- SKU товара
- API ключи
- Любые другие поля, которые должны быть уникальны
9. Лучшие практики
Правило 1: Всегда наличествует Primary Key
class GoodTable(Base):
__tablename__ = "good_table"
id = Column(Integer, primary_key=True)
# ... другие колонки
# Никогда не создавай таблицу без Primary Key
Правило 2: Используй Natural vs Surrogate Keys
# Surrogate Key (суррогатный) — искусственный ID
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True) # Surrogate Key
email = Column(String, unique=True) # Natural Key
username = Column(String, unique=True) # Natural Key
# Surrogate Keys лучше для Foreign Keys
Правило 3: Composite Primary Key
from sqlalchemy import PrimaryKeyConstraint
class OrderItem(Base):
__tablename__ = "order_items"
order_id = Column(Integer, nullable=False)
product_id = Column(Integer, nullable=False)
quantity = Column(Integer)
__table_args__ = (
PrimaryKeyConstraint('order_id', 'product_id'),
)
10. Вывод
Первичный ключ — это фундамент таблицы, уникально идентифицирующий каждую строку и используемый для связей между таблицами. Уникальный индекс — это дополнительное ограничение для гарантии уникальности других важных полей, которые не являются основными идентификаторами. Одна таблица может иметь только один первичный ключ, но несколько уникальных индексов.