Warum sollten Sie eine Aufzählung in DB speichern?

69

Ich habe eine Reihe von Fragen wie diese gesehen , in denen ich um Rat gefragt wurde, wie man Enums in DB speichert. Aber ich frage mich, warum Sie das tun würden. Nehmen wir also an, ich habe eine Entität Personmit einem genderFeld und einer GenderAufzählung. Dann hat meine Personentabelle eine Spalte Geschlecht.

Abgesehen von dem offensichtlichen Grund für die Durchsetzung der Korrektheit sehe ich nicht, warum ich eine zusätzliche Tabelle erstellen würde, um genderdas abzubilden, was ich bereits in meiner Anwendung habe. Und ich mag diese Vervielfältigung nicht wirklich.

user3748908
quelle
1
Wo würden Sie sonst Daten speichern, die sich regelmäßig ändern könnten? Möglicherweise haben Sie sich alle Optionen überlegt, was passiert, wenn jemand mitkommt und eine neue Option hinzufügen möchte. Sind Sie bereit, diese hartkodierte Liste zu optimieren? Jemand möchte möglicherweise sein Geschlecht als etwas anderes als männlich oder weiblich angeben, z. B. intersexuell.
JB King
4
@JBKing ... schau einfach auf Facebooks Gender-Liste.
3
Wenn Ihre Kunden "verblendete Tumblrites" sind, dann erstellen Sie verdammt gut ein Datenbankschema, mit dem Sie etwas erstellen können, das ihren Bedürfnissen entspricht, zumindest wenn Sie beabsichtigen, im Geschäft zu bleiben.
Gort the Robot

Antworten:

74

Nehmen wir ein weiteres Beispiel, das weniger mit Vorstellungen und Erwartungen behaftet ist. Ich habe hier eine Aufzählung, und das sind die Prioritäten für einen Fehler.

Welchen Wert speichern Sie in der Datenbank?

Also, ich könnte zu speichern 'C', 'H', 'M', und 'L'in der Datenbank. Oder 'HIGH'so weiter. Dies hat das Problem der Eingabe von Zeichenfolgen . Es gibt einen bekannten Satz von gültigen Werten, und wenn Sie nicht diesen Satz in der Datenbank zu speichern, kann es schwierig sein , mit zu arbeiten.

Warum speichern Sie die Daten im Code?

Sie haben List<String> priorities = {'CRITICAL', 'HIGH', 'MEDIUM', 'LOW'};oder etwas in diesem Sinne im Code. Dies bedeutet, dass Sie verschiedene Zuordnungen dieser Daten zum richtigen Format haben (Sie fügen alle Großbuchstaben in die Datenbank ein, aber Sie zeigen sie als an Critical). Ihr Code ist jetzt auch schwer zu lokalisieren. Sie haben die Datenbankdarstellung der Idee an eine im Code gespeicherte Zeichenfolge gebunden.

Überall dort, wo Sie auf diese Liste zugreifen müssen, müssen Sie entweder über Codeduplizierung oder eine Klasse mit einer Reihe von Konstanten verfügen. Beides sind keine guten Optionen. Man sollte auch nicht vergessen, dass es andere Anwendungen gibt, die diese Daten verwenden (die möglicherweise in anderen Sprachen geschrieben sind - die Java-Webanwendung verwendet ein Crystal Reports- Berichtssystem und einen Perl- Batch-Job, der Daten einspeist). Das Berichtsmodul muss die gültige Liste der Daten kennen (was passiert, wenn 'LOW'keine Priorität markiert ist und Sie wissen müssen, dass dies eine gültige Priorität für den Bericht ist?), Und der Batch-Job muss die Informationen darüber enthalten, welche Daten gültig sind Werte sind.

