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

Что необходимо для приведения таблицы к форме Бойса-Кодда кроме детерминанта

2.7 Senior🔥 71 комментариев
#Базы данных и SQL

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

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

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

Нормальная форма Бойса-Кодда (BCNF) - Полное объяснение

Форма Бойса-Кодда (Boyce-Codd Normal Form, BCNF) — это наиболее строгая нормальная форма в теории реляционных БД. Для приведения таблицы к BCNF нужны несколько элементов, кроме детерминанта.

Понятия, необходимые для BCNF

1. Детерминант — атрибут, от которого функционально зависят другие атрибуты

Пример таблицы Student_Course:
┌──────────────┬──────────┬────────────┐
│ StudentID    │ Course   │ Instructor │
├──────────────┼──────────┼────────────┤
│ S001         │ Math     │ Prof. Smith│
│ S001         │ Physics  │ Prof. Jones│
│ S002         │ Math     │ Prof. Smith│
└──────────────┴──────────┴────────────┘

Детерминант: (StudentID, Course)
Зависимый: Instructor

Функциональная зависимость: (StudentID, Course) → Instructor

2. Функциональная зависимость (Functional Dependency, FD)

Атрибут B функционально зависит от атрибута A, если каждому значению A соответствует ровно одно значение B.

Обозначение: A → B (читается: A определяет B)

Пример:
- Student ID → Student Name (один студент, одно имя)
- (Student ID, Course) → Grade (для каждой пары уникальная оценка)

3. Неполная функциональная зависимость

Атрибут C зависит от составного ключа (A, B), но также зависит только от A или только от B.

Плохо (неполная зависимость):
(StudentID, Course) → InstructorName
InstructorName зависит только от Course, не от всего ключа!

Хорошо (полная зависимость):
(StudentID, Course) → Grade
Grade зависит от обоих: студента И курса

4. Транзитивная зависимость

Атрибут C зависит от атрибута A через атрибут B.

Пример:
StudentID → Department
Department → Building

Транзитивная зависимость:
StudentID → Building (косвенно, через Department)

Определение BCNF

BCNF: Таблица находится в форме Бойса-Кодда, если
      для каждой функциональной зависимости A → B:
      
      1. A — детерминант (определяющий)
      2. A содержит первичный ключ таблицы
         (или является первичным ключом/его частью)

Проще: Каждый детерминант должен быть потенциальным ключом (candidate key).

Пример: приведение к BCNF

Исходная таблица (НЕ в BCNF):

CREATE TABLE StudentCourse (
    StudentID INT,
    Course VARCHAR(50),
    Instructor VARCHAR(100),
    Semester VARCHAR(20),
    PRIMARY KEY (StudentID, Course, Semester)
);

Данные:
┌──────────────┬──────────┬────────────┬──────────┐
│ StudentID    │ Course   │ Instructor │ Semester │
├──────────────┼──────────┼────────────┼──────────┤
│ S001         │ Math     │ Prof. Smith│ Spring   │
│ S001         │ Physics  │ Prof. Jones│ Spring   │
│ S002         │ Math     │ Prof. Smith│ Spring   │
└──────────────┴──────────┴────────────┴──────────┘

Функциональные зависимости:
1. (StudentID, Course, Semester) → Instructor (первичный ключ)
2. (Course, Semester) → Instructor ❌ ПРОБЛЕМА!
   Instructor зависит ТОЛЬКО от Course и Semester,
   а не от всего первичного ключа (StudentID)!

Проблема: (Course, Semester) — детерминант, но он НЕ является первичным ключом!

Решение: разделить на две таблицы

-- Таблица 1: студент-курс (без инструктора)
CREATE TABLE Enrollment (
    StudentID INT,
    Course VARCHAR(50),
    Semester VARCHAR(20),
    PRIMARY KEY (StudentID, Course, Semester)
);

-- Таблица 2: курс-инструктор (новая таблица)
CREATE TABLE CourseInstructor (
    Course VARCHAR(50),
    Semester VARCHAR(20),
    Instructor VARCHAR(100),
    PRIMARY KEY (Course, Semester)
);

-- Внешний ключ
ALTER TABLE Enrollment 
ADD FOREIGN KEY (Course, Semester) 
REFERENCES CourseInstructor(Course, Semester);

Теперь каждый детерминант — первичный ключ!

Необходимые условия для BCNF (кроме детерминанта)

1. Первичный ключ (Primary Key)

// В Java/Hibernate:
@Entity
@Table(name = "enrollment")
public class Enrollment {
    
