В чем необходимость соединительных таблиц?

Я реализовал следующие способы хранения реляционной топологии:

1. Общая таблица взаимосвязей узлов:

Таблица: отношение

Столбцы: id parent_type parent_id parent_prop child_type child_id child_prop

Для которых соединения обычно не могут выполняться большинством движков sql.

2. Соединительные таблицы для конкретных отношений

Таблица: Class2Student

Столбцы: id parent_id parent_prop child_id child_prop

Для которых могут выполняться соединения.

3. Хранение списков/строковых карт связанных объектов в поле text на обоих двунаправленных объектах.

Класс: Класс

Свойства класса: идентификатор имени ученика

Столбцы таблицы: имя идентификатора student_keys

Строки: 1 история [{type:Basic_student,id:1},{type:Advanced_student,id:3}]

Чтобы включить соединения с помощью движков sql, можно было бы написать пользовательский модуль, который был бы еще проще, если бы содержимое student_keys было просто [1,3], т. е. отношение было к явному типу Student.

Вопросы следующие в контексте:

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

  • Невозможность логически правильно сохранить двунаправленные отношения (например, нет потери данных в двунаправленных отношениях или любых отношениях с полем keys, потому что одно рекурсивно сохраняется, и можно довольно легко принудительно применить другие операции (удалить, обновить)).
  • Неспособность эффективно присоединиться

Я не запрашиваю мнения о вашем личном мнении о лучших практиках или каких-либо культовых заявлениях о нормализации.

Явные вопросы:

  1. Каковы случаи, когда нужно запросить соединительную таблицу, которая не предоставлена, путем запроса поля keys объекта-владельца?
  2. Каковы проблемы логической реализации в контексте вычислений, предоставляемых механизмом sql, где предпочтительнее использовать соединительную таблицу?
  3. Единственная разница в реализации полей junction table и keys заключается в следующем:

При поиске запроса следующего характера вам нужно будет сопоставить поле keys либо с пользовательской реализацией индексации, либо с какой-либо другой разумной реализацией:

class_dao.search({студенты:advanced_student_3,имя:история});

поиск классов, в которых есть конкретный ученик и история имени

В отличие от поиска в индексированных столбцах соединительной таблицы и последующего выбора соответствующих классов.

Мне не удалось найти ответы, почему таблица соединений логически предпочтительнее буквально по любой причине. Я не утверждаю, что это так, или у меня так или иначе есть религиозные предпочтения, о чем свидетельствует тот факт, что я применил несколько способов достижения этого. Моя проблема в том, Я не знаю, что это такое.


person MetaChrome    schedule 13.03.2014    source источник
comment
Итак, вы хотите хранить денормализованные данные в реляционной базе данных?   -  person Jodrell    schedule 13.03.2014
comment
Я хочу понять функциональную точку соединительной таблицы в контексте стоимости вычислений, а не обязательно двусмысленного термина реляционная база данных. Я не собираюсь пересказывать весь контекст, в котором я хочу понять суть, потому что это будет просто переформулировка вопроса.   -  person MetaChrome    schedule 13.03.2014
comment
TL; DR - как вы храните отношения N-N в базе данных, особенно когда обе стороны отношений имеют сотни записей?   -  person Arvo    schedule 13.03.2014


Ответы (1)


Как я это вижу, у вас есть несколько сущностей

CREATE TABLE StudentType
(
    Id Int PRIMARY KEY,
    Name NVarChar(50) 
);

INSERT StudentType VALUES
(
    (1, 'Basic'),
    (2, 'Advanced'),
    (3, 'SomeOtherCategory')
);

CREATE TABLE Student
(
    Id Int PRIMARY KEY,
    Name NVarChar(200),
    OtherAttributeCommonToAllStudents Int,
    Type Int,
    CONSTRAINT FK_Student_StudentType
        FOREIGN KEY (Type) REFERENCES StudentType(Id)
)

CREATE TABLE StudentAdvanced
(
    Id Int PRIMARY KEY,
    AdvancedOnlyAttribute Int,
    CONSTRIANT FK_StudentAdvanced_Student
        FOREIGN KEY (Id) REFERENCES Student(Id)
)