Hypothetisch könnte man sagen "Wir sind ein einsprachiger Shop - alles ist in Java geschrieben" und haben eine einzige .jar-Datei, die diese Informationen enthält - aber jetzt bedeutet dies, dass Ihre Anwendungen eng miteinander verbunden sind und diese .jar-Datei enthält die Daten. Sie müssen den Berichterstellungsteil und den Stapelaktualisierungsteil zusammen mit der Webanwendung bei jeder Änderung freigeben - und hoffen , dass diese Freigabe für alle Teile reibungslos verläuft.

Was passiert, wenn Ihr Chef eine andere Priorität haben möchte?

Ihr Chef ist heute vorbei gekommen. Es gibt eine neue Priorität - CEO. Jetzt müssen Sie den gesamten Code ändern , eine Neukompilierung durchführen und erneut implementieren.

Mit der Methode "Aufzählung in der Tabelle" aktualisieren Sie die Aufzählungsliste, um eine neue Priorität zu erhalten. Der gesamte Code, der die Liste abruft, ruft sie aus der Datenbank ab.

Daten stehen selten allein

Bei Prioritäten werden die Daten in andere Tabellen übernommen, die möglicherweise Informationen zu Workflows enthalten, oder wer kann diese Priorität festlegen oder so weiter.

Kehren Sie kurz zum Geschlecht zurück, wie in der Frage erwähnt: Geschlecht hat einen Link zu den verwendeten Pronomen: he/his/himund she/hers/her... und Sie möchten vermeiden, dass dies hart in den Code selbst codiert wird. Und dann kommt dein Chef vorbei und du musst hinzufügen, dass du das 'OTHER'Geschlecht hast (um es einfach zu halten) und dass du dieses Geschlecht in Beziehung setzen musst zu they/their/them... und dein Chef sieht, was Facebook hat und ... na ja.

Indem Sie sich statt auf eine Aufzählungstabelle auf ein Zeichenfolgenbit beschränken, müssen Sie diese Zeichenfolge jetzt in einer Reihe anderer Tabellen replizieren, um diese Beziehung zwischen den Daten und ihren anderen Bits aufrechtzuerhalten.

Was ist mit anderen Datenspeichern?

Egal, wo Sie dies speichern, das gleiche Prinzip besteht.

  • Sie könnten eine Datei haben priorities.prop, die die Prioritätenliste enthält. Sie lesen diese Liste aus einer Eigenschaftendatei ein.
  • Sie könnten eine Dokumentenspeicher-Datenbank (wie CouchDB ) haben, die einen Eintrag für hat enums(und dann eine Validierungsfunktion in JavaScript schreiben ):

    {
       "_id": "c18b0756c3c08d8fceb5bcddd60006f4",
       "_rev": "1-c89f76e36b740e9b899a4bffab44e1c2",
       "priorities": [ "critical", "high", "medium", "low" ],
       "severities": [ "blocker", "bad", "annoying", "cosmetic" ]
    }
    
  • Sie könnten eine XML-Datei mit einem gewissen Schema haben:

    <xs:element name="priority" type="priorityType"/>
    
    <xs:simpleType name="priorityType">
      <xs:restriction base="xs:string">
        <xs:enumeration value="critical"/>
        <xs:enumeration value="high"/>
        <xs:enumeration value="medium"/>
        <xs:enumeration value="low"/>
      </xs:restriction>
    </xs:simpleType>
    

Die Kernidee ist die gleiche. Im Datenspeicher selbst muss die Liste der gültigen Werte gespeichert und erzwungen werden. Indem Sie es hier platzieren, ist es einfacher, über den Code und die Daten nachzudenken. Sie müssen nicht jedes Mal defensiv überprüfen, was Sie haben (Groß- oder Kleinschreibung? Warum gibt es einen chriticalTyp in dieser Spalte? Usw.), weil Sie wissen, was Sie vom Datenspeicher zurückerhalten Genau das, was der Datenspeicher von Ihnen erwartet - und Sie können den Datenspeicher nach einer Liste gültiger Werte abfragen.

Das wegnehmen

