В чём разница между кластерным индексом и PRIMARY KEY в SQL?

Ответ

PRIMARY KEY — это логическое ограничение целостности данных, которое гарантирует уникальность и запрещает значения NULL в указанных столбцах. Кластерный индекс — это физическая структура хранения, определяющая порядок записей на диске.

Ключевые отличия:

  1. Назначение: PRIMARY KEY обеспечивает уникальность, кластерный индекс — физический порядок.
  2. Количество: В таблице может быть только один кластерный индекс, но несколько ограничений PRIMARY KEY быть не может (оно одно).
  3. Зависимость: PRIMARY KEY по умолчанию в SQL Server создаётся как кластерный индекс, но это поведение можно изменить.
  4. Столбцы: Кластерный индекс можно создать на неуникальных или допускающих NULL столбцах (с добавлением уникафикатора), что для PRIMARY KEY недопустимо.

Практический пример разделения:

-- PRIMARY KEY как некластеризованный индекс, а кластеризация по другому полю
CREATE TABLE Orders (
    OrderID INT NOT NULL,
    OrderDate DATETIME NOT NULL,
    CustomerID INT NOT NULL,
    -- Логический первичный ключ (уникальный, не NULL)
    CONSTRAINT PK_Orders PRIMARY KEY NONCLUSTERED (OrderID)
);
-- Физический порядок хранения по дате заказа (частый запрос по диапазону дат)
CREATE CLUSTERED INDEX IX_Orders_OrderDate ON Orders(OrderDate);

Почему это важно? Выбор кластерного ключа критически влияет на производительность операций вставки и диапазонных запросов (WHERE BETWEEN, ORDER BY). Часто оптимальный кластерный ключ — монотонно возрастающий (например, IDENTITY), чтобы избежать фрагментации страниц. PRIMARY KEY же выбирается исходя из бизнес-логики.

Ответ 18+ 🔞

Давай разжую эту тему, как будто объясняю за кружкой пива после работы. Сиди, слушай.

Представь себе, что таблица в базе — это твой шкаф с одеждой. PRIMARY KEY — это как бирка на каждой вешалке с уникальным номерком. Никаких двух одинаковых номеров быть не может, и пустых бирок тоже. Это закон, логика. Просто правило, которое все соблюдают.

А кластерный индекс — это сам способ, как ты эти вешалки в шкафу развесил. Например, по цвету: все красные рубашки вместе, все синие — рядом. Или, что чаще бывает, по дате покупки — новые ближе к двери. Это физический порядок, как данные реально лежат на диске, в одной куче.

Теперь главный подвох, на котором все обламываются: в SQL Server, по умолчанию, когда ты говоришь «Вот это у меня главный ключ (PRIMARY KEY)», система услужливо думает: «А, понятно! Значит, по этому полю и будем всё в шкафу сортировать!» — и делает его кластерным индексом. И в 80% случаев так и надо.

Но, блядь, не всегда! Это как если бы ты хранил документы в архиве. Их уникальный номер (PRIMARY KEY) — это инвентарный номер дела. А вот кластеризовать (раскладывать на полках) тебе выгоднее не по номеру, а, например, по году, чтобы все дела за 2023 год были в одном месте. Понимаешь разницу?

Итоговые пункты, чтобы в голове отложилось:

  1. PRIMARY KEY — это правило (уникальность, не NULL). Такое правило — одно на таблицу.
  2. Кластерный индекс — это порядок хранения на диске. Такой порядок — тоже один на таблицу.
  3. Они могут быть разными! Номер дела (PRIMARY KEY) — уникален, а лежат дела на полке по году (Clustered Index).

Вот смотри, живой пример, почему это может быть охуенно полезно:

-- Допустим, таблица заказов. По бизнесу, номер заказа (OrderID) — наш главный уникальный ключ.
CREATE TABLE Orders (
    OrderID INT NOT NULL,
    OrderDate DATETIME NOT NULL,
    CustomerID INT NOT NULL,
    -- Объявляем PRIMARY KEY, но ЯВНО говорим: не кластеризуй по нему!
    CONSTRAINT PK_Orders PRIMARY KEY NONCLUSTERED (OrderID)
);

-- А кластеризовать мы хотим по дате заказа. Почему?
-- Потому что 90% наших запросов — это "покажи все заказы за последнюю неделю".
-- Если данные на диске уже лежат в порядке дат, такой запрос выполнится в разы быстрее.
CREATE CLUSTERED INDEX IX_Orders_OrderDate ON Orders(OrderDate);

На что это влияет в реальности, кроме теории? Всё просто: на скорость. Если ты часто ищешь по диапазонам (даты, суммы) или сортируешь — кластерный индекс по этому полю будет твоим лучшим другом. А если вставляешь данные пачками — то кластерный ключ лучше делать монотонно растущим (типа IDENTITY), чтобы новые записи просто приклеивались в конец, а не вставлялись в середину, вызывая переносы и фрагментацию — это пиздец как тормозит.

Короче, PRIMARY KEY — это «что», а кластерный индекс — это «как». И иногда для производительности их нужно разводить по разным углам.