Что такое внешний ключ (ForeignKey) в базах данных?

Ответ

Внешний ключ (Foreign Key, FK) — это ограничение в реляционной базе данных, которое обеспечивает ссылочную целостность данных. Он связывает поле (или набор полей) одной таблицы с первичным ключом (Primary Key, PK) другой таблицы, создавая отношение «родитель-потомок».

Основная цель: Гарантировать, что каждое значение в столбце внешнего ключа таблицы-потомка соответствует существующему значению первичного ключа в таблице-родителе. Это предотвращает появление «висячих» или «осиротевших» записей.

Пример на SQL (используя SQLite C API):

#include <sqlite3.h>
#include <iostream>

int main() {
    sqlite3* db;
    char* errMsg = 0;

    // Открываем/создаем БД
    int rc = sqlite3_open("test.db", &db);
    if (rc) { /* обработка ошибки */ }

    // Создаем таблицу-родитель (Departments)
    const char* sql_parent = R"(
        CREATE TABLE IF NOT EXISTS Departments (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL UNIQUE
        );
    )";
    rc = sqlite3_exec(db, sql_parent, 0, 0, &errMsg);

    // Создаем таблицу-потомок (Employees) с внешним ключом
    const char* sql_child = R"(
        CREATE TABLE IF NOT EXISTS Employees (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            department_id INTEGER NOT NULL,
            FOREIGN KEY (department_id) REFERENCES Departments(id)
                ON DELETE RESTRICT   -- Запрещает удаление отдела с сотрудниками
                ON UPDATE CASCADE    -- При изменении id отдела, оно обновится и у сотрудников
        );
    )";
    rc = sqlite3_exec(db, sql_child, 0, 0, &errMsg);

    sqlite3_close(db);
    return 0;
}

Действия при нарушении ссылочной целостности (ON DELETE / ON UPDATE):

  • RESTRICT / NO ACTION: Запретить операцию, нарушающую целостность (по умолчанию во многих СУБД).
  • CASCADE: Каскадно удалить или обновить связанные записи в дочерней таблице.
  • SET NULL: Установить NULL в столбце внешнего ключа у дочерних записей (если столбец допускает NULL).
  • SET DEFAULT: Установить значение по умолчанию.

В контексте C++ и ORM: При использовании ORM-фреймворков (например, ODB, Qt SQL) внешние ключи отображаются на связи между объектами (агрегация/композиция).

Ответ 18+ 🔞

А, ну вот, объясняю на пальцах, что такое этот ваш внешний ключ. Представь, что у тебя есть два списка. В одном — отделы компании, в другом — сотрудники. Так вот, если в графе «отдел» у какого-нибудь Васьки написать «Беспредельный цех», которого в первом списке нет, — это пиздец и бардак. База данных такого не любит. Совсем. Волнение ебать её охватывает.

Вот чтобы этого не было, и придумали внешний ключ. Это такая железная цепь, которая приковывает запись в таблице-ребёнке (сотрудники) к существующей записи в таблице-родителе (отделы). Попробуй привязать сотрудника к несуществующему отделу — СУБД тебе хуй с горы покажет, то есть ошибку влепит. Доверия ебать ноль к твоим кривым данным.

Смотри, как это в коде выглядит. Сначала создаём родительскую таблицу, где отделы. Всё стандартно.

CREATE TABLE IF NOT EXISTS Departments (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL UNIQUE
);

А вот теперь самое интересное — создаём таблицу сотрудников и пристёгиваем её цепью к отделам. Э бошка думай, что пишешь!

CREATE TABLE IF NOT EXISTS Employees (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    department_id INTEGER NOT NULL,
    FOREIGN KEY (department_id) REFERENCES Departments(id)
        ON DELETE RESTRICT   -- Не даст удалить отдел, если в нём кто-то числится
        ON UPDATE CASCADE    -- Если поменяем id отдела, у всех его сотрудников оно автоматом обновится
);

Видишь эту строчку FOREIGN KEY ... REFERENCES? Это и есть та самая цепь. Она говорит: «Эй, чувак, поле department_id — это не просто цифра. Это священная ссылка на id в таблице Departments! Храни её в чистоте!».

А эти штуки ON DELETE и ON UPDATE — это правила поведения на случай, если в родительской таблице начинается движ. Подозрение ебать чувствую, что тут могут быть подвохи.

  • RESTRICT / NO ACTION: Самый строгий папаша. «Ты что, с ума сошёл удалять отдел, пока там люди работают? Иди ты нахуй со своим удалением». Операция просто не выполнится.
  • CASCADE: Каскадный эффект, ёпта. Удалили отдел — все сотрудники из него автоматом летят в корзину. Поменяли id отдела — у всех его работников этот id тихо и мирно обновляется. Сильно, но опасно.
  • SET NULL: Более мягкий вариант. Отдел удалили? Ладно, у сотрудника в графе «отдел» просто появится NULL. Но для этого само поле должно позволять быть пустым, иначе опять пидарас шерстяной вылезет.
  • SET DEFAULT: Воспитательная мера. Нет отдела — получай значение по умолчанию, которое заранее задал. Не самый популярный вариант, честно говоря.

Если пишешь на чистом C++ с SQLite, как в примере, — всё это твои прямые заботы. А если взял какую-нибудь ORM-библиотеку, типа ODB, то она эту всю манду с ушами (связи между объектами) может взять на себя, превратив внешние ключи в красивые указатели или коллекции внутри твоих классов. Но суть-то от этого не меняется! Ссылочная целостность — она и в Африке целостность. Без неё твоя база превратится в свалку, а ты сам от себя охуеешь, когда начнёшь искать, где же этот чёртов баг.