Verwendung von PostgreSQL v9.1. Ich habe folgende Tabellen:
CREATE TABLE foo
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
type VARCHAR(60) NOT NULL UNIQUE
);
CREATE TABLE bar
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
description VARCHAR(40) NOT NULL UNIQUE,
foo_id BIGINT NOT NULL REFERENCES foo ON DELETE RESTRICT
);
Angenommen, die erste Tabelle foo
wird wie folgt gefüllt:
INSERT INTO foo (type) VALUES
( 'red' ),
( 'green' ),
( 'blue' );
Gibt es eine Möglichkeit, Zeilen bar
einfach durch Verweisen auf die foo
Tabelle einzufügen ? Oder muss ich das in zwei Schritten tun, indem foo
ich zuerst den gewünschten Typ suche und dann eine neue Zeile einfüge bar
?
Hier ist ein Beispiel eines Pseudocodes, der zeigt, was ich mir erhofft habe:
INSERT INTO bar (description, foo_id) VALUES
( 'testing', SELECT id from foo WHERE type='blue' ),
( 'another row', SELECT id from foo WHERE type='red' );
postgresql
foreign-key
postgresql-9.1
insert
Stéphane
quelle
quelle
Einfaches INSERT
Die Verwendung von a
LEFT [OUTER] JOIN
anstelle von[INNER] JOIN
bedeutet, dass Zeilen vonval
nicht gelöscht werden, wenn in keine Übereinstimmung gefunden wirdfoo
. StattdessenNULL
wird für eingegebenfoo_id
.Der
VALUES
Ausdruck in der Unterabfrage entspricht dem CTE von @ ypercube . Gängige Tabellenausdrücke bieten zusätzliche Funktionen und sind in großen Abfragen leichter zu lesen, sie stellen jedoch auch ein Optimierungshindernis dar. Daher sind Unterabfragen in der Regel etwas schneller, wenn keine der oben genannten Anforderungen erfüllt ist.id
als Spaltenname dient ein weit verbreitetes Anti-Pattern. Solltefoo_id
undbar_id
oder etwas beschreibend sein. Wenn Sie eine Reihe von Tabellen verbinden, erhalten Sie am Ende mehrere Spalten mit dem Namenid
...Betrachten Sie einfach
text
odervarchar
stattvarchar(n)
. Wenn Sie wirklich eine Längenbeschränkung festlegen müssen, fügen Sie eineCHECK
Einschränkung hinzu:Möglicherweise müssen Sie explizite Typumwandlungen hinzufügen. Da der
VALUES
Ausdruck nicht direkt an eine Tabelle angehängt ist (wie inINSERT ... VALUES ...
), können keine Typen abgeleitet und Standarddatentypen ohne explizite Typdeklaration verwendet werden, was möglicherweise nicht in allen Fällen funktioniert. Es ist genug, um es in der ersten Reihe zu tun, der Rest wird in die Schlange fallen.INSERT fügt gleichzeitig fehlende FK-Zeilen ein
Wenn Sie nicht vorhandene Einträge im laufenden Betrieb erstellen möchten, sind CTEs
foo
in einer einzelnen SQL-Anweisung von entscheidender Bedeutung:Beachten Sie die zwei neuen einzufügenden Platzhalterzeilen. Beide sind lila , was es in
foo
noch nicht gibt . Zwei Zeilen zur Veranschaulichung der NotwendigkeitDISTINCT
in der erstenINSERT
Anweisung.Schritt für Schritt Erklärung
Der 1. CTE
sel
stellt mehrere Zeilen von Eingabedaten bereit. Die Unterabfrageval
mit demVALUES
Ausdruck kann durch eine Tabelle oder Unterabfrage als Quelle ersetzt werden. SofortLEFT JOIN
,foo
um diefoo_id
für bereits vorhandenetype
Zeilen anzufügen. Alle anderen Zeilen erhalten auffoo_id IS NULL
diese Weise.Der 2. CTE
ins
fügt verschiedene neue Typen (foo_id IS NULL
) einfoo
und gibt den neu generiertenfoo_id
zusammen mit demtype
zurück, um Zeilen einzufügen.Das letzte äußere
INSERT
kann jetzt eine foo.id für jede Zeile einfügen: Entweder der Typ, der bereits vorhanden war, oder er wurde in Schritt 2 eingefügt.Genau genommen geschehen beide Einfügungen "parallel", aber da dies eine einzelne Anweisung ist, werden sich Standardeinschränkungen
FOREIGN KEY
nicht beschweren. Die referenzielle Integrität wird standardmäßig am Ende der Anweisung erzwungen.SQL Fiddle für Postgres 9.3. (Funktioniert genauso in 9.1.)
Es ist eine winzige Race - Bedingung , wenn Sie mehrere dieser Abfragen gleichzeitig ausgeführt werden . Lesen Sie mehr unter verwandten Fragen hier und hier und hier . Wirklich nur unter starker gleichzeitiger Belastung, wenn überhaupt. Im Vergleich zu Caching-Lösungen, wie sie in einer anderen Antwort angekündigt wurden, ist die Chance winzig .
Funktion für den wiederholten Gebrauch
Für die wiederholte Verwendung würde ich eine SQL-Funktion erstellen, die ein Array von Datensätzen als Parameter verwendet und
unnest(param)
anstelle desVALUES
Ausdrucks verwendet.Wenn Ihnen die Syntax für Arrays von Datensätzen zu unübersichtlich ist, verwenden Sie eine durch Kommas getrennte Zeichenfolge als Parameter
_param
. Zum Beispiel des Formulars:Verwenden Sie dann diesen Befehl, um den
VALUES
Ausdruck in der obigen Anweisung zu ersetzen :Funktion mit UPSERT in Postgres 9.5
Erstellen Sie einen benutzerdefinierten Zeilentyp für die Parameterübergabe. Wir könnten darauf verzichten, aber es ist einfacher:
Funktion:
Anruf:
Schnell und absolut zuverlässig für Umgebungen mit gleichzeitigen Transaktionen.
Zusätzlich zu den obigen Abfragen ...
... trifft zu
SELECT
oderINSERT
einfoo
: Alletype
, die noch nicht in der FK-Tabelle vorhanden sind, werden eingefügt. Vorausgesetzt, die meisten Typen existieren bereits. Um absolut sicher zu sein und Rennbedingungen auszuschließen, werden vorhandene Zeilen, die wir benötigen, gesperrt (damit gleichzeitige Transaktionen nicht stören können). Wenn das für Ihren Fall zu paranoid ist, können Sie Folgendes ersetzen:mit
... gilt
INSERT
oderUPDATE
(wahres "UPSERT") ambar
: Wenn dasdescription
bereits existiert,type
wird es aktualisiert:Aber nur wenn sich
type
tatsächlich ändert:... übergibt Werte als bekannte Zeilentypen mit einem
VARIADIC
Parameter. Beachten Sie das Standardmaximum von 100 Parametern! Vergleichen Sie:Es gibt viele andere Möglichkeiten, mehrere Zeilen zu übergeben ...
Verbunden:
quelle
INSERT missing FK rows at the same time
Würde das Einfügen dieser Option in eine Transaktion in Ihrem Beispiel das Risiko von Racebedingungen in SQL Server verringern?SELECT
innerhalb einerWITH
Klausel). Quelle: MS-Dokumentation.INSERT ... RETURNING \gset
in tunpsql
, indem Sie die zurückgegebenen Werte als psql verwenden:'variables'
, dies funktioniert jedoch nur bei Einfügungen in einzelne Zeilen.Sieh nach oben. Sie benötigen im Allgemeinen die foo Identifikation, um sie in Stab einzufügen.
Nicht postgres-spezifisch, übrigens. (und Sie haben es nicht so getaggt) - so funktioniert SQL im Allgemeinen. Keine Abkürzungen hier.
In Bezug auf die Anwendung haben Sie möglicherweise einen Cache mit foo Elementen im Speicher. Meine Tabellen enthalten häufig bis zu 3 eindeutige Felder:
Beispiel:
Wenn Sie etwas mit einem Konto verknüpfen möchten, müssen Sie zunächst die ID abrufen. Wenn sich jedoch sowohl der Bezeichner als auch der Code ändern, kann ein positiver Cache im Speicher die meisten Suchvorgänge daran hindern, auf die Datenbank zuzugreifen.
quelle