Ist es verschwenderisch, eine neue Datenbanktabelle zu erstellen, anstatt den Datentyp enum zu verwenden?


38

Angenommen, ich habe vier Arten von Dienstleistungen, die ich anbiete (die sich wahrscheinlich nicht oft ändern):

  • Testen
  • Design
  • Programmierung
  • Andere

Angenommen, ich habe 60-80 tatsächliche Dienste, die jeweils in eine der oben genannten Kategorien fallen. Beispielsweise kann "ein Dienst" "Testprogramm unter Verwendung von Technik A" sein und es ist vom Typ "Testen".

Ich möchte sie in eine Datenbank kodieren. Ich habe mir ein paar Möglichkeiten ausgedacht:

Option 0:

Verwenden Sie diese Option VARCHARdirekt, um den Diensttyp direkt als Zeichenfolge zu codieren

Option 1:

Datenbank verwenden enum. Aber Enum ist böse

Option 2:

benutze zwei Tabellen:

service_line_item (id, service_type_id INT, description VARCHAR);
service_type (id, service_type VARCHAR);

Ich kann sogar referentielle Integrität genießen:

ALTER service_line_item 
    ADD FOREIGN KEY (service_type_id) REFERENCES service_type (id);

Hört sich gut an, ja?

Aber ich muss immer noch Dinge kodieren und mich mit ganzen Zahlen befassen, dh wenn ich die Tabelle auffülle. Oder ich muss aufwändige Programmier- oder DB-Konstrukte erstellen, wenn ich die Tabelle auffülle oder damit umgehe. Nämlich JOINs, wenn Sie sich direkt mit der Datenbank befassen oder neue objektorientierte Entitäten auf der Programmierseite erstellen und sicherstellen, dass ich sie richtig betreibe.

Option 3:

Verwenden Sie nicht, verwenden enumSie nicht zwei Tabellen, sondern nur eine Ganzzahlspalte

service_line_item (
    id,
    service_type INT,        -- use 0, 1, 2, 3 (for service types)
    description VARCHAR
);

Dies ist wie eine „falsche Aufzählung“, die mehr Overhead auf der Codeseite erfordert, wie z. B. das zu wissen {2 == 'Programming'}und angemessen damit umzugehen.

Frage:

Derzeit habe ich es mit Option 2 implementiert , die unter Konzepte geführt wird

  1. benutze keine Aufzählung (Option 1)
  2. Vermeiden Sie die Verwendung einer Datenbank als Tabellenkalkulation (Option 0)

Aber ich kann nicht anders, als zu glauben, dass mir das in Bezug auf Programmierung und kognitiven Aufwand verschwenderisch erscheint - ich muss zwei Tabellen kennen und mich mit zwei Tabellen gegen eine auseinandersetzen.

Für einen "weniger verschwenderischen Weg" schaue ich Option 3. IT ist leichter und erfordert im Wesentlichen die gleichen Code-Konstrukte, um zu funktionieren (mit geringfügigen Änderungen, aber Komplexität und Struktur sind im Wesentlichen die gleichen, jedoch mit einer einzigen Tabelle).

Ich nehme an, es ist im Idealfall nicht immer verschwenderisch und es gibt gute Fälle für beide Optionen, aber gibt es eine gute Richtlinie, wann man Option 2 und wann Option 3 verwenden sollte?

Wenn es nur zwei Typen gibt (binär)

Um dieser Frage noch ein bisschen mehr hinzuzufügen, habe ich am selben Ort die binäre Option "Standard" - oder "Ausnahmeservice", die für die Servicebuchung gelten kann. Ich habe das mit Option 3 verschlüsselt .

Ich habe beschlossen, keine neue Tabelle zu erstellen, um nur die Werte {"Standard", "Exception"} zu speichern. Meine Spalte enthält also nur {0, 1} und mein Spaltenname wird aufgerufen exception, und mein Code führt eine Übersetzung aus {0, 1} => {STANDARD, EXCEPTION}(die ich in der Programmiersprache als Konstanten codiert habe).

