Лучший подход к моделированию данных для обработки избыточных внешних ключей в базе данных об опросах, вопросах и ответах


13

Я ищу советы о лучшем подходе реляционного моделирования для хранения опросов, вопросов и ответов.

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

У меня есть по крайней мере эти лица:

  • вопрос
  • опрос
  • человек

И хотя бы эти отношения:

  • Каждый опрос имеет 1 или более вопросов.
  • Каждый вопрос может быть использован в 0 или более опросах.
  • Каждый человек может пройти 0 или более опросов.

Вот где я сталкиваюсь с неприятностями: как смоделировать ответы на вопросы опроса, сделанные человеком.

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

Подход 1: Подход 1

Что мне не нравится в этом подходе:

  • survey_person_question_responseТаблица имеет две различные столбцы , которые относятся к опросу: survey_question_survey_idиsurvey_person_survey_id
    • Было бы ошибкой иметь разные survey_idссылки в одной строке для этих двух столбцов. Survey_question должен быть из того же опроса, что и человек, который принимал участие в survey_person. Я не вижу хорошего способа обеспечить это.
  • Похоже, что я здесь делаю отношения между двумя. Это почему-то неправильно для меня.

Подход 2:

Старайтесь избегать двух FK из подхода 1, которые должны ссылаться на одно и то же значение ... введите описание изображения здесь

Что мне не нравится в этом подходе:

  • Нет никакого принуждения, что question_idи survey_idFK из действительной survey_questionпары
  • Нет никакого принуждения, что survey_idи person_idFK из действительной survey_personпары

Любой совет по:

  • Является ли один из этих подходов типичным подходом?
  • Плюсы и минусы одного из этих подходов над другим
  • Лучший способ упорядочить эти данные полностью

Будем весьма признательны!

Ответы:


12

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

  1. тип связи (или ассоциации ) между типами сущностей Person и Survey ;
  2. тип отношений между опросом и вопросом ;
  3. тип отношений, который устанавливает связь между двумя вышеупомянутыми типами отношений и, как следствие, между человеком , опросом и вопросом , т. е. ответ (более короткое имя, которое упрощает интерпретацию, с моей точки зрения).

Итак, я считаю, что вы находитесь на правильном пути с вашим подходом 1 , хотя он требует некоторых небольших (но важных) уточнений, чтобы сделать его более точным. Я подробно опишу такие уточнения и другие соответствующие соображения в следующих разделах.

Бизнес правила

Давайте немного расширим применимые бизнес-правила и переформулируем их следующим образом:

  • Person регистров в нуль один или-многих обследований
  • Survey получает регистрацию нулевой один или-многих людей
  • Survey интегрируется один-ко-многим вопросам
  • Вопрос интегрирует нулевой один или многим Surveys
  • Вопрос получает нуль-один или многих ответов
  • Ответ обеспечивается точно-один Человек в контексте точно один год обследования

Описательная схема IDEF1X

Затем я создал IDEF1x на схему , что представлено на рисунке 1 , который синтезирует бизнес - правила , сформулированные выше:

Рис.1 Упрощенный опрос IDEF1X


Определение интеграции для информационного моделирования ( IDEF1X ) является весьма рекомендуется метод моделированиякоторый был созданкачестве стандарта в декабре 1993 года Национальным институтом Соединенных Штатов стандартов и технологий ( NIST ). Она прочно основана на теоретическую работе авторства единственным учредителем в реляционной модели , т.е. д - р Ф. Кодда , а также на взгляде сущности-связи , разработанный д - ром П. Чен .


PersonSurvey отношения

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

SurveyQuestion отношения

Я предполагаю, что свойство (или атрибут), называемое suvery_question.question_number в вашей диаграмме, используется для представления порядка представления данного экземпляра Вопроса в отношении конкретного опроса . Как вы можете видеть, я обозначил такое свойство как SurveyQuestion.PresentationOrder , и я думаю, что вы должны предотвратить, чтобы (i) два или более значения Question.QuestionNumber разделяли (ii) одно и то же значение PresentationOrder в (iii) одно и то же вхождение SurveyQuestion .

Чтобы изобразить эту потребность, я включил составной ALTERNATE KEY (AK) в поле, представляющее этот тип сущности, который состоит из комбинации свойств ( SurveyNumber, QuestionNumber, PresentationOrder ). Как вам хорошо известно, составной AK может быть объявлен в логической структуре DDL с помощью ограничения UNIQUE, состоящего из нескольких столбцов (как я показал в SurveyQuestionтаблице, которая является частью описательной схемы DDL, изложенной в нескольких разделах ниже).

Тип объекта ответа

Да, с типом объекта Ответ я изображаю отношения между двумя другими отношениями ; На первый взгляд это может показаться неловким, но в этом подходе нет ничего плохого, если он (а) точно представляет особенности интересующего бизнес-контекста и (б) правильно представлен в макете логического уровня.

Да, вы абсолютно правы, было бы ошибкой изображать эту часть сценария на логическом уровне абстракции с помощью двух Response.SurveyNumber(или, скажем, Response.SurveyId) значений, на которые ссылаются два разных столбца в одной Responseстроке.

Производная логическая структура SQL-DDL

-- You should determine which are the most fitting 
-- data types and sizes for all your table columns 
-- depending on your business context characteristics.

-- As one would expect, you are free to make use of 
-- your preferred (or required) naming conventions.

CREATE TABLE Person (
    PersonId        INT      NOT NULL,
    FirstName       CHAR(30) NOT NULL,
    LastName        CHAR(30) NOT NULL,
    GenderCode      CHAR(3)  NOT NULL,
    BirthDate       DATE     NOT NULL,
    CreatedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT Person_PK PRIMARY KEY (PersonId),
    CONSTRAINT Person_AK UNIQUE      (
        FirstName,
        LastName,
        GenderCode,
        BirthDate
    )
);