CREATE TABLE StudentSomeOtherCategory
(
    Id Int PRIMARY KEY,
    SomeOtherCategoryOnlyAttribute Int,
    CONSTRIANT FK_StudentSomeOtherCategory_Student
        FOREIGN KEY (Id) REFERENCES Student(Id)
)
  1. Все атрибуты, общие для всех учащихся, имеют столбцы в таблице Student.
  2. Типы учеников, которые имеют дополнительные атрибуты, добавляются в таблицу StudentType.
  3. Каждый дополнительный тип учащегося получает таблицу Student<TypeName> для хранения своих конкретных атрибутов. Эти таблицы имеют необязательное взаимно-однозначное отношение с Student.

Я думаю, что ваша соединительная таблица «соломенный человек» является частичной реализацией анти-шаблона EAV, единственный раз, когда это разумно, - это когда вы не можете знать, какие атрибуты вам нужно моделировать, т. е. ваши данные будут полностью неструктурированы . Когда это действительно необходимо, реляционные базы данных становятся менее желательными. В таких случаях рассмотрите альтернативу базе данных NOSQL/Document.


Таблица соединений будет полезна в следующем сценарии.

Скажем, мы добавляем в модель объект класса.

CREATE TABLE Class
(
    Id Int PRIMARY KEY,
    ...
)

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

CREATE TABLE Registration
(
    Id Int PRIMARY KEY,
    StudentId Int,
    ClassId Int,
    CONSTRAINT FK_Registration_Student
        FOREIGN KEY (StudentId) REFERENCES Student(Id),
    CONSTRAINT FK_Registration_Class
        FOREIGN KEY (ClassId) REFERENCES Class(Id)
)

Этот объект был бы подходящим местом для хранения атрибутов, относящихся конкретно к регистрации учащегося в классе, например, флага завершения. Другие данные, естественно, будут относиться к этому перекрестку, например, запись о посещаемости конкретного класса или история оценок.

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


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

CREATE TABLE StudentClass
(
    StudentId Int,
    ClassId Int,
    CONSTRAINT PK_StudentClass PRIMARY KEY (ClassId, StudentId),
    CONSTRAINT FK_Registration_Student
        FOREIGN KEY (StudentId) REFERENCES Student(Id),
    CONSTRAINT FK_Registration_Class
        FOREIGN KEY (ClassId) REFERENCES Class(Id)
)

это позволяет выполнять простые запросы, например

// students in a class?
SELECT StudentId
FROM StudentClass
WHERE ClassId = @classId

// classes read by a student?
SELECT ClassId
FROM StudentClass
WHERE StudentId = @studentId

Кроме того, это обеспечивает простой способ частичного или полного управления взаимосвязью в любом аспекте, который будет знаком разработчикам реляционных баз данных и доступен для оптимизаторов запросов.

person Jodrell    schedule 13.03.2014
comment
Вопрос касается не типа иерархии Student, а скорее отношения Class2Student. Возьмем простейший случай для оценки эффективности соединительной таблицы. Добавление иерархии типов учащихся должно было проиллюстрировать тип пользовательской индексации, необходимый для реализации #3. Каковы преимущества в контексте: [производительности вычислений, функциональности], предлагаемых соединительной таблицей? Зачем вам использовать соединительную таблицу, если она вызывает 2 соединения вместо 1 и не предоставляет никакой функциональности? - person MetaChrome; 13.03.2014
comment
@MetaChrome Надеюсь, мой расширенный ответ лучше ответит на ваш вопрос. - person Jodrell; 13.03.2014
comment
Пример свойств самого отношения действителен и потребует сущности соединения/отношения. - person MetaChrome; 13.03.2014
comment
select * from class join student where (student.id in class.student_keys) в контексте правильно сохраненной двунаправленной связи можно было бы назначить все классы студента по характеру того, что они возвращаются запросом (с итерацией студентов после инициализации классов). для не двунаправленного отношения между двумя типами... думая об этом. вложенное соединение? - person MetaChrome; 13.03.2014
comment
Я полагаю, что в целом это вопрос того, сколько вложенных соединений вы хотите выполнить для поддержки глубокого каскада в общем случае. Если вы не собираетесь выполнять глубокие каскады с соединениями, это может не иметь значения (в том смысле, что вы решили выполнять несколько запросов для облегчения глубоких каскадов, когда кеш объектов недоступен)? Однако в случае желания иметь такой пользовательский запрос, я полагаю, это будет несколько запросов или вложенное рекурсивное соединение. - person MetaChrome; 13.03.2014
comment
@MetaChrome Я добавил больше о ситуации, когда атрибутов не существует. - person Jodrell; 13.03.2014