Moyen approprié de stocker une valeur pouvant être de plusieurs types différents

11

J'ai un tableau de réponses et un tableau de questions .

Le tableau de réponses a une valeur, mais en fonction de la question, cette valeur pourrait être bit, nvarcharou number(jusqu'à présent). La question a une idée de ce que devrait être son type de valeur de réponse.

Il sera important d'analyser ces valeurs de réponse à un moment ou à un autre car les nombres, au moins, devront être comparés.

Pour un peu plus de contexte, les questions et les réponses potentielles (généralement un type de données autorisé pour une entrée de type zone de texte) sont fournies par certains utilisateurs dans une enquête de toutes sortes. Les réponses sont ensuite fournies par d'autres utilisateurs spécifiés.

J'ai envisagé quelques options:

A. XML ou chaîne qui est analysée différemment selon le type souhaité (qui est conservé dans la question)

B. Trois tables distinctes qui référencent (ou sont référencées par) la table Answer et sont jointes en fonction du type prévu. Dans ce cas, je ne suis pas sûr de la meilleure façon de configurer les contraintes pour garantir que chaque question n'a qu'une seule réponse, ou si cela doit être laissé à l'application.

C. Trois colonnes distinctes sur la table de réponses qui peuvent être récupérées en fonction du type souhaité.

Je serais heureux de simplement obtenir des informations sur les avantages et les inconvénients de ces approches, ou d'autres approches que je n'avais pas envisagées.

David Garrison
la source

Réponses:

2

Cela dépend vraiment de la façon dont votre frontal accède aux données.

Si vous utilisez un mappeur O / R, concentrez-vous sur la conception orientée objet de vos classes, pas sur la conception de la base de données. La base de données reflète alors simplement la conception de la classe. La conception exacte de la base de données dépend du mappeur O / R et du modèle de mappage d'héritage que vous utilisez.

Si vous accédez directement aux tables via des jeux d'enregistrements, des tables de données, des lecteurs de données ou similaires, une chose simple à faire est de convertir les valeurs en chaîne en utilisant une culture invariante et de les stocker dans une simple colonne de texte . Et, bien sûr, utilisez à nouveau la même culture afin de reconvertir le texte en types de valeurs spécialisés lors de la lecture des valeurs.

Vous pouvez également utiliser une colonne par type de valeur. Nous avons des disques de téraoctets aujourd'hui!

Une colonne XML est possible, mais ajoute probablement plus de complexité par rapport à la simple colonne de texte et fait à peu près la même chose, à savoir la sérialisation / désérialisation.

Les tables jointes séparées sont la bonne façon normalisée de faire les choses; cependant, ils ajoutent également une certaine complexité.

Rester simple.

Voir aussi ma réponse à la conception de la base de données du questionnaire - quelle est la meilleure solution? .

Olivier Jacot-Descombes
la source
4

Sur la base de ce que vous avez dit, j'utiliserais le schéma général suivant:

CREATE TABLE [dbo].[PollQuestion]
(
    [PollQuestionId] INT NOT NULL PRIMARY KEY IDENTITY,
    [QuestionText] NVARCHAR(150) NOT NULL, -- Some reasonable character limit
    [Created] DATETIME2(2) NOT NULL DEFAULT SYSUTCDATETIME(),
    [Archived] DATETIME2(2) NULL,  -- Remove this if you don't need to hide questions
)
CREATE TABLE [dbo].[PollOption]
(
    [PollOptionId] INT NOT NULL PRIMARY KEY IDENTITY,
    [PollQuestionId] INT NOT NULL,  -- Link to the question here because options aren't shared across questions
    [OptionText] NVARCHAR(50) NOT NULL, -- Some reasonable character limit
    [Created] DATETIME2(2) NOT NULL DEFAULT SYSUTCDATETIME(),
    [Archived] DATETIME2(2) NULL  -- Remove this if you don't need to hide options

    CONSTRAINT [FK_PollOption_PollQuestionId_to_PollQuestion_PollQuestionId] FOREIGN KEY ([PollQuestionId]) REFERENCES [dbo].[PollQuestion]([PollQuestionId])
)
CREATE TABLE [dbo].[PollResponse]
(
    [PollResponseId] INT NOT NULL PRIMARY KEY IDENTITY,
    [PollOptionId] INT NOT NULL,
    [UserId] INT NOT NULL,
    [Created] DATETIME2(2) NOT NULL DEFAULT SYSUTCDATETIME(),
    [Archived] DATETIME2(2) NULL,  -- Remove this if you don't need to hide answers

    CONSTRAINT [FK_PollResponse_PollOptionId_to_PollOption_PollOptionId] FOREIGN KEY ([PollOptionId]) REFERENCES [dbo].[PollOption]([PollOptionId]),
    CONSTRAINT [FK_PollResponse_UserId_to_User_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User]([UserId])
)

Vous ne vous souciez pas vraiment si la réponse est un nombre, une date, un mot, etc. parce que les données sont une réponse à une question et non quelque chose que vous devez utiliser directement. De plus, les données n'ont de sens que dans le contexte de la question. En tant que tel, un nvarchar est le mécanisme lisible par l'homme le plus polyvalent pour stocker les données.

La question et les réponses potentielles seraient recueillies auprès du premier utilisateur et insérées dans les tables PollQuestion et PollOption. Le deuxième utilisateur qui répond aux questions sélectionnerait dans une liste de réponses (vrai / faux = liste de 2). Vous pouvez également développer la table PollQuestion pour inclure l'ID utilisateur du créateur, le cas échéant, afin de suivre les questions qu'ils créent.

Sur votre interface utilisateur, la réponse que l'utilisateur sélectionne peut être liée à la valeur PollOptionId. Avec PollQuestionId, vous pouvez vérifier que la réponse est valide pour la question rapidement. Leur réponse, si elle était valide, serait entrée dans le tableau PollResponse.

Il existe quelques problèmes potentiels en fonction des détails de votre cas d'utilisation. Si le premier utilisateur souhaite utiliser une question mathématique et que vous ne souhaitez pas proposer plusieurs réponses possibles. Une autre situation est si les options fournies par l'utilisateur initial ne sont pas les seules options que le deuxième utilisateur peut choisir. Vous pouvez retravailler ce schéma comme suit pour prendre en charge ces cas d'utilisation supplémentaires.

CREATE TABLE [dbo].[PollResponse]
(
    [PollResponseId] INT NOT NULL PRIMARY KEY IDENTITY,
    [PollOptionId] INT NULL,
    [PollQuestionId] INT NOT NULL,
    [UserId] INT NOT NULL,
    [AlternateResponse] NVARCHAR(50) NULL, -- Some reasonable character limit
    [Created] DATETIME2(2) NOT NULL DEFAULT SYSUTCDATETIME(),
    [Archived] DATETIME2(2) NULL,  -- Remove this if you don't need to hide answers

    CONSTRAINT [FK_PollResponse_PollOptionId_to_PollOption_PollOptionId] FOREIGN KEY ([PollOptionId]) REFERENCES [dbo].[PollOption]([PollOptionId]),
    CONSTRAINT [FK_PollResponse_UserId_to_User_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User]([UserId])
)

J'ajouterais probablement une contrainte de vérification pour m'assurer qu'une option est fournie ou une réponse alternative, mais pas les deux (option et réponse alternative), selon vos besoins.

Edit: communication du type de données pour AlternateResponse.

Dans un monde parfait, nous pourrions utiliser le concept des génériques pour gérer différents types de données pour la réponse alternative. Hélas, nous ne vivons pas dans un monde parfait. Le meilleur compromis auquel je peux penser est de spécifier quel type de données AlternateResponse doit être dans la table PollQuestion et de stocker AlternateReponse dans la base de données en tant que nvarchar. Vous trouverez ci-dessous le schéma de question mis à jour et la nouvelle table de types de données:

CREATE TABLE [dbo].[PollQuestion]
(
    [PollQuestionId] INT NOT NULL PRIMARY KEY IDENTITY,
    [QuestionText] NVARCHAR(150) NOT NULL, -- Some reasonable character limit
    [QuestionDataTypeId] INT NOT NULL,
    [Created] DATETIME2(2) NOT NULL DEFAULT SYSUTCDATETIME(),
    [Archived] DATETIME2(2) NULL,  -- Remove this if you don't need to hide questions
    -- Insert FK here for QuestionDataTypeId
)
CREATE TABLE [dbo].[QuestionDataType]
(
    [QuestionDataTypeId] INT NOT NULL PRIMARY KEY IDENTITY,
    [Description] NVARCHAR(50) NOT NULL, -- Some reasonable character limit
)

Vous pouvez répertorier tous les types de données disponibles pour les créateurs de questions en sélectionnant dans ce tableau QuestionDataType. Votre interface utilisateur peut référencer le QuestionDataTypeId pour sélectionner le format approprié pour le champ de réponse alternatif. Vous n'êtes pas limité aux types de données TSQL, donc "Numéro de téléphone" peut être un type de données et vous obtiendrez un formatage / masquage approprié sur l'interface utilisateur. De plus, si nécessaire, vous pouvez convertir vos données dans les types appropriés via une simple déclaration de cas afin d'effectuer tout type de traitement (sélection, validation, etc.) sur les réponses alternatives.

Erik
la source
0

Jetez un coup d'œil à ce qui est si mauvais à propos de l'EAV, de toute façon? par Aaron Bertrand pour quelques informations sur le modèle EAV.

Il sera probablement préférable de plusieurs façons d'avoir une colonne pour chaque type de données au lieu d'avoir XML ou plusieurs tables.

La partie contrainte est simple:

CHECK 
(
    CASE WHEN col1 IS NOT NULL THEN 1 ELSE 0 END + 
    CASE WHEN col2 IS NOT NULL THEN 1 ELSE 0 END + 
    CASE WHEN col3 IS NOT NULL THEN 1 ELSE 0 END = 1
)

Il existe de nombreuses questions et réponses sur ce site tagué , et probablement d'autres où le demandeur ne savait pas utiliser ce terme dans sa question.

Je recommande fortement de les lire, car ils couvriront probablement tous les avantages et les inconvénients (cela empêche les gens de les ré-hacher ici, alors qu'en réalité ils n'ont pas changé).

Réponse basée sur les commentaires de questions laissés par Aaron Bertrand


la source
-1

Je pense que le problème est trop réfléchi ou qu'il y a des contraintes supplémentaires pour expliquer pourquoi certaines réponses pourraient être plus adaptées que d'autres. Actuellement, il ne semble pas y avoir de preuve que la réponse devrait être traitée de quelque manière que ce soit par la base de données, mais simplement comme un champ de journal.

J'irais avec un NVARCHAR (MAX), puis je laisserais le frontend gérer le stockage / la récupération du contenu. Peut-être un champ de bits IS_CORRECT où le frontend pourrait stocker si la réponse est correcte.

HansLindgren
la source