Der Satz gültiger Werte ist Daten , kein Code. Sie tun müssen , streben DRY Code - aber die Frage der Vervielfältigung ist , dass Sie die duplizieren Daten in dem Code, anstatt seinen Platz als Daten zu respektieren und sie in einer Datenbank zu speichern.

Es erleichtert mehrere Anwendungen gegen den Datenspeicher zu schreiben und vermeidet Instanzen mit dem Sie alles benötigen bereitstellen , die eng an die Daten gekoppelt ist , sich - weil Sie nicht haben , um Ihren Code zu den Daten gekoppelt.

Dies erleichtert das Testen von Anwendungen, da Sie nicht die gesamte Anwendung erneut testen müssen, wenn die CEOPriorität hinzugefügt wird - da Sie keinen Code haben, der sich um den tatsächlichen Wert der Priorität kümmert.

Durch die Möglichkeit, unabhängig voneinander über den Code und die Daten nachzudenken, ist es einfacher, Fehler bei der Wartung zu finden und zu beheben.

Peter Mortensen
quelle
6
Wenn Sie Ihrem Code einen Aufzählungswert hinzufügen können, ohne eine Logik ändern zu müssen (und dies nicht die lokalisierte Anzeige davon sein zu müssen), bezweifle ich zunächst die Notwendigkeit des zusätzlichen Aufzählungswerts. Und obwohl ich alt genug bin, um die Fähigkeit zu schätzen, Datenbanksicherungen mit einfachen SQL-Abfragen einfach abzufragen, um ein Problem zu analysieren, können Sie mit ORMs heutzutage sehr gut vorgehen, ohne sich die zugrunde liegende Datenbank überhaupt ansehen zu müssen. Ich verstehe den Punkt über Lokalisierung (Pronomen) hier allerdings nicht - das Zeug sollte auf keinen Fall in einer Datenbank sein, aber Ressourcendateien, wie ich sagen würde.
Voo
1
@Voo das Pronomen ist ein Beispiel für andere Daten, die sich auf diesen enumesken Wert beziehen. Ohne die Daten in einer Tabelle müssten die stringtypisierten Werte ohne geeignete FK-Einschränkungen vorhanden sein. Wenn eine Ressourcendatei Pronomen (wie diese) enthält, besteht eine Kopplung zwischen der Datenbank und der Datei (aktualisieren Sie die Datenbank und stellen Sie die Datei erneut bereit). Berücksichtigen Sie die Enums von Redmine, die über die Administrationsoberfläche im laufenden Betrieb geändert werden können, ohne dass ein erneutes Deploy durchgeführt werden muss.
1
... denken Sie auch daran, dass Datenbanken ein polygloter Datenspeicher sind. Wenn Sie die Validierung als Teil des ORM in einer Sprache benötigen, haben Sie es erforderlich gemacht, diese Validierung in einer anderen von Ihnen verwendeten Sprache zu duplizieren (ich habe kürzlich mit einem Java-Front-End gearbeitet, bei dem Python Daten in die Datenbank pusht - Das Java ORM- und das Python-System müssen sich einig sein - und diese Vereinbarung (die gültigen Typen) konnte am einfachsten implementiert werden, indem die Datenbank sie mit einer 'enum'-Tabelle erzwang.)
2
@Voo die Redmine Verwendung von Enum ist die gleiche wie Bugzilla "Die wichtigste Tabelle enthält alle Fehler des Systems. Sie besteht aus verschiedenen Fehlereigenschaften einschließlich aller Aufzählungswerte wie Schweregrad und Priorität." - Es handelt sich nicht um ein Freiform-Textfeld, sondern um einen Wert aus dieser bekannten und aufzählbaren Menge. Es ist keine Kompilierungszeitaufzählung , aber es ist immer noch aufzählung. Siehe auch Mantis .
1
Um das zu bestätigen - ist es Ihr Punkt, dass die Leute niemals Enums benutzen sollten? War nicht klar.
Niico
18

