Używam Pythona do pisania w bazie danych Postgres:
sql_string = "INSERT INTO hundred (name,name_slug,status) VALUES ("
sql_string += hundred + ", '" + hundred_slug + "', " + status + ");"
cursor.execute(sql_string)
Ale ponieważ niektóre z moich wierszy są identyczne, pojawia się następujący błąd:
psycopg2.IntegrityError: duplicate key value
violates unique constraint "hundred_pkey"
Jak napisać instrukcję SQL „INSERT, chyba że ten wiersz już istnieje”?
Widziałem złożone takie polecenia, jak to zalecane:
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
Ale po pierwsze, czy to przesada w stosunku do tego, czego potrzebuję, a po drugie, jak mogę wykonać jedną z nich jako prosty ciąg?
postgresql
sql-insert
upsert
AP257
źródło
źródło
Odpowiedzi:
Postgres 9.5 (wydany od 01.01.2016) oferuje polecenie „wstawiania” , znane również jako klauzula ON CONFLICT dla INSERT :
Rozwiązuje wiele subtelnych problemów, na które możesz natknąć się podczas jednoczesnego działania, które proponują inne odpowiedzi.
źródło
INSERT INTO distributors (did, dname) VALUES (7, 'Redline GmbH') ON CONFLICT (did) DO NOTHING;
(2) WSTAW jeśli nie istnieje inaczej AKTUALIZACJA -INSERT INTO distributors (did, dname) VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc') ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname;
Te przykłady pochodzą z instrukcji - postgresql.org/docs/9.5/static/sql-insert.htmlJest ładny sposób na wykonanie warunkowego INSERT w PostgreSQL:
CAVEAT Podejście to nie jest w 100% niezawodne dla równoczesnych operacji zapisu. Istnieje bardzo małe wyścigu pomiędzy
SELECT
wNOT EXISTS
anty-semi-join aINSERT
sama. To może nie w takich warunkach.źródło
RETURNS id
na przykład do uzyskania informacji,id
czy został wstawiony, czy nie?RETURNING id
na końcu zapytania, a zwróci albo nowy identyfikator wiersza, albo nic, jeśli nie wstawiono żadnego wiersza.Jednym z podejść byłoby utworzenie tabeli nieograniczonej (bez unikalnych indeksów) do wstawienia wszystkich danych i wybranie innej opcji niż wstawienie do setki tabel.
Tak wysoki byłby poziom. Zakładam, że wszystkie trzy kolumny są różne w moim przykładzie, więc dla kroku 3 zmień połączenie NOT EXITS, aby połączyć tylko na unikalnych kolumnach w tabeli setek.
Utwórz tymczasowy stół. Zobacz dokumenty tutaj .
Wstaw dane do tabeli temp.
Dodaj dowolne indeksy do tabeli temp.
Wykonaj wkładkę do stołu głównego.
źródło
SELECT name,name_slug,status
lub*
SELECT DISTINCT name, name_slug, status FROM temp_data
?Niestety
PostgreSQL
nie obsługujeMERGE
aniON DUPLICATE KEY UPDATE
, więc musisz to zrobić w dwóch instrukcjach:Możesz zawinąć w funkcję:
i po prostu nazwij to:
źródło
INSERT INTO hundred (name, name_slug, status) SELECT 'Chichester', 'chichester', NULL WHERE 'Chichester' NOT IN (SELECT NAME FROM hundred);
dowolną liczbę razy i ciągle wstawia wiersz.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
. Jest jeden rekord.Możesz skorzystać z WARTOŚCI - dostępnych w Postgres:
źródło
Wiem, że to pytanie jest dawno temu, ale pomyślałem, że to może komuś pomóc. Myślę, że najłatwiej to zrobić za pomocą wyzwalacza. Na przykład:
Wykonaj ten kod z wiersza polecenia psql (lub jakkolwiek chcesz wykonywać zapytania bezpośrednio w bazie danych). Następnie możesz wstawić jak zwykle z Pythona. Na przykład:
Zauważ, że jak już wspomniano @Thomas_Wouters, powyższy kod wykorzystuje parametry zamiast konkatenacji łańcucha.
źródło
Jest ładny sposób na wykonanie warunkowego INSERT w PostgreSQL przy użyciu zapytania WITH: Like:
źródło
Właśnie z tym mam problem, a moja wersja to 9.5
I rozwiązuję to za pomocą zapytania SQL poniżej.
Mam nadzieję, że pomoże to komuś, kto ma ten sam problem z wersją> = 9.5.
Dziękuje za przeczytanie.
źródło
WSTAW .. GDZIE NIE ISTNIEJE to dobre podejście. A warunków wyścigu można uniknąć dzięki „kopercie” transakcji:
źródło
To proste dzięki regułom:
Ale to nie powiedzie się przy równoczesnym zapisie ...
źródło
Podejście z najbardziej pozytywnymi opiniami (od Johna Doe) w jakiś sposób działa dla mnie, ale w moim przypadku z oczekiwanych 422 wierszy dostaję tylko 180. Nie mogłem znaleźć nic złego i nie ma żadnych błędów, więc szukałem innego proste podejście.
Używanie
IF NOT FOUND THEN
poSELECT
prostu działa idealnie dla mnie.(opisane w dokumentacji PostgreSQL )
Przykład z dokumentacji:
źródło
Klasa kursora psycopgs ma atrybut rowcount .
Możesz więc najpierw spróbować UPDATE i WSTAWIĆ tylko wtedy, gdy liczba wierszy wynosi 0.
Ale w zależności od poziomów aktywności w bazie danych możesz osiągnąć warunek wyścigu między UPDATE a INSERT, w którym inny proces może utworzyć ten rekord w międzyczasie.
źródło
Twoja kolumna „sto” wydaje się być zdefiniowana jako klucz podstawowy i dlatego musi być unikalna, co nie jest prawdą. Problem nie jest związany z twoimi danymi.
Sugeruję, aby wstawić identyfikator jako typ szeregowy, aby podać klucz podstawowy
źródło
Jeśli powiesz, że wiele wierszy jest identycznych, skończysz sprawdzanie wiele razy. Możesz je wysłać, a baza danych określi, czy wstawić, czy nie, z klauzulą ON CONFLICT w następujący sposób
źródło
Szukałem podobnego rozwiązania, próbując znaleźć SQL, który działa zarówno w PostgreSQL, jak i HSQLDB. (To właśnie utrudniało HSQLDB.) Korzystając z twojego przykładu jako podstawy, jest to format, który znalazłem gdzie indziej.
źródło
Oto ogólna funkcja python, która podając tablename, kolumny i wartości, generuje odpowiednik upsert dla postgresql.
import json
źródło
Rozwiązanie jest proste, ale nie natychmiastowe.
Jeśli chcesz skorzystać z tej instrukcji, musisz wprowadzić jedną zmianę w db:
po tych zmianach „WSTAW” będzie działać poprawnie.
źródło