    @EmbeddedId  // Составной первичный ключ
    private EnrollmentId id;  // EnrollmentId содержит StudentID, Course, Semester
    
    // Все остальные атрибуты должны зависеть ТОЛЬКО от этого ключа
}

@Embeddable
public class EnrollmentId implements Serializable {
    @Column(name = "student_id")
    private Long studentId;
    
    @Column(name = "course")
    private String course;
    
    @Column(name = "semester")
    private String semester;
}

2. Внешние ключи (Foreign Keys)

-- Связь между таблицами
ALTER TABLE Enrollment 
ADD FOREIGN KEY (Course, Semester) 
REFERENCES CourseInstructor(Course, Semester);

-- В Java:
@Entity
@Table(name = "enrollment")
public class Enrollment {
    @EmbeddedId
    private EnrollmentId id;
    
    @ManyToOne  // Это вторичный ключ
    @JoinColumns({
        @JoinColumn(name = "course", referencedColumnName = "course"),
        @JoinColumn(name = "semester", referencedColumnName = "semester")
    })
    private CourseInstructor courseInstructor;
}

3. Кандидатские ключи (Candidate Keys)

Все потенциальные ключи должны быть определены:

Таблица Course_Instructor:
┌──────────┬──────────┬────────────┐
│ Course   │ Semester │ Instructor │
└──────────┴──────────┴────────────┘

Каждая комбинация (Course, Semester) уникальна ✓
Это первичный ключ и единственный кандидат ✓

4. Отсутствие остаточных зависимостей

После приведения к BCNF:
- Неполные функциональные зависимости: УДАЛЕНЫ ✓
- Транзитивные зависимости: УДАЛЕНЫ ✓
- Мультизначные зависимости: РАЗДЕЛЕНЫ ✓

Полный пример

Исходная схема (3NF, но НЕ BCNF):

CREATE TABLE Professor_Course_Time (
    ProfessorID INT,
    Course VARCHAR(50),
    TimeSlot VARCHAR(50),
    Building VARCHAR(50),
    PRIMARY KEY (ProfessorID, Course, TimeSlot)
);

Функциональные зависимости:
1. ProfessorID → Building (профессор учит в одном здании)
2. (Course, TimeSlot) → ProfessorID (в каждый слот для курса один преподаватель)
3. ProfessorID → Building (транзитивная через Course)

Проблема: ProfessorID — детерминант, но не первичный ключ!

Приведение к BCNF:

-- Таблица 1: профессор-здание
CREATE TABLE Professor (
    ProfessorID INT PRIMARY KEY,
    Building VARCHAR(50)
);

-- Таблица 2: курс-время-профессор
CREATE TABLE CourseSchedule (
    Course VARCHAR(50),
    TimeSlot VARCHAR(50),
    ProfessorID INT NOT NULL,
    PRIMARY KEY (Course, TimeSlot),
    FOREIGN KEY (ProfessorID) REFERENCES Professor(ProfessorID)
);

Теперь все детерминанты — первичные ключи ✓

Проверка BCNF

Алгоритм:

1. Список все функциональные зависимости
2. Для каждой зависимости A → B:
   ├─ Является ли A первичным ключом? ✓ ОК
   ├─ Является ли A кандидатским ключом? ✓ ОК
   └─ Ни то ни другое? ❌ НАРУШЕНИЕ BCNF
3. Если есть нарушение:
   ├─ Разделить таблицу
   ├─ Создать новую таблицу с зависимостью
   ├─ Добавить внешний ключ
   └─ Повторить проверку

Отличие от 3NF

3NF:  Атрибут может быть зависим от части ключа
      (неполная зависимость разрешена)

BCNF: Все неполные зависимости ДОЛЖНЫ быть удалены
      Только детерминанты, являющиеся ключами, разрешены

Таблица сравнения:

Аспект3NFBCNF
Неполные FD✗ Удалены✗ Удалены
Транзитивные FD✗ Удалены✗ Удалены
Остаточные FD? Могут быть✗ Не допускаются
ДетерминантыМогут быть любымиТОЛЬКО первичные ключи
СложностьПроще реализоватьТребует больше таблиц

Итог: Для BCNF нужны:

  1. Детерминант (определяющий атрибут)
  2. Первичный ключ (уникальный идентификатор)
  3. Функциональные зависимости (отношения между атрибутами)
  4. Внешние ключи (связи между таблицами)
  5. Декомпозиция (разделение на несколько таблиц)
Что необходимо для приведения таблицы к форме Бойса-Кодда кроме детерминанта | PrepBro