дизайн реляционной базы данных (нормализация сопоставлений «многие ко многим»)

Ниже приведен аналогичный (и упрощенный) пример вопроса дизайна, с которым я столкнулся:

Предположим, у вас есть ученики, классы и оценки. Студенты могут быть в разных классах. В каждом классе много разных учеников. И каждая пара (ученик, класс) имеет одну оценку.

Должен ли я размещать базу данных (базу данных mysql), например:

Опция 1)

students table - (student_id, student_name)
classes table - (class_id, class_name)
students_classes table - (student_class_id, student_id, class_id)
grades table - (student_class_id, grade)

Вариант 2)

students table - (student_id, student_name)
classes table - (class_id, class_name)
grades table - (student_id, class_id, grade)

Или он должен быть разработан как что-то еще? Вариант 2 сейчас кажется проще, но в будущем мне могут понадобиться другие статистические данные, относящиеся к каждой паре (student_id, class_id) (в этом случае вариант 1 кажется немного лучше? Хотя вариант 1 все еще кажется слишком сложным).

Что вы порекомендуете? Спасибо.


person Peter1491    schedule 19.11.2010    source источник
comment
Разве в вашем примере оценка не является атрибутом ученика?   -  person JNK    schedule 19.11.2010
comment
Предположим, что это также может быть числовая оценка   -  person Peter1491    schedule 19.11.2010
comment
@JNK: да, но это также атрибут класса - ученик может получить пятерку по математике и пятерку по английскому языку.   -  person Jonathan Leffler    schedule 19.11.2010
comment
@Jonathan - По какой-то причине я подумал, что для ученика это был 7-й класс, а не его оценка в классе. В этом отношении есть смысл.   -  person JNK    schedule 19.11.2010
comment
Пожалуйста, проверьте мои комментарии к ответам и мой ответ.   -  person PerformanceDBA    schedule 28.11.2010


Ответы (5)


Вариант 3)

students table - (student_id, student_name)
classes table - (class_id, class_name)
students_classes table - (student_class_id, student_id, class_id, grade)

Оценка является атрибутом студенческого класса.

Если только у Grade нет возможности стать полноценной сущностью. В таком случае:

Вариант 4)

students table - (student_id, student_name)
classes table - (class_id, class_name)
students_classes table - (student_class_id, student_id, class_id)
grades table - (grade_id, grade, student_class_id)
person Axn    schedule 19.11.2010
comment
-1. Оба варианта неверны. student_class не требует student_class_id, это избыточный столбец с дополнительным индексом, который открывает таблицу для дубликатов. ПК (student_id, class_id). Если вы удалите его, вы получите вариант 2, за исключением того, что он должен называться student_class. grades основано на ошибке, поэтому оно также неверно. Если бы Grade стал полноценной сущностью, это было бы совсем другое дело, здесь это просто дочерняя таблица 1::1; который можно нормализовать в student_class. - person PerformanceDBA; 28.11.2010

Я бы выбрал вариант 2 лично. Нет ничего плохого в составном первичном ключе для оценок, и он фиксирует необходимую информацию в вашей модели данных.

В варианте 1 student_classes не служит никакой цели, кроме как иметь суррогатный ключ.

Изменить, увидев другие ответы:

  • 2NF: оценка (неключевая) зависит исключительно от учащегося/класса (ключевая)
  • 3NF: не применяется. У вас нет неключевых на неключевых зависимостях
  • BCNF: не применяется, у вас есть только один ключ-кандидат
person gbn    schedule 19.11.2010
comment
Вариант 2 IS 3NF. Вы правильно идентифицировали составной ключ, но не зарегистрировали, что grade является чистой зависимостью от него 1::1. - person PerformanceDBA; 28.11.2010
comment
-1. Вариант 2 является 3NF. Вы правильно идентифицировали составной ключ, но не зарегистрировали, что оценка является чистой зависимостью от него 1::1. - person PerformanceDBA; 28.11.2010

Вариант 2 правильный, за исключением того, что он должен называться student_class, отражая его функцию n::n или Enrollment как сущность. и (student_id, class_id)является ПК.

Оценка (как вы это показали) является зависимостью 1::1 от этого составного ключа (не от того или иного элемента) и ни от чего другого, поэтому она является атрибутом student_class.

И поэтомуstudent_classнаходится в 3НФ.

Если бы люди не начали слепо прикреплять столбцы Idiot ко всему, что движется, как вы сделали с Вариантом 1, они смогли бы лучше понимать данные и, таким образом, лучше нормализовать их. Это (столбец Idiot в Варианте 1 в качестве отправной точки) мешало вашей интуиции, что (student_id, class_id) был Идентификатором; не было необходимости в дополнительном столбце Idiot с его дополнительным индексом. Затем, когда вы приступили к оценкеgrade, его зависимость от этого PK очевидна.

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

То, что в «нормализованной» базе данных так много соединений, верно только наполовину. Полная правда в том, что поскольку база данных не нормализована правильно, да, вы вынуждены выполнять намного больше соединений, чем необходимо. В действительно нормализованной базе данных с теми же таблицами код требует гораздо меньше объединений.

Вот >Модель данных для колледжа‹< /strong> из недавнего задания, упрощенная версия.

