← Назад к вопросам

Какие знаешь типы связи между таблицами БД?

1.0 Junior🔥 121 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Типы связей между таблицами в БД

Введение

Связи между таблицами — это основа релационных баз данных. Они позволяют избежать дублирования данных и обеспечивают целостность информации. Есть три основных типа связей.

1. One-to-One (Один к одному)

Определение

Одна строка в таблице A связана с ровно одной строкой в таблице B, и наоборот.

Пример: Профиль пользователя

-- Таблица пользователей
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  username VARCHAR(255) UNIQUE NOT NULL
);

-- Таблица профилей (один профиль на одного пользователя)
CREATE TABLE profiles (
  id SERIAL PRIMARY KEY,
  user_id INT UNIQUE NOT NULL,  -- UNIQUE обеспечивает 1:1
  bio TEXT,
  avatar_url VARCHAR(255),
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

Визуально

users                profiles
+----+----------+    +----+-------+------+
| id | username |    | id | user_id | bio |
+----+----------+    +----+-------+------+
| 1  | alice    | ← → | 1  | 1     | ... |
| 2  | bob      | ← → | 2  | 2     | ... |
| 3  | charlie  | ← → | 3  | 3     | ... |
+----+----------+    +----+-------+------+

Когда использовать

  • Разделение большой таблицы — если таблица имеет много колонок
  • Опциональные данные — профиль может не существовать
  • Различные права доступа — одни данные публичные, другие приватные

SQL запросы

-- Получить профиль пользователя
SELECT u.username, p.bio, p.avatar_url
FROM users u
JOIN profiles p ON u.id = p.user_id
WHERE u.username = 'alice';

-- Вставить пользователя с профилем
INSERT INTO users (username) VALUES ('alice');
INSERT INTO profiles (user_id, bio) VALUES (1, 'Software Engineer');

2. One-to-Many (Один ко многим)

Определение

Одна строка в таблице A связана с несколькими строками в таблице B.

Пример: Пользователь и его посты

-- Таблица пользователей
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  username VARCHAR(255) UNIQUE NOT NULL
);

