it-swarm.com.de

Wie füge ich Werte in eine stark normalisierte Datenbank ein, ohne übermäßige Doppelprüfungen durchzuführen?

Die Situation:

Der Leiter unseres Projekts hat beschlossen, eine stark normalisierte Datenbank als Datenbankdesign zu verwenden. Dies bedeutet, dass buchstäblich jedes Feld einer großen Tabelle eine ID anstelle des tatsächlichen Werts ist. Seine Absicht ist es, keine Duplikate jeglicher Art zu haben, selbst an Orten, an denen Duplikate nicht schaden (Vornamen der Leute, so etwas).

Dies führt jedoch zu einem Problem: Wenn Sie neue Daten einfügen, müssen Sie jede einzelne Untertabelle überprüfen, um festzustellen, ob der Wert vorhanden ist (erste Abfrage), und ihn dann einfügen, wenn dies nicht der Fall ist (zweite Abfrage), oder die ID abrufen Dies gilt buchstäblich für jede Spalte in der Haupttabelle (also 30 Mal oder so), und dann können wir das Objekt erstellen, das wir tatsächlich erhalten wollten. (Das sind ~ 60 Datenbanktreffer zum Erstellen eines Objekts!).

Wir arbeiten im Frühjahr, also verwenden wir jdbcTemplate, um tatsächlich eine Datenbankverbindung aufzubauen, und jede Abfrage ist teuer. Wenn wir Tausende neuer Datensätze einfügen oder aktualisieren, verlangsamt dies die Datenbank erheblich.

Dieser ganze Prozess fühlt sich für mich völlig schmutzig und falsch an, und deshalb wollte ich fragen: Gibt es einen besseren Weg? Ist es möglich, dass eine Unterabfrage einen Wert einfügt, wenn er nicht vorhanden ist oder nicht, und in beiden Fällen den tatsächlichen Schlüssel zurückgibt, der sofort zum Festlegen der ID in der Haupttabelle verwendet wird? Gibt es eine elegante Lösung, um die Anzahl der Abfragen zu reduzieren, ohne zu viel kompliziertes SQL einzuführen (um der Teammitglieder willen)?

6
Joe

Ein kleiner Gedanke ...

Formal haben Sie etwas Ähnliches wie diese vereinfachte Struktur:

CREATE TABLE slave1 (id PK, value UNIQUE);
CREATE TABLE slave2 (id PK, value UNIQUE);
CREATE TABLE main (id PK, id_slave1 FK, id_slave2 FK);

Wenn Sie 2 Datensätze (id_1, val_1_1, val_1_2) und (id_2, val_2_1, val_2_2) einfügen müssen, führen Sie Folgendes aus:

CREATE TEMPORARY TABLE temp (val_slave1, val_slave2) [ENGINE=Memory];

INSERT INTO temp (val_slave1, val_slave2)
VALUES (val_1_1, val_1_2),
       (val_2_1, val_2_2);

INSERT IGNORE INTO slave1 (value)
SELECT DISTINCT val_slave1
FROM temp;

INSERT IGNORE INTO slave2 (value)
SELECT DISTINCT val_slave2
FROM temp;

INSERT INTO main (id_slave1, id_slave2)
SELECT slave1.id, slave2.id
FROM temp
JOIN slave1 ON temp.val_slave1 = slave1.value
JOIN slave2 ON temp.val_slave2 = slave2.value;

Die Engine von temp kann Memory sein, wenn die Anzahl der eingefügten Werte gering ist, und InnoDB oder etwas anderes, wenn das eingefügte Datenarray sehr groß ist.

INSERT IGNORE funktioniert schnell genug über das EINZIGARTIGE indizierte Feld. Es garantiert, dass keine Duplikate in Slave-Tabellen vorhanden sind und dass die Werte, die eingefügt werden müssen, beim Einfügen in die Haupttabelle in Slaves vorhanden sind.

Die endgültige Abfrage muss auch schnell erfolgen - insbesondere, wenn auch temporäre Tabellenfelder indiziert werden.

Wenn Sie nur einen Datensatz einfügen müssen, können Sie natürlich nicht die Tabelle temp... verwenden, aber ich denke, Einheitlichkeit ist sicherer als eine kleine Vereinfachung.

Dies kann natürlich optimiert werden. Beispielsweise können alle Einfügungen zu einer gespeicherten Prozedur zusammengefasst werden, und Sie benötigen in "60 Datenbanktreffern" nicht, ein Aufruf ist ausreichend. Schließlich müssen Sie nur 3 Abfragen unabhängig von der Anzahl der Datensätze ausführen, für die eingefügt werden soll. Und nur einer von ihnen (Einfügen in Versuchung) kann riesig sein (oder sogar in viele Einsätze unterteilt werden).

3
Akina

Wenn Sie die Daten stapeln können, kann ich Ihnen eine Technik geben, die zwei Abfragen pro Stapel umfasst - eine zum Einfügen von neuen Namen für den Stapel, eine um alle IDs zu finden. Und es verschwendet keine IDs wie REPLACE, INSERT IGNORE, IODKU usw. tun.

  1. Erstellen Sie eine temporäre Tabelle mit allen denormalisierten Daten. Es hat freie Spalten für die IDs.
  2. Führen Sie die 2 Abfragen für jede Spalte aus, die normalisiert werden muss. (2 * 30 Anfragen in Ihrem Fall).
  3. INSERT .. SELECT .., um die Daten in die reale (n) Tabelle (n) zu verschieben.
  4. Schneiden Sie die temporäre Tabelle ab oder löschen Sie sie.

Siehe Massennormalisierung

Das wurde vor Jahren entworfen (und perfektioniert), als ich große Datenmengen (möglicherweise von vielen Clients) in Tabellen schaufeln musste und eine gewisse (nicht vollständige) Normalisierung erforderlich war.

Zuerst hatte ich INSERT IGNORE, aber schnell wurde mir klar, dass mir wahrscheinlich die Auto_Increment-IDs ausgehen würden. Ich war nicht bereit, 8-Byte BIGINTs zu verwenden. Immerhin ein Zweck der Normalisierung, um Platz zu sparen.

0
Rick James