Richtige Methode zum Speichern eines Werts, der mehrere verschiedene Typen haben kann

11

Ich habe eine Antworttabelle und eine Fragentabelle .

Die Antworten Tabelle hat einen Wert, aber abhängig von der Frage, könnte dieser Wert sein , bit, nvarchar, oder number(bisher). Die Frage hat eine Vorstellung davon, wie der beabsichtigte Antwortwerttyp aussehen soll.

Es ist wichtig, diese Antwortwerte an der einen oder anderen Stelle zu analysieren, da zumindest die Zahlen verglichen werden müssen.

Für ein wenig mehr Kontext werden die Fragen und möglichen Antworten (normalerweise ein Datentyp, der für die Eingabe eines Textfeldtyps zulässig ist) von einigen Benutzern in einer Art Umfrage bereitgestellt. Die Antworten werden dann von anderen angegebenen Benutzern bereitgestellt.

Einige Optionen, die ich in Betracht gezogen habe, sind:

A. XML oder Zeichenfolge, die je nach beabsichtigtem Typ unterschiedlich analysiert wird (was in der Frage nachverfolgt wird)

B. Drei separate Tabellen, die auf die Antworttabelle verweisen (oder von diesen referenziert werden) und mit denen basierend auf dem beabsichtigten Typ verbunden werden. In diesem Fall bin ich mir nicht sicher, wie ich die Einschränkungen am besten einrichten kann, um sicherzustellen, dass jede Frage nur eine Antwort hat, oder ob dies der Anwendung überlassen bleiben sollte.

C. Drei separate Spalten in der Antworttabelle, die basierend auf dem beabsichtigten Typ abgerufen werden können.

Ich würde mich freuen, nur einen Beitrag zu den Vor- und Nachteilen dieser Ansätze oder zu alternativen Ansätzen zu erhalten, die ich nicht in Betracht gezogen hatte.

David Garrison
quelle

Antworten:

2

Es hängt wirklich davon ab, wie Ihr Front-End auf die Daten zugreift.

Wenn Sie einen O / R-Mapper verwenden, konzentrieren Sie sich auf das objektorientierte Design Ihrer Klassen und nicht auf das Datenbankdesign. Die Datenbank spiegelt dann nur das Klassendesign wider. Das genaue DB-Design hängt vom verwendeten O / R-Mapper- und Vererbungs-Mapping-Modell ab.

Wenn Sie direkt über Datensatzgruppen, Datentabellen, Datenleser oder dergleichen auf die Tabellen zugreifen, können Sie die Werte einfach mithilfe einer invarianten Kultur in eine Zeichenfolge konvertieren und in einer einfachen Textspalte speichern . Verwenden Sie natürlich dieselbe Kultur erneut, um den Text beim Lesen der Werte wieder in die speziellen Werttypen zu konvertieren.

Alternativ können Sie eine Spalte pro Werttyp verwenden. Wir haben heute Terabyte-Laufwerke!

Eine XML-Spalte ist möglich, erhöht jedoch wahrscheinlich die Komplexität im Vergleich zur einfachen Textspalte und macht fast dasselbe, nämlich das Serialisieren / Deserialisieren.

Getrennte verknüpfte Tabellen sind die richtige normalisierte Vorgehensweise. Sie erhöhen jedoch auch die Komplexität.

Halte es einfach.

Siehe auch meine Antwort auf das Design der Fragebogendatenbank - welcher Weg ist besser? .

Olivier Jacot-Descombes
quelle
4

Basierend auf dem, was Sie gesagt haben, würde ich das folgende allgemeine Schema verwenden:

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

Es ist Ihnen eigentlich egal, ob die Antwort eine Zahl, ein Datum, ein Wort usw. ist, da die Daten eine Antwort auf eine Frage sind, die Sie nicht direkt bearbeiten müssen. Darüber hinaus haben die Daten nur im Zusammenhang mit der Frage eine Bedeutung. Als solches ist nvarchar der vielseitigste vom Menschen lesbare Mechanismus zum Speichern der Daten.

Die Frage und die möglichen Antworten werden vom ersten Benutzer gesammelt und in die Tabellen PollQuestion und PollOption eingefügt. Der zweite Benutzer, der die Fragen beantwortet, würde aus einer Liste von Antworten auswählen (wahr / falsch = Liste von 2). Sie können die PollQuestion-Tabelle auch erweitern, um gegebenenfalls die Benutzer-ID des Erstellers einzuschließen, um die von ihnen erstellten Fragen zu verfolgen.

