Melhor abordagem de modelagem de dados para lidar com chaves estrangeiras redundantes em um banco de dados sobre pesquisas, perguntas e respostas

13

Estou procurando conselhos sobre a melhor abordagem de modelagem relacional para armazenar pesquisas, perguntas e respostas.

Estou procurando qual das duas abordagens abaixo parece melhor ou uma abordagem alternativa para qualquer uma delas.

Eu tenho pelo menos estas entidades:

  • questão
  • pesquisa
  • pessoa

E pelo menos esses relacionamentos:

  • Cada pesquisa tem 1 ou mais perguntas.
  • Cada pergunta pode ser usada em 0 ou mais pesquisas.
  • Cada pessoa pode participar de 0 ou mais pesquisas.

Aqui é onde encontro problemas: como modelar as respostas às perguntas da pesquisa feitas por uma pessoa.

Aqui estão duas abordagens que eu considerei, nenhuma das quais me parece muito boa. Os diagramas aqui são bastante simplificados para ilustrar o problema.

Abordagem 1: Abordagem 1

O que eu não gosto nessa abordagem:

  • A survey_person_question_responsetabela possui duas colunas diferentes que se referem a uma pesquisa: survey_question_survey_idesurvey_person_survey_id
    • Seria um erro ter survey_idreferências diferentes em uma linha para essas duas colunas. A survey_question deve ser da mesma pesquisa que a pessoa fez na survey_person. Não vejo uma boa maneira de fazer cumprir isso.
  • Parece que o que estou fazendo aqui é estabelecer um relacionamento entre dois relacionamentos. Isso parece errado para mim por algum motivo.

Abordagem 2:

Tente evitar dois FKs da abordagem 1 que devem se referir ao mesmo valor ... insira a descrição da imagem aqui

O que eu não gosto nessa abordagem:

  • Não há imposição de que os question_ide os survey_idFKs sejam de um survey_questionpar válido
  • Não há imposição de que os survey_ide os person_idFKs sejam de um survey_personpar válido

Qualquer conselho sobre:

  • Se uma dessas abordagens é uma abordagem típica
  • Os prós e contras de uma dessas abordagens em relação à outra
  • Uma maneira melhor de organizar esses dados inteiramente

Seria muito apreciado!

código morto
fonte

Respostas:

12

De acordo com meu entendimento de suas especificações, seu ambiente de negócios envolve um relacionamento ternário em nível conceitual . Nesse sentido, você precisa definir:

  1. o tipo de relacionamento (ou associação ) entre os tipos de entidade Pessoa e Pesquisa ;
  2. o tipo de relação entre pesquisa e pergunta ;
  3. o tipo de relacionamento que estabelece a conexão entre os dois tipos de relacionamento acima mencionados e, como conseqüência, entre Pessoa , Pesquisa e Pergunta , ou seja, Resposta (um nome mais curto que simplifica a interpretação, do meu ponto de vista).

Portanto, considero que você está no caminho certo com sua Abordagem 1 , embora exija alguns refinamentos pequenos (mas importantes) para torná-la mais precisa. Vou detalhar esses refinamentos e outras considerações relevantes nas seções a seguir.

Regras do negócio

Vamos expandir um pouco as regras de negócios aplicáveis ​​e reformulá-las da seguinte maneira:

  • Uma Pessoa registros em zero-um ou-muitas Surveys
  • Uma pesquisa obtém o registro de zero-uma-ou-muitas pessoas
  • Uma pesquisa é integrada por perguntas um-para-muitos
  • A questão integra-zero um ou-muitos Surveys
  • Uma pergunta recebe zero-uma-ou-muitas respostas
  • Uma resposta é fornecida por exatamente uma pessoa no contexto de exatamente uma pesquisa

Diagrama do IDEF1X expositivo

Em seguida, criei o IDEF1X, um diagrama que é apresentado na Figura 1 , que sintetiza as regras de negócios formuladas acima:

Fig.1 Pesquisa simplificada IDEF1X


a Definição de integração para Information Modeling ( IDEF1X ) é uma técnica de modelagem altamente recomendável que foi estabelecido como um padrão em Dezembro de 1993 pelo Instituto Estados Unidos Nacional de Padrões e Tecnologia ( NIST ). Baseia-se solidamente no trabalho teórico de autoria do único fundador do modelo relacional , o Dr. EF Codd, e também na visão de entidade-relacionamento desenvolvida pelo Dr. PP Chen .


O relacionamento PersonSurvey

A meu ver, é necessário que o relacionamento do PersonSurvey forneça um meio de autorização para que uma Pessoa possa participar de uma determinada Pesquisa . Dessa forma, uma vez que uma determinada pessoa tenha sido registrada em uma pesquisa específica , ela estará autorizada a fornecer respostas às perguntas que integram a respectiva pesquisa .

O relacionamento SurveyQuestion

Suponho que a propriedade (ou atributo) chamada suvery_question.question_number em seu diagrama seja usada para representar a ordem de apresentação de uma determinada instância de questão em relação a uma pesquisa específica . Como você pode ver, eu denotei essa propriedade como SurveyQuestion.PresentationOrder e acho que você deve impedir que (i) dois ou mais valores de Question.QuestionNumber compartilhem (ii) o mesmo valor PresentationOrder em (iii) a mesma ocorrência SurveyQuestion .

