Modo corretto per memorizzare un valore che potrebbe essere di diversi tipi


11

Ho una tabella Risposte e una tabella Domande .

La tabella di risposte ha un valore, ma a seconda della domanda, questo valore potrebbe essere una bit, nvarcharo number(finora). La domanda ha un'idea di quale dovrebbe essere il tipo di valore di risposta previsto.

Sarà importante analizzare questi valori di risposta in un punto o nell'altro poiché i numeri, almeno, dovranno essere confrontati.

Per un po 'più di contesto, le domande e le potenziali risposte (in genere un tipo di dati consentito per un input di tipo casella di testo) vengono fornite da alcuni utenti in un sondaggio di sorta. Le risposte vengono quindi fornite da altri utenti specificati.

Un paio di opzioni che ho considerato sono:

A. XML o stringa che viene analizzata in modo diverso a seconda del tipo previsto (di cui si tiene traccia nella domanda)

B. Tre tabelle separate che fanno riferimento (o fanno riferimento a) la tabella delle risposte e vengono unite in base al tipo previsto. In questo caso, non sono sicuro del modo migliore per impostare i vincoli per garantire che ogni domanda abbia una sola risposta, o se ciò dovrebbe essere lasciato all'applicazione.

C. Tre colonne separate nella tabella delle risposte che possono essere recuperate in base al tipo previsto.

Sarei felice solo di avere un contributo sui pro e contro di questi approcci o approcci alternativi che non avevo considerato.

Risposte:


2

Dipende davvero da come il tuo front-end accede ai dati.

Se si utilizza un mapper O / R, concentrarsi sulla progettazione orientata agli oggetti delle classi, non sulla progettazione del database. Il database rispecchia quindi solo il design della classe. La progettazione esatta del db dipende dal mappatore O / R e dal modello di mappatura dell'ereditarietà che si sta utilizzando.

Se si accede direttamente alle tabelle tramite set di record, tabelle di dati, lettori di dati o simili, una cosa semplice da fare è convertire i valori in una stringa utilizzando una cultura invariante e archiviarla in una semplice colonna di testo . E, naturalmente, usa di nuovo la stessa cultura per riconvertire il testo in tipi di valore specializzati durante la lettura dei valori.

In alternativa è possibile utilizzare una colonna per tipo di valore. Oggi abbiamo unità terabyte!

Una colonna XML è possibile, ma probabilmente aggiunge più complessità rispetto alla semplice colonna di testo e fa praticamente la stessa cosa, vale a dire serializzare / deserializzare.

Le tabelle unite separate sono il modo normalizzato corretto di fare le cose; tuttavia, aggiungono anche un po 'di complessità.

Mantienilo semplice.

Vedi anche la mia risposta alla progettazione del database dei questionari: in che modo è meglio? .


4

Sulla base di ciò che hai detto, utilizzerei il seguente schema generale:

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

Non ti interessa davvero se la risposta è un numero, una data, una parola, ecc. Perché i dati sono una risposta a una domanda e non qualcosa su cui devi operare direttamente. Inoltre, i dati hanno significato solo nel contesto della domanda. In quanto tale, nvarchar è il meccanismo più versatile e leggibile per l'archiviazione dei dati.

La domanda e le potenziali risposte verrebbero raccolte dal primo utente e inserite nelle tabelle PollQuestion e PollOption. Il secondo utente che risponde alle domande selezionerebbe da un elenco di risposte (vero / falso = elenco di 2). Puoi anche espandere la tabella PollQuestion per includere l'id utente del creatore, se necessario, al fine di tenere traccia delle domande che creano.

Nella tua UI la risposta selezionata dall'utente può essere legata al valore PollOptionId. Insieme a PollQuestionId è possibile verificare rapidamente che la risposta sia valida per la domanda. La loro risposta, se valida, verrà inserita nella tabella PollResponse.

Ci sono un paio di potenziali problemi a seconda dei dettagli del tuo caso d'uso. Se il primo utente desidera utilizzare una domanda di matematica e non desideri offrire più risposte possibili. Un'altra situazione è se le opzioni fornite dall'utente iniziale non sono le uniche opzioni che il secondo utente può scegliere. È possibile rielaborare questo schema come segue per supportare questi casi d'uso aggiuntivi.

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

Vorrei anche aggiungere un vincolo di controllo per assicurarsi che sia fornita un'opzione o una risposta alternativa, ma non entrambe (opzione e risposta alternativa), a seconda delle esigenze.

Modifica: tipo di dati di comunicazione per AlternateResponse.

In un mondo perfetto potremmo usare il concetto di generica per gestire vari tipi di dati per AlternateReponse. Purtroppo non viviamo in un mondo perfetto. Il miglior compromesso a cui riesco a pensare è quello di specificare quale tipo di dati AlternateResponse dovrebbe essere nella tabella PollQuestion e archiviare AlternateReponse nel database come nvarchar. Di seguito è riportato lo schema delle domande aggiornato e la nuova tabella del tipo di dati:

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
)

Puoi elencare tutti i tipi di dati disponibili per i creatori di domande selezionando da questa tabella QuestionDataType. L'interfaccia utente può fare riferimento a QuestionDataTypeId per selezionare il formato corretto per il campo di risposta alternativo. Non sei limitato ai tipi di dati TSQL, quindi "Numero di telefono" può essere un tipo di dati e otterrai una formattazione / mascheramento appropriati sull'interfaccia utente. Inoltre, se necessario, puoi trasmettere i tuoi dati ai tipi appropriati tramite una semplice dichiarazione del caso al fine di eseguire qualsiasi tipo di elaborazione (selezione, convalida, ecc.) Sulle risposte alternative.


0

Dai un'occhiata a Cosa c'è di così brutto in EAV, comunque? di Aaron Bertrand per alcune informazioni sul modello EAV.

Sarà probabilmente meglio in più modi avere una colonna per ciascun tipo di dati invece di avere XML o più tabelle.

La parte del vincolo è semplice:

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
)

Ci sono molte domande e risposte esistenti su questo sito taggate , e probabilmente altre in cui il richiedente non sapeva usare quel termine nella loro domanda.

Consiglio vivamente di leggere quelli, poiché probabilmente copriranno tutti i pro e i contro (questo impedisce alle persone di rielaborarli qui, quando in realtà non sono cambiati).

Risposta basata sui commenti alle domande lasciati da Aaron Bertrand


-1

Penso che al problema sia stato dato troppo pensiero o ci sono alcuni vincoli aggiuntivi nel perché alcune risposte potrebbero essere più accessibili di altre. Attualmente non sembrano esserci prove del fatto che la Risposta debba essere elaborata in alcun modo dal DB ma solo come campo di registro.

Vorrei andare con un NVARCHAR (MAX) e poi lasciare che il frontend si occupasse di archiviare / recuperare il contenuto. Forse un campo bit IS_CORRECT in cui il frontend potrebbe memorizzare se la risposta è corretta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.