Auf Ihrer Benutzeroberfläche kann die vom Benutzer ausgewählte Antwort an den PollOptionId-Wert gebunden werden. Zusammen mit der PollQuestionId können Sie schnell überprüfen, ob die Antwort für die Frage gültig ist. Ihre Antwort, falls gültig, wird in die PollResponse-Tabelle eingetragen.

Abhängig von den Details Ihres Anwendungsfalls gibt es einige potenzielle Probleme. Wenn der erste Benutzer eine mathematische Frage verwenden möchte und Sie nicht mehrere mögliche Antworten anbieten möchten. Eine andere Situation ist, wenn die vom Erstbenutzer bereitgestellten Optionen nicht die einzigen Optionen sind, die der zweite Benutzer auswählen kann. Sie können dieses Schema wie folgt überarbeiten, um diese zusätzlichen Anwendungsfälle zu unterstützen.

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

Ich würde wahrscheinlich auch eine Prüfbeschränkung hinzufügen, um sicherzustellen, dass entweder eine Option oder eine alternative Antwort bereitgestellt wird, aber nicht beide (Option und alternative Antwort), abhängig von Ihren Anforderungen.

Bearbeiten: Datentyp für AlternateResponse kommunizieren.

In einer perfekten Welt könnten wir das Konzept der Generika verwenden , um verschiedene Datentypen für die AlternateReponse zu verarbeiten. Leider leben wir nicht in einer perfekten Welt. Der beste Kompromiss, den ich mir vorstellen kann, besteht darin, anzugeben, wie der AlternateResponse-Datentyp in der PollQuestion-Tabelle aussehen soll, und die AlternateReponse als nvarchar in der Datenbank zu speichern. Unten finden Sie das aktualisierte Fragenschema und die neue Datentypentabelle:

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
)

Sie können alle verfügbaren Datentypen für Fragenersteller auflisten, indem Sie aus dieser QuestionDataType-Tabelle auswählen. Ihre Benutzeroberfläche kann auf die QuestionDataTypeId verweisen, um das richtige Format für das alternative Antwortfeld auszuwählen. Sie sind nicht auf TSQL-Datentypen beschränkt, daher kann "Telefonnummer" ein Datentyp sein und Sie erhalten eine entsprechende Formatierung / Maskierung auf der Benutzeroberfläche. Bei Bedarf können Sie Ihre Daten auch über eine einfache case-Anweisung in die entsprechenden Typen umwandeln, um die alternativen Antworten in beliebiger Weise zu verarbeiten (auswählen, validieren usw.).

Erik
quelle
0

Werfen Sie einen Blick auf Was ist überhaupt so schlecht an EAV? von Aaron Bertrand für einige Informationen zum EAV-Modell.

Es ist wahrscheinlich in mehrfacher Hinsicht besser, eine Spalte für jeden Datentyp zu haben, als XML oder mehrere Tabellen.

Der Einschränkungsteil ist einfach:

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
)

Auf dieser Site gibt es viele Fragen und Antworten mit dem Tag und wahrscheinlich auch andere, bei denen der Fragesteller nicht wusste, dass er diesen Begriff in seiner Frage verwenden sollte.

Ich empfehle dringend, diese durchzulesen, da sie wahrscheinlich alle Vor- und Nachteile abdecken (dies verhindert, dass die Leute sie hier erneut durchsuchen, wenn sie sich in Wirklichkeit nicht geändert haben).

Antwort basierend auf Fragenkommentaren von Aaron Bertrand


quelle
-1

Ich denke, das Problem wird zu viel überlegt, oder es gibt einige zusätzliche Einschränkungen, warum bestimmte Antworten besser geeignet sind als andere. Derzeit scheint es keine Beweise dafür zu geben, dass die Antwort in irgendeiner Weise von der Datenbank verarbeitet werden müsste, sondern nur als Protokollfeld.

Ich würde mit einem NVARCHAR (MAX) gehen und dann einfach das Frontend mit dem Speichern / Abrufen des Inhalts befassen. Möglicherweise ein IS_CORRECT-Bitfeld, in dem das Frontend speichern kann, wenn die Antwort korrekt ist.

HansLindgren
quelle