Learning Platform
Глоссарий Troubleshooting
Урок 07.04 · 18 мин
Начальный
relationshipsidentifying-relationshipweak-entitycomposite-key

Identifying vs non-identifying relationships

Мы научились различать связи по кардинальности (1:1, 1:N, M:N) и по optionality (mandatory/optional). Есть ещё одно различие связей, важное для проектирования: identifying против non-identifying. Оно отвечает на вопрос: входит ли foreign key родителя в primary key ребёнка, или лежит в дочерней таблице как обычный столбец.

Это различие напрямую связано с понятиями weak и strong entity из модуля про ER-моделирование, и оно определяет, может ли дочерняя строка вообще существовать самостоятельно. Junior-инженеру это нужно, чтобы правильно строить ключи дочерних таблиц — особенно junction-таблиц.

Два вида связи: где живёт foreign key в ребёнке

В связи через foreign key есть parent (родитель) и child (ребёнок). Foreign key всегда лежит в дочерней таблице. Вопрос — какую роль он там играет.

Non-identifying relationship (неидентифицирующая связь). Foreign key родителя лежит в дочерней таблице как обычный атрибут, НЕ входящий в её primary key. У ребёнка есть свой собственный, независимый primary key.

Identifying relationship (идентифицирующая связь). Foreign key родителя входит в состав primary key дочерней таблицы. Ребёнок не имеет полностью собственного ключа — его идентичность частично или полностью определяется родителем.

Разберём на двух примерах.

Пример non-identifying: users и orders

CREATE TABLE users (
    user_id INTEGER PRIMARY KEY,
    name    TEXT NOT NULL
);

CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,                       -- свой независимый PK
    user_id  INTEGER NOT NULL REFERENCES users(user_id), -- FK — обычный столбец
    amount   NUMERIC(12,2) NOT NULL
);

Здесь связь users -> ordersnon-identifying. Смотрим на orders: её primary key — order_id, и он полностью свой, родителя в нём нет. Foreign key user_id лежит в orders отдельным столбцом, в primary key он не входит. Заказ идентифицируется собственным order_id независимо от пользователя.

Пример identifying: orders и order_items

CREATE TABLE orders (
    order_id INTEGER PRIMARY KEY,
    user_id  INTEGER NOT NULL REFERENCES users(user_id),
    amount   NUMERIC(12,2) NOT NULL
);

CREATE TABLE order_items (
    order_id   INTEGER NOT NULL REFERENCES orders(order_id),  -- FK...
    line_no    INTEGER NOT NULL,
    product_id INTEGER NOT NULL REFERENCES products(product_id),
    quantity   INTEGER NOT NULL,
    PRIMARY KEY (order_id, line_no)   -- ...и FK ВХОДИТ в primary key!
);

Здесь связь orders -> order_itemsidentifying. Смотрим на order_items: её primary key — составной (order_id, line_no), и order_id — это foreign key на orders, который входит в primary key. Позиция заказа не имеет полностью собственного ключа: она идентифицируется как «позиция номер line_no внутри заказа order_id». line_no сам по себе («позиция 1») не значит ничего — нужен ещё order_id.

Где foreign key родителя в primary key ребёнка
Non-identifyingFK родителя — обычный столбец дочерней таблицы, в primary key НЕ входит. У ребёнка свой независимый PK. Пример: orders.user_id.
IdentifyingFK родителя ВХОДИТ в состав primary key дочерней таблицы. Идентичность ребёнка зависит от родителя. Пример: order_items PK (order_id, line_no).

Связь с weak и strong entity

Это различие — прямое продолжение темы weak/strong entity из модуля про ER-моделирование. Напомним кратко:

  • Strong (regular) entity — сущность с собственным ключом, существует самостоятельно.
  • Weak entity — сущность без собственного полного ключа; идентифицируется только через родительскую сущность плюс свой частичный ключ (discriminator).

Соответствие прямое:

  • Non-identifying relationship связывает родителя со strong entity. Ребёнок самодостаточен: у заказа есть order_id, он существует и опознаётся сам по себе.
  • Identifying relationship связывает родителя со weak entity. Ребёнок несамодостаточен: позиция заказа order_items — это weak entity, она не существует без своего заказа и опознаётся только в паре «заказ + номер позиции».
strong entity  <-- non-identifying --  parent   (ребёнок самодостаточен)
weak entity    <-- identifying     --  parent   (ребёнок зависит от родителя)