Para retratar essa necessidade, incluí uma ALTERNATE KEY (AK) na caixa que representa esse tipo de entidade, composta pela combinação de propriedades ( SurveyNumber, QuestionNumber, PresentationOrder ). Como você bem sabe, um AK composto pode ser declarado em um design DDL lógico com o auxílio de uma restrição UNIQUE de várias colunas (como exemplifiquei na SurveyQuestiontabela que faz parte do layout DDL expositivo exposto algumas seções abaixo).

O tipo de entidade Resposta

Sim, com o tipo de entidade Resposta , estou representando um relacionamento entre dois outros relacionamentos ; isso pode parecer estranho à primeira vista, mas não há nada de errado com essa abordagem, desde que (a) representa as características do contexto de negócios de interesse com precisão e (b) é representado adequadamente em um layout de nível lógico.

Sim, você está totalmente correto, seria um erro retratar essa parte do cenário no nível lógico de abstração por meio de dois Response.SurveyNumber(ou, digamos Response.SurveyId) valores referenciados de duas colunas diferentes na mesma Responselinha.

Layout SQL-DDL lógico derivado

-- 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)
);

Duas CHAVES ESTRANGEIRAS compostas na Responsetabela

Este é, provavelmente, o ponto mais importante a ser discutido: as referências feitas de uma determinada Responselinha para

  1. SurveyQuestion.SurveyNumbere
  2. SurveyPerson.SurveyNumber

deve ter valores correspondentes . No que me diz respeito, a melhor opção para impor essa condição de maneira declarativa é usar duas FOREIGN KEYs (FKs) compostas.

Conforme mostrado no design do DDL, o primeiro FK está fazendo uma referência à PersonSurveytabela PRIMARY KEY (PK), isto é (PersonId, SurveyNumber), e é formado pelas colunas Response.PersonIde Response.SurveyNumber.

O segundo FK está apontando para a SurveyQuestiontabela PK, ou seja,, (SurveyNumber, QuestionNumber)e é, portanto, composto pelas colunas Response.SurveyNumbere Response.QuestionNumber.

Dessa forma, a Response.SurveyNumbercoluna é bastante instrumental, pois é usada como parte de uma referência FK em duas restrições diferentes.

Com este método, assegura a gestão da base de dados sistema de garantia de integridade referencial de

  • (a) Responsepara PersonSurvey;
  • (b) Responsepara o SurveyQuestion; e
  • (c) cada uma das tabelas que representam um tipo de entidade associativa às tabelas que representam tipos de entidades independentes, a saber Person, Surveye Question.

Dados derivados para evitar anomalias de atualização

Notei no seu diagrama dois elementos que considero dignos de menção. Esses elementos estão relacionados a duas PersonSurveycolunas que podem (devem) ser derivadas .

Nesse sentido, você pode derivar o PersonSurvey.IsStarteddado consultando se uma determinada Personocorrência forneceu um ou mais desses elementos Responsespara Questionsintegrar exatamente Surveypor meio da SurveyQuestiontabela.

E você também pode obter o PersonSurvey.IsCompletedponto de dados determinando se uma determinada Personinstância forneceu a Responsea todos os Questionsque possuem um valor 'TRUE' na IsMandatorycoluna em uma SurveyQuestionlinha específica .

Por meio da derivação desses valores, você está impedindo algumas anomalias de atualização que eventualmente surgiriam caso você mantivesse esses valores na SurveyQuestioncoluna.

Consideração importante

Como o @Dave salienta corretamente em seu comentário, se você enfrentar um requisito futuro exigindo o gerenciamento de diferentes tipos de respostas que impliquem gerenciamento de datas, valores numéricos, múltipla escolha e outros possíveis aspectos, você terá que estender esse layout do banco de dados.

MDCCL
fonte
1
Uau, isso respondeu perfeitamente à pergunta na minha cabeça e depois me ensinou mais! Como os comentários devem sugerir melhorias: Era um pouco confuso que as chaves terminassem com ambos IDe Number, mas por outro lado isso é fantástico. Obrigado.
Zach Mierzejewski
@ Zach De nada, estou feliz que o post tenha ajudado. Obrigado pelo feedback, alguns refinamentos são decididamente necessários.
MDCCL
1

Essa é uma das razões pelas quais não gosto de prefixar colunas ao migrá-las como chaves estrangeiras. No seu primeiro caso, a ferramenta de modelagem pode forçar você a prefixar uma das survey_idcolunas na survey_person_question_responsetabela. Você poderá ajustar isso depois que o relacionamento for estabelecido.

Se necessário, remova o campo de ID de pesquisa redundante ao criar o modelo físico no qual você não precisa da coluna duplicada. Como você identificou, os dois modelos têm problemas, mas acredito que o primeiro modelo seja melhor no geral.

BillThor
fonte
Obrigado pela compreensão - reduzi para 1 coluna no modelo físico que aplica tudo o que eu queria.
deadcode