-- Таблица постов (много постов от одного пользователя)
CREATE TABLE posts (
  id SERIAL PRIMARY KEY,
  user_id INT NOT NULL,  -- Без UNIQUE (может быть много постов)
  title VARCHAR(255) NOT NULL,
  content TEXT,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

Визуально

users              posts
+----+----------+    +----+---------+-------+
| id | username |    | id | user_id | title |
+----+----------+    +----+---------+-------+
| 1  | alice    | ← → | 1  | 1       | Post 1|
|    |          |    | 2  | 1       | Post 2|
| 2  | bob      | ← → | 3  | 2       | Post 3|
+----+----------+    +----+---------+-------+

Когда использовать

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

SQL запросы

-- Получить все посты пользователя
SELECT u.username, p.title, p.created_at
FROM users u
JOIN posts p ON u.id = p.user_id
WHERE u.username = 'alice'
ORDER BY p.created_at DESC;

-- Получить количество постов для каждого пользователя
SELECT u.username, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id, u.username;

-- Вставить пост
INSERT INTO posts (user_id, title, content)
VALUES (1, 'My First Post', 'This is content');

3. Many-to-Many (Много ко многим)

Определение

Одна строка в таблице A связана с несколькими строками в таблице B, и наоборот.

Пример: Студенты и курсы

-- Таблица студентов
CREATE TABLE students (
  id SERIAL PRIMARY KEY,
  name VARCHAR(255) NOT NULL
);

-- Таблица курсов
CREATE TABLE courses (
  id SERIAL PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  description TEXT
);

-- Связующая таблица (junction table)
CREATE TABLE enrollments (
  id SERIAL PRIMARY KEY,
  student_id INT NOT NULL,
  course_id INT NOT NULL,
  enrolled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  grade CHAR(1),  -- A, B, C, D, F
  FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE,
  FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE CASCADE,
  UNIQUE(student_id, course_id)  -- Один студент максимум один раз на курс
);

Визуально

students          enrollments         courses
+----+-----+      +----+--------+--------+      +----+----------+
| id | name|      | id | stud.. | course |      | id | title    |
+----+-----+      +----+--------+--------+      +----+----------+
| 1  |Alice| ← ──→ | 1  | 1      | 1      | ←─→ | 1  |Math      |
|    |     |      | 2  | 1      | 2      |      |    |          |
| 2  |Bob  | ← ──→ | 3  | 2      | 1      | ←─→ | 2  |Physics   |
|    |     |      | 4  | 2      | 3      |      |    |          |
| 3  |Charlie      | 5  | 3      | 2      |      | 3  |Chemistry |
+----+-----+      +----+--------+--------+      +----+----------+

Когда использовать

  • Гибкие связи — когда обе сущности могут иметь множество друг друга
  • Примеры: студенты-курсы, пользователи-группы, продукты-категории, посты-теги

SQL запросы

-- Получить все курсы студента
SELECT c.id, c.title, e.grade
FROM courses c
JOIN enrollments e ON c.id = e.course_id
JOIN students s ON e.student_id = s.id
WHERE s.name = 'Alice';

-- Получить студентов в курсе
SELECT s.id, s.name, e.grade
FROM students s
JOIN enrollments e ON s.id = e.student_id
JOIN courses c ON e.course_id = c.id
WHERE c.title = 'Math';

-- Записать студента на курс
INSERT INTO enrollments (student_id, course_id)
VALUES (1, 1);

-- Выставить оценку
UPDATE enrollments
SET grade = 'A'
WHERE student_id = 1 AND course_id = 1;

-- Удалить студента из курса
DELETE FROM enrollments
WHERE student_id = 1 AND course_id = 1;

Сравнение типов связей

СвязьСоотношениеТаблица связиПример
1:11 ↔ 1Нетuser ↔ passport
1:N1 ↔ NНетuser ↔ posts
N:MN ↔ MДаstudents ↔ courses

Ограничения целостности (Integrity Constraints)

Foreign Key

-- Обеспечивает, что в posts может быть только user_id существующих пользователей
FOREIGN KEY (user_id) REFERENCES users(id)

ON DELETE

-- CASCADE: удаляем все посты при удалении пользователя
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE

-- SET NULL: устанавливаем NULL при удалении
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL

-- RESTRICT: не даем удалить, если есть зависимые записи
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT

Примеры на TypeScript (с использованием ORM)

Один к одному

import { Column, Entity, OneToOne, PrimaryGeneratedColumn } from 'typeorm';

@Entity('users')
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @OneToOne(() => Profile, profile => profile.user)
  profile: Profile;
}

@Entity('profiles')
export class Profile {
  @PrimaryGeneratedColumn()
  id: number;

  @OneToOne(() => User, user => user.profile)
  @JoinColumn({ name: 'user_id' })
  user: User;
}

Один ко многим

@Entity('users')
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @OneToMany(() => Post, post => post.user)
  posts: Post[];
}

@Entity('posts')
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @ManyToOne(() => User, user => user.posts, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'user_id' })
  user: User;
}

Много ко многим

@Entity('students')
export class Student {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @ManyToMany(() => Course, course => course.students)
  @JoinTable({ name: 'enrollments' })
  courses: Course[];
}

@Entity('courses')
export class Course {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @ManyToMany(() => Student, student => student.courses)
  students: Student[];
}

Лучшие практики

  1. Выбирайте правильный тип связи — не усложняйте
  2. Используйте Foreign Keys — обеспечивают целостность
  3. Индексируйте FK колонки — улучшает производительность JOIN
  4. Документируйте связи — нарисуйте ER диаграмму
  5. Избегайте циклических зависимостей — они затрудняют удаление
-- ✅ Индексируем Foreign Key колонки
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_enrollments_student_id ON enrollments(student_id);
CREATE INDEX idx_enrollments_course_id ON enrollments(course_id);

Вывод

Типы связей — это фундамент структуры БД:

  • 1:1 — редко, только когда нужно разделить таблицу
  • 1:N — самый частый тип (пользователь-посты, заказ-товары)
  • N:M — необходим для гибких связей (тэги, категории, подписки)

Правильное использование связей обеспечивает отсутствие дублирования и целостность данных.

Какие знаешь типы связи между таблицами БД? | PrepBro