В нотации Crow’s Foot identifying-связь рисуют сплошной линией (и дочернюю сущность — со скруглёнными углами как признак weak entity), non-identifying — пунктирной. Но главное — не картинка, а суть: входит FK в PK ребёнка или нет.

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

Различие identifying/non-identifying — не теоретическое украшение. Оно влияет на три вещи.

1. Существование дочерней строки. В identifying-связи дочерняя строка не может существовать без родителя — это вшито в саму структуру ключа. Раз order_id входит в primary key order_items, а primary key обязан быть NOT NULL (урок про ключи), то order_id в order_items физически не может быть пустым. Позиция заказа без заказа невозможна по построению. В non-identifying связи, если FK nullable, ребёнок может существовать без родителя (это была тема прошлого урока про optionality).

2. Каскадное удаление логично по умолчанию. Для identifying-связи ON DELETE CASCADE — естественный выбор: если weak entity не существует без родителя, то при удалении родителя дочерние строки тоже должны исчезнуть (удалили заказ — его позиции бессмысленны). Для non-identifying связи каскад — не само собой разумеющийся выбор: удаление пользователя не обязано удалять заказы, тут возможен и RESTRICT, и SET NULL.

3. Распространение ключа вглубь. Identifying-связи складываются. Если C идентифицируется через B, а B — через A, то primary key C включает ключ B, который включает ключ A. Цепочка identifying-связей наращивает составной ключ всё шире на каждом уровне. Это работает, но широкие составные ключи неудобны (вспомните, почему — модуль про ключи: ширина, JOIN, foreign keys). Поэтому глубокие цепочки identifying-связей часто разрывают, вводя на каком-то уровне surrogate key и превращая связь в non-identifying.

NOTE

Junction table — почти всегда identifying-связь с ОБЕИХ сторон. Вспомните enrollments с primary key (student_id, course_id): оба столбца — foreign key, и оба входят в primary key. Значит, enrollments идентифицируется через обоих родителей — связь identifying и со стороны students, и со стороны courses. Это естественно: строка-связь не существует без обеих сторон, которые она соединяет. Поэтому простая junction table — классический пример weak entity.

ON DELETE CASCADE — каскадное удаление для identifying-связей

Как выбрать: identifying или non-identifying

Junior-инженеру нужен критерий. Спросите про дочернюю сущность:

Может ли дочерняя строка быть осмысленно идентифицирована САМА ПО СЕБЕ, без родителя?

  • Да — у неё есть естественный собственный ключ (заказ -> order_id, пользователь -> user_id). Это strong entity, связь делайте non-identifying: дайте ребёнку свой primary key, а FK родителя положите обычным столбцом.
  • Нет — её идентичность имеет смысл только внутри родителя (позиция -> только «позиция N в заказе X»; строка-связь -> только «связь A и B»). Это weak entity, связь делайте identifying: включите FK родителя в составной primary key ребёнка.

Пример. Сущность «комментарий к статье». Может ли комментарий быть идентифицирован сам по себе? Да — у комментария вполне может быть свой comment_id, он самодостаточен как объект. Значит, strong entity, связь articles -> comments — non-identifying: comments получает свой comment_id PK, а article_id — обычный FK-столбец.

Контрпример. Сущность «строка спецификации товара» — характеристика товара по порядковому номеру. Имеет ли «характеристика номер 3» смысл без товара? Нет — нужен товар. Weak entity, связь identifying: primary key (product_id, spec_no).

Тонкость: identifying-связь и surrogate key

В уроках про ключи мы сделали surrogate key стандартом для primary key. Как это сочетается с identifying-связями, где ключ ребёнка должен включать ключ родителя?

Тут есть нюанс, который стоит понимать чётко. Identifying-связь — это про логику модели: дочерняя сущность семантически зависит от родителя, она weak entity. Это утверждение о предметной области, и оно остаётся верным независимо от того, какие ключи вы выберете физически.

На физическом уровне есть два способа реализовать эту зависимость:

  • Естественный путь. Сделать primary key ребёнка составным, включив в него foreign key родителя: order_items (order_id, line_no). Тогда зависимость weak entity вшита прямо в ключ — это «чистая» identifying-связь.
  • Surrogate-путь. Дать ребёнку собственный surrogate primary key order_item_id, а зависимость от родителя выразить через NOT NULL foreign key плюс, при необходимости, UNIQUE (order_id, line_no). Связь формально становится non-identifying (FK не в primary key), но семантика weak entity сохраняется: позиция всё равно не существует без заказа.

