Какие типы связей между таблицами существуют в реляционных базах данных?

Ответ

В реляционных базах данных существует три основных типа связей, которые реализуются с помощью внешних ключей (FOREIGN KEY):

  1. Один к одному (One-to-One, 1:1) Одна запись в таблице А связана максимум с одной записью в таблице Б, и наоборот. Часто используется для разделения редко используемых или чувствительных данных.

    -- Пример: Пользователь и его паспортные данные
    CREATE TABLE users (
        id SERIAL PRIMARY KEY,
        email VARCHAR(255) UNIQUE NOT NULL
    );
    CREATE TABLE user_profiles (
        id SERIAL PRIMARY KEY,
        user_id INT UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE,
        passport_number VARCHAR(50)
    );
  2. Один ко многим (One-to-Many, 1:N) Наиболее распространенный тип. Одна запись в таблице А может быть связана со многими записями в таблице Б, но запись в Б ссылается только на одну запись в А.

    -- Пример: Автор и его книги
    CREATE TABLE authors (
        id SERIAL PRIMARY KEY,
        name VARCHAR(255) NOT NULL
    );
    CREATE TABLE books (
        id SERIAL PRIMARY KEY,
        title VARCHAR(255) NOT NULL,
        author_id INT NOT NULL REFERENCES authors(id) ON DELETE SET NULL
    );
  3. Многие ко многим (Many-to-Many, N:M) Реализуется через третью, связующую таблицу (junction table). Одна запись в А может быть связана со многими записями в Б, и наоборот.

    -- Пример: Студенты и курсы
    CREATE TABLE students (
        id SERIAL PRIMARY KEY,
        name VARCHAR(255) NOT NULL
    );
    CREATE TABLE courses (
        id SERIAL PRIMARY KEY,
        title VARCHAR(255) NOT NULL
    );
    CREATE TABLE enrollments ( -- Связующая таблица
        student_id INT NOT NULL REFERENCES students(id) ON DELETE CASCADE,
        course_id INT NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
        enrolled_at TIMESTAMP DEFAULT NOW(),
        PRIMARY KEY (student_id, course_id)
    );

В Node.js приложениях, использующих ORM вроде Sequelize или TypeORM, эти связи определяются в моделях, что упрощает запросы с джойнами.

Ответ 18+ 🔞

Э, слушай, давай про связи в базах данных поговорим, а то у некоторых в голове после JOIN'ов каша, хуй с горы. В реляционках, блядь, всего три основных типа связей через эти самые внешние ключи. Не овердохуища, всего три, запомнить можно.

Первый тип — один к одному. Это когда одной записи в одной таблице соответствует ровно одна хуйня в другой, и наоборот. Как паспорт у человека — один. Часто так делают, когда какие-то данные редко нужны или особо секретные, чтобы основную таблицу не засирать. Смотри, как это выглядит:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL
);
CREATE TABLE user_profiles (
    id SERIAL PRIMARY KEY,
    user_id INT UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    passport_number VARCHAR(50)
);

Видишь UNIQUE на user_id? Вот это и есть вся магия, ёпта. Без него уже будет пиздец, а не связь.

Дальше идёт один ко многим — это, блядь, хлеб насущный, основа основ. Встречается на каждом шагу. Одна запись влечёт за собой кучу других. Как автор, который написал дохуя книг, а каждая книга имеет только одного автора. Проще пареной репы:

CREATE TABLE authors (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);
CREATE TABLE books (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    author_id INT NOT NULL REFERENCES authors(id) ON DELETE SET NULL
);

Вот тут author_id в books — это и есть та самая плодотворная связь, ядрёна вошь. Удали автора — и книги останутся сиротами, NULL станет.

А теперь, блядь, приготовься для самого интересного — многие ко многим. Тут уже без третьей, связующей таблицы не обойтись, хитрая жопа. Как студенты и курсы: один студент ходит на много курсов, на один курс записано много студентов. Реализуется через промежуточную таблицу-сводник, вот эту самую мартышлюшку:

CREATE TABLE students (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);
CREATE TABLE courses (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL
);
-- А вот и наша палочка-выручалочка
CREATE TABLE enrollments (
    student_id INT NOT NULL REFERENCES students(id) ON DELETE CASCADE,
    course_id INT NOT NULL REFERENCES courses(id) ON DELETE CASCADE,
    enrolled_at TIMESTAMP DEFAULT NOW(),
    PRIMARY KEY (student_id, course_id)
);

Видишь? Два внешних ключа и составной первичный ключ. Красота, ёперный театр! Удали студента — и записи о его подписках на курсы хуйнут каскадом. Удобно, бля.

И знаешь что самое приятное? В современных Node.js приложениях, если ты используешь ORM вроде Sequelize, тебе эту всю хуйню с джойнами руками писать не надо. Объявил связи в моделях — и потом просто include делаешь, как будто так и надо. Волнение ебать, как же это упрощает жизнь, чувак. Главное — понять, какая связь где нужна, а то можно такой пиздопроебибны наворотить, что потом сам от себя охуеешь.