Welche davon führen Ihrer Meinung nach eher zu Fehlern beim Lesen der Abfrage?

select * 
from Person 
where Gender = 1

Oder

select * 
from Person join Gender on Person.Gender = Gender.GenderId
where Gender.Label = "Female" 

In SQL werden Aufzählungstabellen erstellt, da letztere besser lesbar sind. Dies führt zu weniger Fehlern beim Schreiben und Verwalten von SQL.

Sie könnten Gender direkt zu einer Zeichenfolge machen Person, aber dann müssten Sie versuchen, die Groß- und Kleinschreibung durchzusetzen. Sie können auch den Speichertreffer für die Tabelle und die Abfragezeit aufgrund des Unterschieds zwischen Zeichenfolgen und Ganzzahlen erhöhen, je nachdem, wie großartig Ihre DB bei der Optimierung von Dingen ist.

Telastyn
quelle
5
Aber dann verbinden wir Tische. Wenn meine Entität zwei Aufzählungen hat, werde ich nur für eine einfache Abfrage drei Tabellen verbinden.
user3748908
11
@ user3748908 - also? Joins sind das, worin DBs gut sind, und die Alternativen sind schlechter - zumindest in den Augen der Leute, die diese Route gewählt haben.
Telastyn
8
@ user3748908: Datenbanken sind nicht nur wirklich gut darin, Verknüpfungen zu erstellen, sondern auch wirklich gut darin, Konsistenz durchzusetzen. Das Durchsetzen von Konsistenz funktioniert wirklich, wirklich gut, wenn Sie eine Spalte in einer Tabelle auf die identifizierende Zeile einer anderen Tabelle verweisen und sagen können, dass der Wert für diese Spalte einer der Bezeichner in dieser Tabelle sein muss.
Blrfl
2
Dies ist alles wahr, aber es gibt viele Fälle, in denen Sie die Joins aus Leistungsgründen opfern müssen. Verstehen Sie mich nicht falsch. Ich beschäftige mich mit dieser Art von Design und Beitritt, aber ich bin der Meinung, dass die Welt nicht untergehen wird, wenn Sie feststellen, dass Sie die Beitritte manchmal aufgrund der Leistung nicht benötigen.
JonH
3
Wenn Sie aus Performancegründen die Verknüpfung mit Referenztabellen unterbrechen müssen, müssen Sie einen größeren Server kaufen oder versuchen nicht mehr, Prädikate durch eine große Anzahl von Unterabfragen zu pushen (ich gehe davon aus, dass Sie wissen, was Sie tun). Referenztabellen sollten sich innerhalb weniger Sekunden nach dem Starten der Datenbank in Ihrem Cache befinden.
Ben
10

Ich kann nicht glauben, dass die Leute das noch nicht erwähnt haben.

Fremde Schlüssel

Indem Sie die Aufzählung in Ihrer Datenbank behalten und der Tabelle einen Fremdschlüssel hinzufügen, der einen Aufzählungswert enthält, stellen Sie sicher, dass kein Code jemals falsche Werte für diese Spalte eingibt. Dies hilft Ihrer Datenintegrität und ist der offensichtlichste Grund, warum Sie IMO Tabellen für Aufzählungen haben sollten.

Benjamin Gruenbaum
quelle
Die Frage ist nur 5 Zeilen lang und lautet eindeutig "Neben dem offensichtlichen Grund für die Durchsetzung der Korrektheit". Niemand hat es erwähnt, weil das OP angibt, dass es offensichtlich ist und er nach anderen Rechtfertigungen sucht. PS: Ich stimme Ihnen zu, das ist ein guter Grund.
User1007074
6