Оба способа корректны, и второй на практике встречается часто — именно потому, что узкий surrogate key удобнее для дальнейших ссылок (модуль про ключи объяснил, почему). Важно не путать уровни: «weak entity» — это про смысл, «identifying relationship» — про конкретную технику с составным ключом. Можно иметь weak entity, реализованную через non-identifying связь и surrogate key.

TIP

Практический ориентир: глубину цепочки identifying-связей стоит держать небольшой. Если C идентифицируется через B, B через A, B-через-A-через-предка — составной primary key на нижнем уровне разрастается, и каждая ссылающаяся таблица тащит всё более широкий foreign key. На втором-третьем уровне вложенности обычно разумно перейти на surrogate key: это разрывает накопление ширины ключа, не теряя смысла зависимости.

Критерий выбора: самодостаточна ли дочерняя сущность
Вопрос: ребёнок идентифицируется сам по себе?Есть ли у дочерней сущности осмысленный собственный ключ, не зависящий от родителя.
Да -> strong entity -> non-identifyingДать ребёнку свой primary key, FK родителя положить обычным столбцом. Пример: orders, comments.
Нет -> weak entity -> identifyingВключить FK родителя в составной primary key ребёнка. Пример: order_items, junction tables.

Попробуй сам

Для каждой пары «родитель -> ребёнок» определите, identifying связь или non-identifying, по критерию «самодостаточен ли ребёнок», и напишите CREATE TABLE дочерней таблицы с правильным primary key:

  1. Дом -> Квартира. Может ли квартира быть идентифицирована сама по себе, или только как «квартира номер N в доме X»? Какой primary key у apartments?
  2. Клиент -> Договор. Имеет ли договор собственный осмысленный номер? Identifying или non-identifying?
  3. Книга -> Глава. Глава опознаётся как «глава 5» вообще или как «глава 5 книги X»? Постройте primary key таблицы chapters.
  4. Университет -> Студент. Студент — самодостаточная сущность или существует только внутри университета? Решите и обоснуйте.
  5. Возьмите identifying-пример из задания (apartments или chapters) и постройте на нём цепочку из трёх уровней: например Дом -> Подъезд -> Квартира, где каждая связь identifying. Посмотрите, как растёт составной primary key на третьем уровне, и подумайте, в какой момент имеет смысл разорвать цепочку через surrogate key.

Проверка знанийKnowledge check
В чём разница между identifying и non-identifying связью, как она соотносится с понятиями weak и strong entity, и по какому критерию проектировщик выбирает между ними?
ОтветAnswer
Разница между двумя видами связи в том, какую роль foreign key родителя играет в дочерней таблице. В non-identifying (неидентифицирующей) связи foreign key родителя лежит в дочерней таблице как обычный атрибут и НЕ входит в её primary key — у ребёнка есть свой собственный, независимый primary key. Пример: orders.user_id — это FK на users, но primary key таблицы orders это order_id, родителя в нём нет, заказ опознаётся сам по себе. В identifying (идентифицирующей) связи foreign key родителя ВХОДИТ в состав primary key дочерней таблицы — ребёнок не имеет полностью собственного ключа, его идентичность определяется родителем. Пример: order_items с primary key (order_id, line_no), где order_id это FK на orders; позиция заказа опознаётся только как "позиция N внутри заказа X". Соотношение с weak/strong entity прямое: non-identifying связь соединяет родителя со strong entity — самодостаточной сущностью с собственным ключом; identifying связь соединяет родителя с weak entity — несамодостаточной сущностью, которая не существует и не опознаётся без родителя. Критерий выбора: проектировщик спрашивает, может ли дочерняя строка быть осмысленно идентифицирована сама по себе, без родителя. Если да — у неё есть естественный собственный ключ (заказ, комментарий), это strong entity, связь делается non-identifying: ребёнок получает свой primary key, а FK родителя кладётся обычным столбцом. Если нет — её идентичность имеет смысл только внутри родителя (позиция заказа, строка-связь в junction-таблице), это weak entity, связь делается identifying: FK родителя включается в составной primary key ребёнка. Практическое следствие identifying-связи в том, что дочерняя строка физически не может существовать без родителя — FK входит в primary key, а primary key обязан быть NOT NULL, поэтому ссылка на родителя не может быть пустой; и каскадное удаление для такой связи логично по умолчанию.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Что характеризует identifying (идентифицирующую) связь?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5