Kann eine Datenbank zu relational sein?

2

Ich habe eine Frage zum Datenbankdesign. Ich frage hier, weil ich ein Enthusiast / Hobbyist und kein "Profi" bin, also möchte ich es nicht in den Stapelaustausch "Datenbankadministratoren" stellen, der speziell auf Profis ausgerichtet ist. Ich hoffe das ist der richtige Ort.

Ich möchte ein System aufbauen, um Begegnungen zwischen Gefangenen und Strafvollzugsbeamten zu verfolgen. Beim Entwerfen einer relationalen Datenbank habe ich die folgenden fünf Tabellen gefunden:

CREATE TABLE `encounters` (
  `id` INT NOT NULL,
  `encounterdate` DATETIME NULL,
  `officers_id` INT NOT NULL,
  `prisoners_id` INT NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `officers` (
  `id` INT NOT NULL,
  `badgenumber` VARCHAR(10) NULL,
  `lastnames_id` INT NOT NULL,
  `firstnames_id` INT NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `prisoners` (
  `id` INT NOT NULL,
  `regnumber` VARCHAR(10) NULL,
  `lastnames_id` INT NOT NULL,
  `firstnames_id` INT NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `firstnames` (
  `id` INT NOT NULL,
  `name` VARCHAR(45) NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `lastnames` (
  `id` INT NOT NULL,
  `name` VARCHAR(45) NULL,
  PRIMARY KEY (`id`)
);

mit folgenden Beziehungen: Beziehungsbild

Dieses Setup würde mich daran hindern, alle Vor- und Nachnamen zweimal zu speichern (einmal für die Beamten und einmal für die Gefangenen), aber ich bin nicht sicher, wie ich den Vor- / Nachnamen eines bestimmten Beamten UND den Vor- / Nachnamen eines bestimmten Gefangenen abrufen soll für eine gegebene Begegnung. Im Idealfall möchte ich eine SELECT-Anweisung verwenden, um einen Datensatz zu erhalten, der wie eine der folgenden Zeilen aussieht:

gewünschte Ausgabe

Ich kann innere Verknüpfungen verwenden, um Vor- und Nachnamen für einen Offizier zu erhalten:

SELECT
  encounters.encounterdate,
  officers.badgenumber,
  prisoners.regnumber,
  fnames.name,
  lnames.name
FROM
  encounters
  INNER JOIN prisoners ON encounters.prisoners_id = prisoners.id
  INNER JOIN officers ON encounters.officers_id = officers.id
  INNER JOIN fnames on officers.firstnames_id = fnames.id
  INNER JOIN lnames on officers.lastnames_id = lnames.id
WHERE
  officers.badgenumber = "b503"

oder ein Gefangener:

SELECT
  encounters.encounterdate,
  officers.badgenumber,
  prisoners.regnumber,
  fnames.name,
  lnames.name
FROM
  encounters
  INNER JOIN prisoners ON encounters.prisoners_id = prisoners.id
  INNER JOIN officers ON encounters.officers_id = officers.id
  INNER JOIN fnames on prisoners.firstnames_id = fnames.id
  INNER JOIN lnames on prisoners.lastnames_id = lnames.id
WHERE
  officers.badgenumber = "b503"

aber ich kann nicht herausfinden, ob es möglich ist, BEIDE für eine gegebene Begegnung mit nur einer SELECT-Anweisung zu erhalten.

Natürlich könnte ich es mit einer gespeicherten Prozedur / Funktion und ein paar SELECTs machen, aber ich bin interessiert zu hören, ob es einen einfacheren Weg gibt, es mit einem SELECT zu machen. Oder ist dies nur ein schlechter Weg, um eine Datenbank zu entwerfen?

Danke für Ihre Hilfe.

mjblay
quelle
Ich gehe davon aus, dass an Begegnungen manchmal mehr als zwei Personen beteiligt sind. Wie werden Sie mit einer Begegnung zwischen zwei Wachen und einem Gefangenen umgehen?
Jason Aller
Als Antwort auf Jason muss das System nur sehr spezifische Arten von Begegnungen verfolgen, die immer nur zwischen einem Offizier und einem Gefangenen auftreten. Wenn / wenn sich andere Personen einmischen, auch wenn dies nur Sekunden später geschieht, ist ein weiterer separater Begegnungsdatensatz erforderlich.
mjblay
Der wichtigste Kompromiss besteht darin, Platz zu sparen, um die Komplexität der Berechnungen zu erhöhen. JOINs sind nicht billig, besonders in komplizierteren und größeren Datenbanken. Ihre Frage hat eine Lösung (dieser Entwurf wird funktionieren), aber das Problem könnte leicht vermieden werden, wenn Sie diesen Tischentwurf nicht verwenden. Ich würde ein solches Design nur dann verwenden, wenn es einen sehr guten Grund dafür gibt. Nur meine $ 0,02, lassen Sie Vergangenheit Vergangenheit sein, etc.
chrishiestand

Antworten:

3

Ihr Beispiel mit Vor- und Nachnamen ist interessant. Was fügt es dem Bild hinzu? Welchen Wert hat es, Vornamen in eine separate Tabelle zu setzen? Wenn Sie alle Personen mit dem Vornamen "Marc" nehmen, was haben Sie dann? Bedeutet es etwas? Was ist, wenn Sie alle Personen mit dem Nachnamen "Smith" nehmen - gehören sie alle zu einer Familie, einem Clan, einer Rasse oder einer Nationalität? Leben sie alle in derselben Stadt? Wahrscheinlich nicht!

Wenn der Name "Smith" bedeutet, dass Sie braune Haare und blaue Augen haben und sich normalerweise im Cowboy-Stil kleiden, ist dies sinnvoll. Eine mögliche Analogie ist hier: Wenn es sich bei dem Artikel um ein Produkt des Typs TV handelt, verfügt er wahrscheinlich über eine Fernbedienung, benötigt ein Stromkabel usw.

Wenn Namen wie 1 MB groß wären, wäre es sinnvoll, eine ID zu verwenden. Das würde viel doppelten Speicherplatz sparen. Aber bei normalen Namen macht es keinen großen Unterschied, während es zu mehr Komplexität führt. Die Datenbank ist nicht nur komplexer, sondern auch der Code, mit dem die Verbindung zur Datenbank hergestellt wird, ist komplexer. Da es keinen Vorteil gibt, sollten Sie ihn besser vermeiden.

Da mit dem Vor- oder Nachnamen nichts Sinnvolles oder Nützliches verbunden ist, schreiben Sie ihn nicht in eine separate Tabelle.

Sie erwähnen in einem Kommentar, dass 240 KB eingespart werden könnten, wenn 20.000 Personen denselben Namen tragen. Das ist die meiste Zeit keine große Ersparnis. Natürlich gibt es Situationen, in denen dies sinnvoll ist, aber in der modernen Welt mit viel Speicherplatz ist die Einsparung von 1 MB Daten völlig irrelevant.

Wenn Sie das Speichern von Daten erwähnen, frage ich mich, ob es die Leistung beeinträchtigt oder nicht. Ich habe keine Ahnung, aber wenn wir über Zahlen sprechen und dies Facebook ist und wir täglich Millionen von Namen abfragen, frage ich mich, was effizienter sein wird, da dies eine zusätzliche Verknüpfung bedeutet. Dies sollte getestet werden und basierend auf diesem Test entscheiden Sie.

Wenn Sie extrem viele Anfragen haben oder nur sehr wenig Platz zur Verfügung haben und feststellen, dass die Verwendung eines zusätzlichen Tisches den Tag spart - tun Sie es!

SPRBRN
quelle
Das spart Platz und ich nehme an, es beschleunigt Routine-Abfragen. Wie ich bereits in einem anderen Kommentar erwähnt habe, kann es bei Tabellen mit nur eindeutigen Namen schneller sein, häufig nach eindeutigen Namen zu fragen (z. B. mit jQuery / ajax). Ein Autovervollständigungsfeld wird möglicherweise nach jedem Tastendruck aktualisiert bzw. neu geladen. Daher ist es möglicherweise weniger effizient, es mit den Ergebnissen von SELECT DISTINCT aus einer großen Tabelle zu füllen, als wenn die Namen separat in ihren eigenen Tabellen gespeichert würden. Aber ich habe hier die allgemeine Vorstellung, dass dies wahrscheinlich nicht die beste Vorgehensweise ist ... vielleicht werde ich die Idee aufgeben.
mjblay
Wenn Sie feststellen, dass dadurch die Ajax-Anforderung schneller ausgeführt wird, ist dies ein guter Grund, dies zu tun. Wenn Sie große Namenslisten auf einer Webseite anzeigen, kann die Verwendung einer ID das Herunterladen von Daten speichern, wenn Sie die Namen nur einmal herunterladen und auf der Seite selbst mit dieser ID verbinden. Trotzdem frage ich mich, ob es die Mühe und die zusätzliche Komplexität wert ist.
SPRBRN,
1

Als Antwort auf Ihre Ja- oder Nein-Frage, ja. Einige Strukturen sind in angemessenem Maße besser denormalisiert. Was ist angemessen? Das hängt natürlich davon ab.
In Ihrem speziellen Beispiel ist die von AEonEX vorgeschlagene Antwort nicht wegen Denormalisierung richtig, sondern weil diese Namen die richtigen Attribute der Entitäten sind, die sie benennen. Es kann angebracht sein, einen Zustand aus einer Adresse heraus zu exportieren, da der Zustand real ist, existiert und sich alle Verweise darauf tatsächlich auf dieselbe reale Sache beziehen. Ein Name, nicht so sehr.

Haakon Dahl
quelle
0

Es werden keine Tabellen firstnamesund benötigt lastnames. Speichern Sie stattdessen das firstnameund lastnamein officersund prisonersanstelle von firstnames_idund lastnames_id.

CREATE TABLE `encounters` (
  `id` INT NOT NULL,
  `encounterdate` DATETIME NULL,
  `officers_id` INT NOT NULL,
  `prisoners_id` INT NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `officers` (
  `id` INT NOT NULL,
  `badgenumber` VARCHAR(10) NULL,
  `lastnames` VARCHAR(45) NULL,
  `firstnames` VARCHAR(45) NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `prisoners` (
  `id` INT NOT NULL,
  `regnumber` VARCHAR(10) NULL,
  `lastnames` VARCHAR(45) NULL,
  `firstnames` VARCHAR(45) NULL,
  PRIMARY KEY (`id`)
);


SELECT
  encounters.encounterdate,
  officers.badgenumber,
  prisoners.regnumber,
  officers.firstnames,
  officers.lastnames,
  prisoners.firstnames,
  prisoners.lastnames,

FROM
  encounters
  INNER JOIN prisoners ON encounters.prisoners_id = prisoners.id
  INNER JOIN officers ON encounters.officers_id = officers.id
WHERE
  officers.badgenumber = "b503"
AEonAX
quelle
Sicher, ich verstehe, dass Sie Tabellen mit denselben Textinformationen erstellen können, die für jeden Datensatz wiederholt gespeichert wurden, aber nach meinem Verständnis war die Möglichkeit, dies zu vermeiden, einer der Vorteile einer relationalen Datenbank. Wenn beispielsweise 20.000 der Häftlinge Christopher heißen (dh varchar (11)), sind 12 x 20 KB = 240 KB erforderlich, während nur ganze Zahlen in jedem Datensatz der Häftlingstabelle gespeichert werden, die auf eine andere Tabelle verweisen, in der "Christopher" einmal gespeichert ist würde etwa ein Drittel des Platzes benötigen, dh (4 x 20.001) + 12 = 80.016 B.
mjblay