Ich bin im Lager, das mit dir übereinstimmt. Wenn Sie eine Gender-Aufzählung in Ihrem Code und einen tblGender in Ihrer Datenbank haben, können Probleme bei der Wartung auftreten. Sie müssen dokumentieren, dass diese beiden Entitäten dieselben Werte haben sollten, und daher müssen alle Änderungen, die Sie an einer vornehmen, auch an der anderen vorgenommen werden.

Anschließend müssen Sie die Enum-Werte wie folgt an Ihre gespeicherten Prozeduren übergeben:

create stored procedure InsertPerson @name varchar, @gender int
    insert into tblPeople (name, gender)
    values (@name, @gender)

Überlegen Sie sich jedoch, wie Sie dies tun würden, wenn Sie diese Werte in einer Datenbanktabelle speichern würden:

create stored procedure InsertPerson @name varchar, @genderName varchar
    insert into tblPeople (name, gender)
    select @name, fkGender
    from tblGender
    where genderName = @genderName --I hope these are the same

Sicher, relationale Datenbanken werden unter Berücksichtigung von Joins erstellt, aber welche Abfrage ist leichter zu lesen?


Hier ist eine weitere Beispielabfrage:

create stored procedure SpGetGenderCounts
    select count(*) as count, gender
    from tblPeople
    group by gender

Vergleichen Sie das damit:

create stored procedure SpGetGenderCounts
    select count(*) as count, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender
    group by genderName --assuming no two genders have the same name

Hier ist noch eine andere Beispielabfrage:

create stored procedure GetAllPeople
    select name, gender
    from tblPeople

Beachten Sie, dass Sie in diesem Beispiel die Geschlechtszelle in Ihren Ergebnissen von einem Int in eine Enumeration konvertieren müssen. Diese Konvertierungen sind jedoch einfach. Vergleichen Sie das damit:

create stored procedure GetAllPeople
    select name, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender

Alle diese Abfragen sind kleiner und leichter zu verwalten, wenn Sie die Idee haben, die Enum-Definitionen aus der Datenbank herauszuhalten.

user2023861
quelle
1
Was wäre, wenn es nicht Geschlecht wäre? Ich denke, wir sind zu sehr damit beschäftigt, dass das Geschlecht das Feld ist. Was wäre, wenn das OP gesagt hätte: "Nehmen wir an, ich habe einen Entitätsfehler mit einem Prioritätsfeld" - würde sich Ihre Antwort ändern?
4
@MichaelT Die Liste der möglichen Werte von "priority" ist Teil des Codes, zumindest in dem Maße, in dem sie Teil der Daten ist. Sie sehen grafische Symbole für verschiedene Prioritäten? Sie erwarten nicht, dass sie aus der Datenbank entfernt werden? Und solche Dinge könnten thematisiert und gestaltet werden und dennoch den gleichen Wertebereich repräsentieren, der in DB gespeichert ist. Sie können es ohnehin nicht einfach in der Datenbank ändern. Sie müssen den Präsentationscode synchronisieren.
Eugene Ryabtsev
1

Ich würde eine Genders-Tabelle erstellen, um sie für die Datenanalyse zu verwenden. Ich könnte alle männlichen oder weiblichen Personen in der Datenbank nachschlagen, um einen Bericht zu erstellen. Je mehr Möglichkeiten Sie haben, Ihre Daten anzuzeigen, desto einfacher ist es, Trendinformationen zu ermitteln. Offensichtlich ist dies eine sehr einfache Aufzählung, aber für komplexe Aufzählungen (wie die Länder der Welt oder Staaten) ist es einfacher, spezielle Berichte zu erstellen.

zackery.fix
quelle
1

Zunächst müssen Sie entscheiden, ob die Datenbank immer nur von einer Anwendung verwendet wird oder ob mehrere Anwendungen sie möglicherweise verwenden. In einigen Fällen ist eine Datenbank nichts anderes als ein Dateiformat für eine Anwendung (SQLite-Datenbanken können in dieser Hinsicht häufig verwendet werden). In diesem Fall kann das Duplizieren der Enum-Definition als Tabelle häufig in Ordnung und sinnvoller sein.