Bisher gefällt mir das auch nicht ..... (weder Option 2 noch Option 3). Ich finde Option 2 besser als 3, aber mit mehr Aufwand, und ich kann es trotzdem nicht vermeiden, Dinge als ganze Zahlen zu codieren, egal welche Option ich von 2 und 3 verwende.

ORM

Um nach dem Lesen der Antworten einen Kontext hinzuzufügen: Ich habe gerade (vor kurzem) wieder angefangen, ein ORM zu verwenden, in meinem Fall Doctrine 2. Nachdem ich das DB-Schema über Annotations definiert hatte, wollte ich die Datenbank auffüllen. Da mein gesamter Datensatz relativ klein ist, wollte ich versuchen, Programmierkonstrukte zu verwenden, um zu sehen, wie es funktioniert.

Ich habe zuerst service_types und dann service_line_items ausgefüllt, da eine Liste aus einer tatsächlichen Tabelle vorhanden war. Dinge wie 'Standard / Ausnahme' und 'Testen' sind also alle Zeichenfolgen in der Tabelle und müssen vor dem Speichern in der Datenbank in richtige Typen codiert werden.

Ich habe diese SO-Antwort gefunden: Was verwenden Sie in doctrine2 anstelle von ENUM? , das vorschlug, das Enum-Konstrukt von DB nicht zu verwenden, sondern ein INTFeld zu verwenden und die Typen mit dem 'const'-Konstrukt der Programmiersprache zu codieren.

Aber wie in der obigen SO-Frage ausgeführt, kann ich die direkte Verwendung von Ganzzahlen vermeiden und Sprachkonstrukte - Konstanten - verwenden, sobald sie definiert sind.

Aber trotzdem .... egal wie du es drehst, wenn ich mit stringeinem Typ beginne, muss ich ihn zuerst in einen richtigen Typ konvertieren , auch wenn ich einen ORM verwende.

Wenn ich also sage $str = 'Testing';, muss ich noch irgendwo einen Block haben, der so etwas macht wie:

switch($str):
{ 
    case 'Testing':  $type = MyEntity::TESTING; break;
    case 'Other':    $type = MyEntity::OTHER; break;
}

Das Gute ist, dass Sie nicht mit ganzen Zahlen / magischen Zahlen zu tun haben [stattdessen mit kodierten konstanten Mengen], aber das Schlechte ist, dass Sie ohne diesen Konvertierungsschritt keine Dinge automatisch in die Datenbank ziehen und aus der Datenbank entfernen können Wissen.

