Ich verwende Python, um in eine Postgres-Datenbank zu schreiben:
sql_string = "INSERT INTO hundred (name,name_slug,status) VALUES ("
sql_string += hundred + ", '" + hundred_slug + "', " + status + ");"
cursor.execute(sql_string)
Da jedoch einige meiner Zeilen identisch sind, wird der folgende Fehler angezeigt:
psycopg2.IntegrityError: duplicate key value
violates unique constraint "hundred_pkey"
Wie kann ich eine SQL-Anweisung "INSERT" schreiben, wenn diese Zeile nicht bereits vorhanden ist?
Ich habe komplexe Aussagen wie diese empfohlen gesehen:
IF EXISTS (SELECT * FROM invoices WHERE invoiceid = '12345')
UPDATE invoices SET billed = 'TRUE' WHERE invoiceid = '12345'
ELSE
INSERT INTO invoices (invoiceid, billed) VALUES ('12345', 'TRUE')
END IF
Aber erstens ist dies ein Overkill für das, was ich brauche, und zweitens, wie kann ich eine davon als einfache Zeichenfolge ausführen?
postgresql
sql-insert
upsert
AP257
quelle
quelle
Antworten:
Postgres 9.5 (veröffentlicht seit dem 07.01.2016) bietet INSERT einen "upsert" -Befehl, der auch als ON CONFLICT-Klausel bezeichnet wird :
Es löst viele der subtilen Probleme, auf die Sie bei gleichzeitiger Operation stoßen können, was einige andere Antworten vorschlagen.
quelle
INSERT INTO distributors (did, dname) VALUES (7, 'Redline GmbH') ON CONFLICT (did) DO NOTHING;
(2) EINFÜGEN, falls nicht vorhanden, sonst UPDATE -INSERT INTO distributors (did, dname) VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc') ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname;
Diese Beispiele stammen aus dem Handbuch - postgresql.org/docs/9.5/static/sql-insert.htmlEs gibt eine gute Möglichkeit, bedingtes INSERT in PostgreSQL durchzuführen:
CAVEAT Dieser Ansatz ist jedoch für gleichzeitige Schreibvorgänge nicht 100% zuverlässig . Es gibt eine sehr kleine Rennbedingung zwischen dem
SELECT
imNOT EXISTS
Anti-Semi-Join und demINSERT
selbst. Es kann unter solchen Bedingungen versagen.quelle
RETURNS id
zum Beispiel zu verwenden, umid
festzustellen , ob es eingefügt wurde oder nicht?RETURNING id
am und der Abfrage hinzu, und es wird entweder eine neue Zeilen-ID oder nichts zurückgegeben, wenn keine Zeile eingefügt wurde.Ein Ansatz wäre, eine Tabelle ohne Einschränkungen (keine eindeutigen Indizes) zu erstellen, in die alle Ihre Daten eingefügt werden, und eine andere Auswahl zu treffen, um sie in Ihre Hundert-Tabelle einzufügen.
So hoch wäre das Niveau. Ich gehe davon aus, dass alle drei Spalten in meinem Beispiel unterschiedlich sind. Ändern Sie daher für Schritt 3 den Join NOT EXITS, um nur die eindeutigen Spalten in der Hundert-Tabelle zu verknüpfen.
Erstellen Sie eine temporäre Tabelle. Siehe Dokumente hier .
INSERT Daten in temporäre Tabelle.
Fügen Sie der temporären Tabelle alle Indizes hinzu.
Haupttabelle einfügen.
quelle
SELECT name,name_slug,status
oder*
SELECT DISTINCT name, name_slug, status FROM temp_data
?PostgreSQL
Unterstützt leider wederMERGE
nochON DUPLICATE KEY UPDATE
, so dass Sie es in zwei Anweisungen tun müssen:Sie können es in eine Funktion einschließen:
und nenne es einfach:
quelle
INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred);
beliebig oft anrufen und es wird immer wieder die Zeile eingefügt.CREATE TABLE hundred (name TEXT, name_slug TEXT, status INT); INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred); INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred); SELECT * FROM hundred
. Es gibt einen Datensatz.Sie können VALUES verwenden - verfügbar in Postgres:
quelle
Ich weiß, dass diese Frage von vor einiger Zeit stammt, dachte aber, dass dies jemandem helfen könnte. Ich denke, der einfachste Weg, dies zu tun, ist über einen Auslöser. Z.B:
Führen Sie diesen Code an einer psql-Eingabeaufforderung aus (oder wie auch immer Sie Abfragen direkt in der Datenbank ausführen möchten). Dann können Sie wie gewohnt aus Python einfügen. Z.B:
Beachten Sie, dass der obige Code, wie bereits erwähnt, bei @Thomas_Wouters die Parameter nutzt, anstatt die Zeichenfolge zu verketten.
quelle
Es gibt eine gute Möglichkeit, das bedingte EINFÜGEN in PostgreSQL mithilfe der WITH-Abfrage durchzuführen: Gefällt mir:
quelle
Dies ist genau das Problem, mit dem ich konfrontiert bin, und meine Version ist 9.5
Und ich löse es mit SQL-Abfrage unten.
Hoffe, das hilft jemandem, der das gleiche Problem mit Version> = 9.5 hat.
Danke fürs Lesen.
quelle
EINFÜGEN .. WO NICHT EXISTIERT ist ein guter Ansatz. Und Rennbedingungen können durch Transaktion "Umschlag" vermieden werden:
quelle
Mit Regeln ist es einfach:
Aber es schlägt fehl bei gleichzeitigen Schreibvorgängen ...
quelle
Der Ansatz mit den meisten Upvotes (von John Doe) funktioniert irgendwie für mich, aber in meinem Fall von den erwarteten 422 Zeilen bekomme ich nur 180. Ich konnte nichts falsch finden und es gibt überhaupt keine Fehler, also habe ich nach einem anderen gesucht einfacher Ansatz.
Die Verwendung
IF NOT FOUND THEN
nach einemSELECT
funktioniert einfach perfekt für mich.(beschrieben in der PostgreSQL-Dokumentation )
Beispiel aus der Dokumentation:
quelle
Die Psycopgs-Cursor-Klasse hat das Attribut rowcount .
Sie können also zuerst UPDATE und INSERT nur versuchen, wenn die Zeilenanzahl 0 ist.
Abhängig von den Aktivitätsstufen in Ihrer Datenbank kann es jedoch vorkommen, dass zwischen UPDATE und INSERT eine Race-Bedingung vorliegt, bei der ein anderer Prozess diesen Datensatz in der Zwischenzeit erstellen kann.
quelle
Ihre Spalte "hundert" scheint als Primärschlüssel definiert zu sein und muss daher eindeutig sein, was nicht der Fall ist. Das Problem liegt nicht bei Ihren Daten.
Ich schlage vor, Sie geben eine ID als seriellen Typ ein, um den Primärschlüssel zu übergeben
quelle
Wenn Sie sagen, dass viele Ihrer Zeilen identisch sind, beenden Sie die Überprüfung mehrmals. Sie können sie senden, und die Datenbank bestimmt wie folgt, ob sie mit der ON CONFLICT-Klausel eingefügt wird oder nicht
quelle
Ich suchte nach einer ähnlichen Lösung und versuchte, SQL zu finden, das sowohl in PostgreSQL als auch in HSQLDB funktioniert. (HSQLDB hat dies schwierig gemacht.) Anhand Ihres Beispiels als Basis ist dies das Format, das ich an anderer Stelle gefunden habe.
quelle
Hier ist eine generische Python-Funktion, die unter Angabe eines Tabellennamens, von Spalten und Werten das Upsert-Äquivalent für postgresql generiert.
json importieren
quelle
Die Lösung ist einfach, aber nicht sofort.
Wenn Sie diese Anweisung verwenden möchten, müssen Sie eine Änderung an der Datenbank vornehmen:
Nach diesen Änderungen funktioniert "INSERT" korrekt.
quelle