Sobald Sie jedoch die Möglichkeit in Betracht ziehen möchten, dass mehrere Anwendungen auf die Datenbank zugreifen, ist eine Tabelle für die Aufzählung sehr sinnvoll (die anderen Antworten gehen ausführlicher auf das Warum ein). Die andere zu berücksichtigende Sache werden Sie oder ein anderer Entwickler die rohen Datenbankdaten betrachten wollen. In diesem Fall kann dies als eine andere Anwendungsverwendung angesehen werden (nur eine, bei der die Laboranzeige Raw-SQL ist).

Wenn Sie die im Code definierte Enumeration (zur Überprüfung des saubereren Codes und der Kompilierungszeit) sowie eine Tabelle in der Datenbank haben, würde ich empfehlen, Komponententests hinzuzufügen, um zu überprüfen, ob die beiden synchron sind.

Eric Johnson
quelle
1

Wenn Sie über eine Code-Enumeration verfügen, mit der die Geschäftslogik im Code gesteuert wird, sollten Sie aus den oben / unten aufgeführten Gründen dennoch eine Tabelle erstellen, um die Daten in der Datenbank darzustellen. Mit den folgenden Tipps können Sie sicherstellen, dass Ihre DB-Werte mit den Codewerten synchron bleiben:

  1. Machen Sie das ID-Feld in der Tabelle nicht zu einer Identitätsspalte. Fügen Sie ID und Beschreibung als Felder ein.

  2. Machen Sie in der Tabelle etwas anderes, damit die Entwickler wissen, dass die Werte semistatisch / an eine Code-Aufzählung gebunden sind. In allen anderen Nachschlagetabellen (in der Regel können Werte von Benutzern hinzugefügt werden) habe ich in der Regel ein LastChangedDateTime und ein LastChangedBy, aber wenn sie nicht in Aufzählungstabellen enthalten sind, kann ich mich daran erinnern, dass sie nur von Entwicklern geändert werden können. Dokumentieren Sie dies.

  3. Erstellen Sie einen Bestätigungscode, mit dem überprüft wird, ob sich jeder Wert in der Aufzählung in der entsprechenden Tabelle befindet und nur diese Werte in der entsprechenden Tabelle enthalten sind. Wenn Sie automatisierte "Health-Tests" für Anwendungen haben, die nach der Erstellung ausgeführt werden, sind Sie dort richtig. Andernfalls wird der Code beim Start der Anwendung automatisch ausgeführt, wenn die Anwendung in der IDE ausgeführt wird.

  4. Create Production liefert SQL-Skripte, die dasselbe tun, jedoch aus der Datenbank heraus. Bei korrekter Erstellung helfen sie auch bei der Migration der Umgebung.

Paul Schirf
quelle
0

Kommt auch darauf an, wer auf die Daten zugreift. Wenn Sie nur eine Anwendung haben, ist dies möglicherweise in Ordnung. Wenn Sie ein Data Warehouse oder ein Berichtssystem hinzufügen. Sie müssen wissen, was dieser Code bedeutet, was die vom Menschen redierbare Version des Codes ist.

Normalerweise würde die Typentabelle nicht als Enumeration im Code dupliziert. Sie können die Typentabelle in eine zwischengespeicherte Liste laden.

Class GenderList

   Public Shared Property UnfilteredList
   Public Shared Property Male = GetItem("M")
   Public Shared Property Female = GetItem("F")

End Class

Typ kommt und geht oft. Sie benötigen ein Datum, an dem der neue Typ hinzugefügt wurde. Wissen, wann ein bestimmter Typ entfernt wurde. Zeigen Sie es nur bei Bedarf an. Was ist, wenn ein Klient "Transgender" als Geschlecht haben möchte, andere Klienten jedoch nicht? All diese Informationen werden am besten in der Datenbank gespeichert.

the_lotus
quelle