>Нотация IDEF1X‹ для тех, кому нужно объяснение символов.

  • Обратите внимание, что требуется только один суррогатный ключ.

    • This is because in the alternative, (LastName+FirstName+Initials_BirthDate+BithDate) would be the Person PK, and that would be carried as FK in 5 child/grandchild tables, which is 81 bytes, and that is not sensible.
      .
  • Посмотрите, понимаете ли вы, что Идентификаторы (сплошные линии) передаются детям и внукам; они имеют и передают значение

  • Было бы глупо добавлять суррогатные ключи для TeacherId, StudentId, StaffId, когда у нас есть совершенно хороший PersonId, который является внешним ключом и уже уникален. (Столбцы названы так, чтобы идентифицировать их роли.)

  • Все бизнес-правила были реализованы в DDL: FK Constraints; Проверить ограничения; Правила.

    • Комната имеет составной ключ с 4 столбцами; Предложение имеет составной ключ из 3 столбцов; вместе они исключают двойное бронирование.

    • ПК предложения и ПК студента вместе образуют ПК для зачисления (идентично этому Вопросу; ПК состоят из разных столбцов, вот и все).

person PerformanceDBA    schedule 28.11.2010
comment
Обратите внимание, что требуется только один суррогатный ключ. - Почему это требуется? Чтобы отличить двух людей с одинаковыми именем, фамилией, местом рождения и датой рождения? - person Tomislav Nakic-Alfirevic; 22.12.2010
comment
@Томислав. Отредактировал свой пост в цитируемом тексте. Можно предположить, что уникальность для АК можно обеспечить каким-то другим способом, например. это пример, реальный db будет иметь столбец UpdatedDateTime и т. д. Для сравнения, Room PK (4 столбца, короткие) переносится в Offering как неидентифицирующий FK. - person PerformanceDBA; 22.12.2010

Я поклонник третьей нормальной формы, когда у вас есть отдельные таблицы Student, Class и Grade, и они связаны с таблицами типа «многие ко многим», такими как ClassStudent и GradeClass.

Но это зависит от того, как вы хотите сохранить его в будущем. В конечном итоге все сводится к будущему расширению и ремонтопригодности. Вот почему я предпочитаю 3NF.

ИЗМЕНИТЬ

Axn's answer намного лучше моего.

person Community    schedule 19.11.2010
comment
+1 Помните, что многие столы становятся медленными, как собака, когда становятся большими. Другое, что я полностью согласен. - person Byron Whitlock; 19.11.2010
comment
Но в этом примере мне понадобится таблица ClassStudentGrade..? (поскольку у учеников может быть много оценок... но только 1 оценка за класс. Таким образом, каждая пара ученик-класс имеет ровно 1 оценку) - person Peter1491; 19.11.2010
comment
3NF не зависит от проблемы, поставленной OP - person gbn; 19.11.2010
comment
@gbn, моя главная мысль заключалась в том, что это зависит. Будущее использование системы должно определять дизайн уже сейчас. - person ; 19.11.2010
comment
@Randolph Potter: я перефразирую, 3NF здесь не применяется. Это не должно было быть упомянуто. Кроме того, вы либо включаете требование сейчас, либо решаете проблему на месте. Вы не можете предсказать будущее требование по определению. И ЯГНИ. - person gbn; 19.11.2010
comment
@ Рэндольф Поттер: спасибо. Проверьте ответ Акна. Это прекрасно покрывает 3NF (пока мы обсуждали :-) - person gbn; 19.11.2010
comment
@ Байрон. Нормализованные таблицы n::n слепо быстры, PK равен (student_id, class_id). Но если у вас есть Id iot PK, избыточность, дополнительный индекс, конечно, они медленные. - person PerformanceDBA; 28.11.2010
comment
@gbn. Эм, вся идея нормализации базы данных заключается в том, чтобы нормализовать данные. Независимо от приложения и функций. Здесь востребован 3NF, бояться нечего. Это не решение проблемы, которой (пока) нет; это гарантирует, что у вас нет проблем. Дополнительных затрат нет, только скорость. - person PerformanceDBA; 28.11.2010
comment
@Рэндольф. Хорошо иметь 3NF как минимум. GradeClass может быть нормализовано в ClassStudent, grade равно 1::1 с этим PK. Когда вы это сделаете, у вас будет 3NF, до тех пор у вас его нет (grade зависит от PK в 2 таблицах). Если вы исправите свой ответ, я удалю -1. - person PerformanceDBA; 28.11.2010

Все зависит, правда. Вариант 1, вероятно, самый надежный способ сделать это приложение; вариант 2 может помочь вам быстрее в этой итерации. Будет ли переход с варианта 2 -> 1 таким болезненным в будущем? Насколько вы уверены, что вам понадобится эта дополнительная гибкость?

Я бы порекомендовал просто выбрать вариант 1. Запросы не будут намного сложнее, и если вы используете ORM (например, ActiveRecord для Rails), то разница практически нулевая.

person alejandro5042    schedule 19.11.2010
comment
Вопрос касается дизайна и нормализации БД, а не того, что приложение может или не может делать легко. Я думаю, вы имеете в виду ноль. - person PerformanceDBA; 28.11.2010