Und das meinte ich zum Teil damit, dass ich Dinge wie "noch immer Dinge verschlüsseln und mit ganzen Zahlen umgehen müssen" sagte. (Zugegeben, jetzt, nach Ocramius 'Kommentar, muss ich mich nicht direkt mit ganzen Zahlen befassen, sondern mit benannten Konstanten und einer gewissen Konvertierung in / von Konstanten, je nach Bedarf).


9
Was auch immer Sie tun, tun Sie nicht # 3. Der Psychopath, der es aufrechterhält, wird ständig herausfinden müssen, was diese magischen Zahlen bedeuten. Wenn Sie das tun, hoffen Sie besser, dass sie nicht wissen, wo Sie leben. blog.codinghorror.com/coding-for-violent-psychopaths
RubberDuck

7
Ich mag Option 2. Wenn Sie die Verbreitung von Nachschlagetabellen nicht mögen, verwenden Sie eine Tabelle und fügen Sie eine Spalte "Nachschlagetyp" hinzu. Aber ja, das Erstellen einer Nachschlagetabelle ist die "Standardmethode", da Sie auf diese Weise unterhaltsame Dinge ausführen können, z. B. das einfache Auffüllen eines Dropdowns in der Benutzeroberfläche.
Robert Harvey

Verwenden Sie in Ihren Posts hier nicht "BEARBEITEN". Wir sind kein Forum. Jeder Stack Exchange-Beitrag enthält bereits einen detaillierten Bearbeitungsverlauf , den jeder anzeigen kann.
Robert Harvey

Wenn ich EDIT nicht verwenden kann, was soll ich verwenden?
Dennis

Bearbeiten Sie einfach den Beitrag und lassen Sie ihn natürlich aussehen, wie ich es bereits getan habe. Sehen Sie sich den Bearbeitungsverlauf an, um die Änderungen zu überprüfen.
Robert Harvey

Antworten:


35

Option 2 unter Verwendung von Referenztabellen ist die Standardmethode dafür. Es wurde von Millionen von Programmierern verwendet und funktioniert bekanntermaßen. Es ist ein Muster , so dass jeder, der sich Ihre Sachen ansieht, sofort weiß, was los ist. Es gibt Bibliotheken und Tools, die mit Datenbanken arbeiten und Sie vor vielen, vielen Arbeiten bewahren, die damit richtig umgehen. Die Vorteile der Verwendung sind unzählig.

Ist es verschwenderisch? Ja, aber nur geringfügig. In jeder halbwegs vernünftigen Datenbank werden häufig verknüpfte kleine Tabellen immer zwischengespeichert, sodass die Verschwendung im Allgemeinen nicht wahrnehmbar ist.

Alle anderen Optionen, die Sie beschrieben haben, sind Ad-hoc- und Hacky-Optionen, einschließlich MySQL enum, da sie nicht zum SQL-Standard gehören. (Abgesehen davon enumist die Implementierung von MySQL schade, nicht die Idee selbst. Es würde mir nichts ausmachen, sie eines Tages als Teil des Standards zu sehen.)

Ihre letzte Option # 3 mit der Verwendung einer einfachen Ganzzahl ist besonders hacky. Sie erhalten die schlechteste aller Welten: keine referenzielle Integrität, keine benannten Werte, kein definitives Wissen in der Datenbank darüber, wofür ein Wert steht, nur willkürliche ganze Zahlen, die überall geworfen werden. Auf diese Weise können Sie die Verwendung von Konstanten in Ihrem Code beenden und stattdessen hartcodierte Werte verwenden. circumference = radius * 6.28318530718;. Wie ist es damit?

Ich denke, Sie sollten noch einmal überprüfen, warum Sie Referenztabellen als lästig empfinden. Soweit ich weiß, sind sie für niemanden lästig. Könnte es sein, dass Sie nicht die richtigen Werkzeuge für den Job verwenden?

Ihr Satz über die Notwendigkeit, "Dinge zu codieren und mit ganzen Zahlen umzugehen" oder "aufwändige Programmierkonstrukte zu erstellen" oder "neue objektorientierte Entitäten auf der Programmierseite zu erstellen", sagt mir, dass Sie möglicherweise versuchen, objektrelationale Operationen durchzuführen Mapping (ORM) im laufenden Betrieb im Code Ihrer Anwendung verteilt, oder Sie versuchen im besten Fall, Ihren eigenen objektrelationalen Mapping-Mechanismus zu implementieren, anstatt ein vorhandenes ORM-Tool für den Job zu verwenden, z. B. Hibernate. All diese Dinge sind mit Hibernate ein Kinderspiel. Das Erlernen dauert eine Weile, aber wenn Sie es erst einmal gelernt haben, können Sie sich wirklich auf die Entwicklung Ihrer Anwendung konzentrieren und vergessen, wie Dinge in der Datenbank dargestellt werden.

Wenn Sie Ihr Leben durch die direkte Arbeit mit der Datenbank vereinfachen möchten, können Sie mindestens zwei Dinge tun, die mir gerade einfallen:

  1. Erstellen Sie Ansichten, die Ihre Haupttabellen mit den von ihnen referenzierten Referenztabellen verknüpfen, sodass jede Zeile nicht nur die Referenz-IDs, sondern auch die entsprechenden Namen enthält.

  2. Verwenden Sie anstelle einer Ganzzahl-ID für die Referenztabelle eine CHAR (4) -Spalte mit 4-Buchstaben-Abkürzungen. Die IDs Ihrer Kategorien würden also "TEST", "DSGN", "PROG", "OTHR" lauten. (Ihre Beschreibungen würden natürlich korrekte englische Wörter bleiben.) Es wird etwas langsamer sein, aber glauben Sie mir, niemand wird es bemerken.

Wenn es nur zwei Typen gibt, verwenden die meisten Leute nur eine Boolesche Spalte. Diese "standard / exception" -Spalte würde also als Boolescher Wert implementiert und als "IsException" bezeichnet.


3
Als beiseite, Postgres hat auch Aufzählungstypen , auch. Sie sind einfach und nichts Besonderes, sodass Sie eine lesbare Zeichenfolge als Wert verwenden können, aber eine effizientere Ganzzahl unter der Haube verwenden können.
Kat

Was ist mit dem Fall, wenn Daten konsequent wiederholt, aber nicht redundant sind (z. B. nicht zu Aktualisierungs- / Einfügungs- / Löschanomalien führen)? Zum Beispiel das Geschlecht einer Person (es ist unwahrscheinlich, dass neue Datentypen eingeführt werden, der Name eines Geschlechts muss nie geändert werden usw.)
Adam Thompson

Dies liegt daran, dass Sie irgendwann feststellen werden, dass Sie eine "Akzeptanzumgebung" benötigen und Ihre nicht veränderten Aufzählungen geändert werden müssen.
Pieter B

3

Option 2 mit Konstanten oder Aufzählungen am Programmende.
Obwohl es Wissen dupliziert und gegen das Prinzip der einzigen Quelle der Wahrheit verstößt, können Sie damit umgehen, indem Sie die Fail-Fast- Technik anwenden. Wenn Ihr System geladen wird, wird überprüft, ob die Enums oder Konstantenwerte in der Datenbank vorhanden sind. Wenn nicht, sollte das System einen Fehler auslösen und das Laden ablehnen. Es ist in der Regel billiger, diesen Fehler zu diesem Zeitpunkt zu beheben, als später, wenn möglicherweise etwas Schwerwiegenderes passiert ist.


0

Es gibt nichts, was Sie davon abhält, [kurze] Zeichenfolgen als Schlüssel zu verwenden, sodass Sie die Lesbarkeit von Namen in Ihren Tabellen behalten und nicht auf sinnlose Ersatznummerncodierung zurückgreifen können. Sie sollten immer noch die separate Tabelle zur Beschreibung der Servicetypen haben, nur für den Fall, dass Ihre Bewerbung beispielsweise international wird!

Ihre Benutzer können Ihre vier Kategorien in ihrer eigenen Sprache sehen, aber Ihre Datenbanktabellen enthalten weiterhin Werte, die Sie lesen können - und keine davon erfordert Datenbankstruktur- oder Codeänderungen!

table service_type 
( id VARCHAR 
, name VARCHAR 
  primary key ( id ) 
);
table service_line_item 
( id 
, service_type VARCHAR 
, description VARCHAR
  foreign key ( service_type ) references service_type ( id )
);

select * from service_type ; 

+-------------+----------------+
| id          | name           |
+-------------+----------------+
| Testing     | Testen         |
| Design      | Design         | 
| Programming | Programmierung |
| Other       | Andere         |
+-------------+----------------+

oder für Ihre französischen Kunden ...

update services_types set name = 'Essai'         where id = 'Testing'; 
update services_types set name = 'Conception'    where id = 'Design'; 
update services_types set name = 'Programmation' where id = 'Programming'; 
update services_types set name = 'Autre'         where id = 'Other'; 
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.