Muszę wstawić wiele wierszy za pomocą jednego zapytania (liczba wierszy nie jest stała), więc muszę wykonać zapytanie takie jak to:
INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);
Znam tylko jeden sposób
args = [(1,2), (3,4), (5,6)]
args_str = ','.join(cursor.mogrify("%s", (x, )) for x in args)
cursor.execute("INSERT INTO t (a, b) VALUES "+args_str)
ale ja chcę prostszego sposobu.
python
postgresql
psycopg2
Sergey Fedoseev
źródło
źródło
execute
strategii. Dzięki temu zauważyłem przyspieszenie około 100x!executemany
uruchamia zatwierdzenie po każdym wstawieniu. Jeśli zamiast tego zawrzesz całość w transakcję, może to przyspieszy sprawę?executemany
nie robi nic optymalnego, po prostu zapętla się i robi wieleexecute
instrukcji. Korzystając z tej metody, wstawianie 700 wierszy na zdalny serwer przeszło z 60 do <2 sekund.+
wydaje się, że może otworzyć się na wstrzyknięcie sql, czuję, żeexecute_values()
rozwiązanie @Clodoaldo Neto jest bezpieczniejsze.Nowa
execute_values
metoda w Psycopg 2.7:Pythonowy sposób na zrobienie tego w Psycopg 2.6:
Objaśnienie: Jeśli dane do wstawienia są podane jako lista krotek, jak w
to jest już w dokładnie wymaganym formacie, jak
values
składniainsert
klauzuli spodziewa listę rekordów, jak winsert into t (a, b) values (1, 'x'),(2, 'y')
Psycopg
dostosowuje Pythonatuple
do Postgresqlrecord
.Jedyną konieczną pracą jest dostarczenie szablonu listy rekordów do wypełnienia przez psycopg
i umieść go w
insert
zapytaniuDrukowanie
insert_query
wynikówTeraz do zwykłego
Psycopg
podstawiania argumentówLub po prostu testowanie tego, co zostanie wysłane na serwer
Wynik:
źródło
execute_values
byłem w stanie uruchomić mój system z prędkością 1 tys. rekordów na minutę do 128 tys. rekordów na minutęAktualizacja za pomocą psycopg2 2.7:
Wersja klasyczna
executemany()
jest około 60 razy wolniejsza niż implementacja @ ant32 (zwana „złożoną”), jak wyjaśniono w tym wątku: https://www.postgresql.org/message-id/20170130215151.GA7081%40deb76.aryehleib.comTa implementacja została dodana do psycopg2 w wersji 2.7 i nazywa się
execute_values()
:Poprzednia odpowiedź:
Aby wstawić wiele wierszy, użycie
VALUES
składni multirow zexecute()
jest około 10x szybsze niż użycie psycopg2executemany()
. Rzeczywiście,executemany()
po prostu uruchamia wiele indywidualnychINSERT
instrukcji.Kod @ ant32 działa doskonale w Pythonie 2. Ale w Pythonie 3
cursor.mogrify()
zwraca bajty,cursor.execute()
pobiera bajty lub łańcuchy i','.join()
oczekujestr
wystąpienia.Dlatego w Pythonie 3 może zajść potrzeba zmodyfikowania kodu @ ant32, dodając
.decode('utf-8')
:Lub używając tylko bajtów (z
b''
lubb""
):źródło
kursor.copy_from to zdecydowanie najszybsze rozwiązanie, jakie znalazłem dla wstawiania zbiorczego. Oto streszczenie, które stworzyłem, zawierające klasę o nazwie IteratorFile, która umożliwia iteratorowi, który generuje ciągi, odczytywanie jak pliku. Możemy przekonwertować każdy rekord wejściowy na łańcuch za pomocą wyrażenia generatora. Więc rozwiązaniem byłoby
W przypadku tego trywialnego rozmiaru argumentów nie spowoduje to dużej różnicy w szybkości, ale widzę duże przyspieszenia w przypadku tysięcy + wierszy. Będzie to również bardziej wydajne w pamięci niż tworzenie gigantycznego ciągu zapytania. Iterator może przechowywać tylko jeden rekord wejściowy w pamięci naraz, gdzie w pewnym momencie zabraknie pamięci w procesie Pythona lub w Postgres, budując ciąg zapytania.
źródło
Fragment ze strony samouczka Psycopg2 na Postgresql.org (patrz na dole) :
Nie oszczędza dużo kodu, ale zdecydowanie wygląda lepiej.
źródło
INSERT
instrukcji. Przydatne, ale nie to samo, co pojedynczaVALUE
wkładka multi- d.Wszystkie te techniki w terminologii Postgres nazywane są „Extended Inserts”, a od 24 listopada 2016 r. Są one wciąż o tonę szybsze niż executemany () psychopg2 i wszystkie inne metody wymienione w tym wątku (które wypróbowałem przed przejściem do tego odpowiedź).
Oto kod, który nie używa cur.mogrify i jest przyjemny i prosty do zrozumienia:
Ale należy zauważyć, że jeśli możesz użyć copy_from (), powinieneś użyć copy_from;)
źródło
Od kilku lat korzystam z powyższej odpowiedzi ant32. Jednak odkryłem, że jest to błąd w Pythonie 3, ponieważ
mogrify
zwraca ciąg bajtów.Konwersja jawna do ciągów bajtowych jest prostym rozwiązaniem umożliwiającym dostosowanie kodu do języka Python 3.
źródło
Innym przyjemnym i wydajnym podejściem jest przekazywanie wierszy do wstawienia jako 1 argument, czyli tablica obiektów json.
Np. Przekazujesz argument:
Jest to tablica, która może zawierać dowolną ilość obiektów wewnątrz. Wtedy twój SQL wygląda tak:
Uwaga: Twój postgress musi być wystarczająco nowy, aby obsługiwał json
źródło
Rozwiązanie kursor.copyfrom dostarczone przez @ jopseph.sheedy ( https://stackoverflow.com/users/958118/joseph-sheedy ) powyżej ( https://stackoverflow.com/a/30721460/11100064 ) jest rzeczywiście błyskawiczne.
Jednak podany przez niego przykład nie jest generalnie użyteczny dla rekordu z dowolną liczbą pól i zajęło mi trochę czasu, aby dowiedzieć się, jak go poprawnie użyć.
IteratorFile musi być
r
utworzony z polami oddzielonymi tabulatorami, takimi jak to ( jest to lista dykt, gdzie każdy dykt jest rekordem):Aby uogólnić dla dowolnej liczby pól, najpierw utworzymy ciąg linii z odpowiednią liczbą tabulatorów i symboli zastępczych:
"{}\t{}\t{}....\t{}"
a następnie użyjemy.format()
do wypełnienia wartości pól*list(r.values())) for r in records
:pełna funkcja w skrócie tutaj .
źródło
Jeśli używasz SQLAlchemy, nie musisz mieszać z ręcznym tworzeniem ciągu, ponieważ SQLAlchemy obsługuje generowanie wielowierszowej
VALUES
klauzuli dla pojedynczejINSERT
instrukcji :źródło
insert_query
linii. Następniesession.execute()
wywołuje po prostuexecute()
instrukcję psycopg2 z pojedynczym ogromnym ciągiem. Tak więc „sztuczka” polega na zbudowaniu najpierw całego obiektu instrukcji wstawiania. Używam tego do wstawiania 200 000 wierszy naraz i zauważyłem ogromny wzrost wydajności przy użyciu tego kodu w porównaniu do normalnegoexecutemany()
.execute_batch zostało dodane do psycopg2 od czasu wysłania tego pytania.
Jest wolniejszy niż execute_values, ale prostszy w użyciu.
źródło
execute_values
jest szybsza niżexecute_batch
executemany akceptuje tablicę krotek
https://www.postgresqltutorial.com/postgresql-python/insert/
źródło
Jeśli chcesz wstawić wiele wierszy w ramach jednej statystyki wstawiania (zakładając, że nie używasz ORM), najłatwiejszym sposobem byłoby dla mnie jak dotąd użycie listy słowników. Oto przykład:
Jak widać, zostanie wykonane tylko jedno zapytanie:
źródło
Korzystanie z aiopg - poniższy fragment działa doskonale
źródło
Wreszcie w wersji SQLalchemy1.2, ta nowa implementacja jest dodawana do używania psycopg2.extras.execute_batch () zamiast executemany podczas inicjalizacji silnika za pomocą use_batch_mode = True, na przykład:
http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109
Wtedy ktoś musiałby używać SQLalchmey, nie zawracałby sobie głowy próbowaniem różnych kombinacji sqla i psycopg2 oraz bezpośredniego SQL.
źródło