Saya perlu memasukkan beberapa baris dengan satu kueri (jumlah baris tidak konstan), jadi saya perlu menjalankan kueri seperti ini:
INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);
Satu-satunya cara saya tahu adalah
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)
tapi saya ingin cara yang lebih sederhana.
python
postgresql
psycopg2
Sergey Fedoseev
sumber
sumber
execute
strategi. Saya melihat percepatan sekitar 100x berkat ini!executemany
menjalankan komit setelah setiap sisipan. Jika Anda malah membungkus semuanya dalam suatu transaksi, mungkin itu akan mempercepat hal-hal?executemany
tidak melakukan sesuatu yang optimal, hanya loop dan melakukan banyakexecute
pernyataan. Dengan menggunakan metode ini, sisipan 700 baris ke server jarak jauh berubah dari 60-an menjadi <2s.+
sepertinya bisa membuka injeksi sql, saya merasaexecute_values()
solusi @Clodoaldo Neto lebih aman.execute_values
Metode baru di Psycopg 2.7:Cara pythonic melakukannya di Psycopg 2.6:
Penjelasan: Jika data yang akan dimasukkan diberikan sebagai daftar tupel seperti di
maka sudah dalam format yang diperlukan sebagai
yang
values
sintaks dariinsert
klausul mengharapkan daftar catatan seperti diinsert into t (a, b) values (1, 'x'),(2, 'y')
Psycopg
mengadaptasi Pythontuple
ke Postgresqlrecord
.Satu-satunya pekerjaan yang diperlukan adalah menyediakan template daftar catatan untuk diisi oleh psycopg
dan letakkan di
insert
kueriMencetak
insert_query
outputSekarang dengan
Psycopg
substitusi argumen biasaAtau hanya menguji apa yang akan dikirim ke server
Keluaran:
sumber
execute_values
saya bisa menjalankan sistem saya pada catatan 1k per menit hingga 128k catatan per menitPerbarui dengan psycopg2 2.7:
Klasik
executemany()
sekitar 60 kali lebih lambat dari implementasi @ ant32 (disebut "dilipat") seperti yang dijelaskan dalam utas ini: https://www.postgresql.org/message-id/20170130215151.GA7081%40deb76.aryehleib.comImplementasi ini ditambahkan ke psycopg2 dalam versi 2.7 dan disebut
execute_values()
:Jawaban sebelumnya:
Untuk menyisipkan banyak baris, menggunakan
VALUES
sintaks multirow denganexecute()
sekitar 10x lebih cepat daripada menggunakan psycopg2executemany()
. Memang,executemany()
hanya menjalankan banyakINSERT
pernyataan individual .@ ant32 's kode berfungsi dengan baik di Python 2. Tetapi dalam Python 3,
cursor.mogrify()
mengembalikan byte,cursor.execute()
mengambil byte atau string, dan','.join()
mengharapkanstr
instance.Jadi dalam Python 3 Anda mungkin perlu memodifikasi kode @ ant32, dengan menambahkan
.decode('utf-8')
:Atau dengan menggunakan byte (dengan
b''
ataub""
) saja:sumber
cursor.copy_from adalah solusi tercepat yang saya temukan untuk sisipan massal sejauh ini. Inilah inti yang saya buat yang mengandung kelas bernama IteratorFile yang memungkinkan iterator menghasilkan string untuk dibaca seperti file. Kami dapat mengonversi setiap rekaman input ke string menggunakan ekspresi generator. Jadi solusinya
Untuk ukuran args yang sepele ini tidak akan membuat banyak perbedaan kecepatan, tapi saya melihat speedup besar ketika berhadapan dengan ribuan baris. Ini juga akan lebih hemat memori daripada membangun string kueri raksasa. Sebuah iterator hanya akan menyimpan satu catatan input dalam memori pada suatu waktu, di mana pada titik tertentu Anda akan kehabisan memori dalam proses Python Anda atau di Postgres dengan membangun string kueri.
sumber
Cuplikan dari halaman tutorial Psycopg2 di Postgresql.org (lihat bawah) :
Itu tidak menyimpan banyak kode, tetapi secara definitif terlihat lebih baik.
sumber
INSERT
pernyataan individual . Berguna, tetapi tidak sama denganVALUE
insert multi- d tunggal .Semua teknik ini disebut 'Sisipan Diperpanjang "dalam terminologi Postgres, dan pada tanggal 24 November 2016, masih satu ton lebih cepat daripada pelaksanaan psychopg2 () dan semua metode lain yang tercantum dalam utas ini (yang saya coba sebelum datang ke ini menjawab).
Berikut adalah beberapa kode yang tidak menggunakan cur.mogrify dan bagus dan hanya untuk mendapatkan kepala Anda:
Tetapi harus dicatat bahwa jika Anda dapat menggunakan copy_from (), Anda harus menggunakan copy_from;)
sumber
Saya telah menggunakan jawaban ant32 di atas selama beberapa tahun. Namun saya telah menemukan bahwa ada kesalahan di python 3 karena
mogrify
mengembalikan byte string.Konversi secara eksplisit untuk memotong string adalah solusi sederhana untuk membuat kode python 3 kompatibel.
sumber
Pendekatan lain yang bagus dan efisien - adalah untuk melewatkan baris untuk dimasukkan sebagai 1 argumen, yang merupakan array dari objek json.
Misalnya Anda menyampaikan argumen:
Ini adalah array, yang dapat berisi sejumlah objek di dalamnya. Kemudian SQL Anda terlihat seperti:
Perhatikan: Kemajuan Anda harus cukup baru, untuk mendukung json
sumber
The cursor.copyfrom solusi yang disediakan oleh @ jopseph.sheedy ( https://stackoverflow.com/users/958118/joseph-sheedy ) di atas ( https://stackoverflow.com/a/30721460/11100064 ) memang cepat kilat.
Namun, contoh yang dia berikan tidak secara umum dapat digunakan untuk catatan dengan sejumlah bidang dan saya butuh waktu untuk mencari tahu bagaimana menggunakannya dengan benar.
IteratorFile perlu di-instantiated dengan bidang-bidang yang dipisahkan-tab seperti ini (
r
adalah daftar dicts di mana setiap dict adalah catatan):Untuk menggeneralisasi jumlah bidang yang berubah-ubah, pertama-tama kita akan membuat string garis dengan jumlah tab dan placeholder bidang yang benar:
"{}\t{}\t{}....\t{}"
dan kemudian gunakan.format()
untuk mengisi nilai bidang*list(r.values())) for r in records
::fungsi lengkap di intinya di sini .
sumber
Jika Anda menggunakan SQLAlchemy, Anda tidak perlu dipusingkan dengan pembuatan string karena SQLAlchemy mendukung pembuatan
VALUES
klausa multi-baris untuk satuINSERT
pernyataan :sumber
insert_query
baris. Kemudian,session.execute()
hanya memanggilexecute()
pernyataan psycopg2 dengan string masif tunggal. Jadi "trik" adalah membangun seluruh objek pernyataan penyisipan terlebih dahulu. Saya menggunakan ini untuk memasukkan 200.000 baris sekaligus dan melihat peningkatan kinerja besar-besaran menggunakan kode ini dibandingkan dengan yang normalexecutemany()
.execute_batch telah ditambahkan ke psycopg2 sejak pertanyaan ini diposting.
Itu lebih lambat dari execute_values tetapi lebih mudah digunakan.
sumber
execute_values
adalah lebih cepat daripadaexecute_batch
executemebanyak menerima array tupel
https://www.postgresqltutorial.com/postgresql-python/insert/
sumber
Jika Anda ingin memasukkan beberapa baris dalam satu statemens insert (dengan asumsi Anda tidak menggunakan ORM) cara termudah sejauh ini bagi saya adalah dengan menggunakan daftar kamus. Berikut ini sebuah contoh:
Seperti yang Anda lihat, hanya satu permintaan yang akan dieksekusi:
sumber
Menggunakan aiopg - Cuplikan di bawah berfungsi dengan baik
sumber
Akhirnya dalam versi SQLalchemy1.2, implementasi baru ini ditambahkan untuk menggunakan psycopg2.extras.execute_batch () alih-alih menjalankan banyak ketika Anda menginisialisasi mesin Anda dengan use_batch_mode = Benar seperti:
http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109
Maka seseorang harus menggunakan SQLalchmey tidak akan repot untuk mencoba berbagai kombinasi sqla dan psycopg2 dan mengarahkan SQL bersama ..
sumber