CREATE TABLE Survey (
    SurveyNumber    INT       NOT NULL,
    Description     CHAR(255) NOT NULL,
    CreatedDateTime DATETIME  NOT NULL,
    --
    CONSTRAINT Survey_PK PRIMARY KEY (SurveyNumber),
    CONSTRAINT Survey_AK UNIQUE      (Description)
);

CREATE TABLE PersonSurvey (
    PersonId           INT      NOT NULL,
    SurveyNumber       INT      NOT NULL,
    RegisteredDateTime DATETIME NOT NULL,
    --
    CONSTRAINT PersonSurvey_PK         PRIMARY KEY (PersonId, SurveyNumber),
    CONSTRAINT PersonSurveyToPerson_FK FOREIGN KEY (PersonId)
        REFERENCES Person (PersonId),
    CONSTRAINT PersonSurveyToSurvey_FK FOREIGN KEY (SurveyNumber)
        REFERENCES Survey (SurveyNumber)
);

CREATE TABLE Question (
    QuestionNumber  INT       NOT NULL,
    Wording         CHAR(255) NOT NULL,
    CreatedDateTime DATETIME  NOT NULL,
    --
    CONSTRAINT Question_PK PRIMARY KEY (QuestionNumber),
    CONSTRAINT Question_AK UNIQUE      (Wording)
);

CREATE TABLE SurveyQuestion (
    SurveyNumber       INT      NOT NULL,
    QuestionNumber     INT      NOT NULL,
    PresentationOrder  TINYINT  NOT NULL,
    IsMandatory        BIT      NOT NULL,
    IntegratedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT SurveyQuestion_PK PRIMARY KEY (SurveyNumber, QuestionNumber),
    CONSTRAINT SurveyQuestion_AK UNIQUE      (
        QuestionNumber,
        SurveyNumber,
        PresentationOrder
    ),
    CONSTRAINT SurveyQuestionToSurvey_FK   FOREIGN KEY (SurveyNumber)
        REFERENCES Survey   (SurveyNumber),
    CONSTRAINT SurveyQuestionToQuestion_FK FOREIGN KEY (QuestionNumber)
        REFERENCES Question (QuestionNumber)
);

CREATE TABLE Response (
    SurveyNumber     INT      NOT NULL,
    QuestionNumber   INT      NOT NULL,
    PersonId         INT      NOT NULL,
    Content          TEXT     NOT NULL,
    ProvidedDateTime DATETIME NOT NULL,
    --
    CONSTRAINT Response_PK                 PRIMARY KEY (SurveyNumber, QuestionNumber, PersonId),
    CONSTRAINT ResponseToPersonSurvey_FK   FOREIGN KEY (PersonId, SurveyNumber)
        REFERENCES PersonSurvey   (PersonId, SurveyNumber),
    CONSTRAINT ResponseToSurveyQuestion_FK FOREIGN KEY (SurveyNumber, QuestionNumber)
        REFERENCES SurveyQuestion (SurveyNumber, QuestionNumber)
);

Два составных ИНОСТРАННЫХ КЛЮЧА в Responseтаблице

Это, пожалуй, самый важный момент для обсуждения: ссылки на данную Responseстроку

  1. SurveyQuestion.SurveyNumber, и
  2. SurveyPerson.SurveyNumber

должны иметь совпадающие значения . Насколько мне известно, лучший вариант для принудительного применения этого условия декларативным способом - использование двух составных FOREIGN KEY (FK).

Как показано в дизайне DDL, первый FK делает ссылку на PersonSurveyтаблицу PRIMARY KEY (PK), т. (PersonId, SurveyNumber)Е. И соответствует столбцам Response.PersonIdи Response.SurveyNumber.

Второй FK указывает на SurveyQuestionтаблицу PK, т. Е. (SurveyNumber, QuestionNumber)И, соответственно, состоит из столбцов Response.SurveyNumberи Response.QuestionNumber.

Таким образом, Response.SurveyNumberстолбец весьма полезен, поскольку он используется как часть ссылки FK в двух разных ограничениях.

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

  • (а) Responseк PersonSurvey;
  • (б) Responseк SurveyQuestion; и
  • (c) каждая из таблиц, представляющих тип ассоциативного объекта для таблиц, обозначающих независимые типы объектов, а именно Person, Surveyи Question.

Производные данные, чтобы избежать обновления аномалий

Я заметил на вашей диаграмме два элемента, которые я считаю заслуживающими упоминания. Эти элементы связаны с двумя PersonSurveyстолбцами, которые могут (должны) быть получены .

В связи с этим, вы можете получить PersonSurvey.IsStartedданные путем запроса, если конкретное Personвхождение предоставило одно или несколько значений Responsesдля Questionsинтегрирования точного Surveyчерез SurveyQuestionтаблицу.

И вы также можете получить PersonSurvey.IsCompletedточку данных, определив, предоставил ли данный Personэкземпляр Responseвсем, Questionsчто совпадает со значением «ИСТИНА» в IsMandatoryстолбце в определенной SurveyQuestionстроке.

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

Важное соображение

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


1
Вау, это прекрасно ответило на вопрос в моей голове, а затем научило меня больше! Поскольку комментарии должны предлагать улучшения: было немного странно, что ключи заканчивались на оба IDи Number, но в остальном это фантастика. Спасибо.
Зак Межеевский,

@ Зач, добро пожаловать, я рад, что пост помог тебе. Спасибо за отзыв, некоторые уточнения решительно необходимы.
MDCCL

1

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

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


Спасибо за понимание - я упал до 1 столбца в физической модели, которая обеспечивает все, что я хотел.
код
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.