1つのクエリで複数の行を挿入する必要があるため(行の数は一定ではありません)、次のようなクエリを実行する必要があります。
INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);
私が知っている唯一の方法は
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)
しかし、もっと簡単な方法が欲しいです。
別の都市にあるサーバーに複数の行を挿入するプログラムを作成しました。
この方法を使用すると、executemany
よりも約10倍高速であることがわかりました。私の場合、tup
は約2000行を含むタプルです。この方法を使用した場合、約10秒かかりました。
args_str = ','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) for x in tup)
cur.execute("INSERT INTO table VALUES " + args_str)
この方法を使用する場合は2分:
cur.executemany("INSERT INTO table VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s)", tup)
新しい execute_values
メソッド Psycopg 2.7で:
data = [(1,'x'), (2,'y')]
insert_query = 'insert into t (a, b) values %s'
psycopg2.extras.execute_values (
cursor, insert_query, data, template=None, page_size=100
)
Psycopg 2.6でそれを行うPythonの方法:
data = [(1,'x'), (2,'y')]
records_list_template = ','.join(['%s'] * len(data))
insert_query = 'insert into t (a, b) values {}'.format(records_list_template)
cursor.execute(insert_query, data)
説明:挿入されるデータが、次のようなタプルのリストとして提供される場合
data = [(1,'x'), (2,'y')]
それはすでに正確に必要な形式になっています
values
句のinsert
構文では、次のようなレコードのリストが必要です。
insert into t (a, b) values (1, 'x'),(2, 'y')
Psycopg
はPython Tuple
をPostgresql record
に適合させます。
唯一必要な作業は、psycopgが入力するレコードリストテンプレートを提供することです
# We use the data list to be sure of the template length
records_list_template = ','.join(['%s'] * len(data))
insert
クエリに配置します
insert_query = 'insert into t (a, b) values {}'.format(records_list_template)
insert_query
出力の印刷
insert into t (a, b) values %s,%s
これで通常のPsycopg
引数の置換に
cursor.execute(insert_query, data)
または、サーバーに送信される内容をテストするだけです
print (cursor.mogrify(insert_query, data).decode('utf8'))
出力:
insert into t (a, b) values (1, 'x'),(2, 'y')
psycopg2 2.7で更新:
このスレッドで説明されているように、古典的なexecutemany()
は@ ant32の実装(「折り畳まれた」と呼ばれる)の約60倍遅い: https://www.postgresql.org/message-id/20170130215151。 GA7081%40deb76.aryehleib.com
この実装はバージョン2.7でpsycopg2に追加され、execute_values()
と呼ばれます:
from psycopg2.extras import execute_values
execute_values(cur,
"INSERT INTO test (id, v1, v2) VALUES %s",
[(1, 2, 3), (4, 5, 6), (7, 8, 9)])
前の回答:
複数の行を挿入するには、execute()
で複数行のVALUES
構文を使用すると、psycopg2 executemany()
を使用するよりも約10倍高速です。実際、executemany()
は多くの個々のINSERT
ステートメントを実行するだけです。
@ ant32のコードはPython 2で完全に機能します。ただし、Python 3では、cursor.mogrify()
はバイトを返し、cursor.execute()
はバイトまたは文字列を取り、','.join()
はstr
インスタンスを想定しています。
したがって、Python 3では、.decode('utf-8')
を追加して@ ant32のコードを変更する必要があります。
args_str = ','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x).decode('utf-8') for x in tup)
cur.execute("INSERT INTO table VALUES " + args_str)
または、(b''
またはb""
で)バイトのみを使用して:
args_bytes = b','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) for x in tup)
cur.execute(b"INSERT INTO table VALUES " + args_bytes)
Postgresql.org(下を参照) :のPsycopg2のチュートリアルページのスニペット
最後に紹介したい項目は、辞書を使用して複数の行を挿入する方法です。次のものがある場合:
namedict = ({"first_name":"Joshua", "last_name":"Drake"},
{"first_name":"Steven", "last_name":"Foo"},
{"first_name":"David", "last_name":"Bar"})
次を使用して、辞書に3行すべてを簡単に挿入できます。
cur = conn.cursor()
cur.executemany("""INSERT INTO bar(first_name,last_name) VALUES (%(first_name)s, %(last_name)s)""", namedict)
多くのコードを保存しませんが、間違いなく見た目は良くなります。
cursor.copy_from は、私がバルク挿入で見つけた最速のソリューションです。 これは要点です 文字列を生成するイテレータがファイルのように読み取れるIteratorFileという名前のクラスを含むようにしました。ジェネレータ式を使用して、各入力レコードを文字列に変換できます。だから解決策は
args = [(1,2), (3,4), (5,6)]
f = IteratorFile(("{}\t{}".format(x[0], x[1]) for x in args))
cursor.copy_from(f, 'table_name', columns=('a', 'b'))
この些細なサイズの引数の場合、速度の差はあまりありませんが、数千行以上を処理する場合に大幅な高速化が見られます。また、巨大なクエリ文字列を作成するよりもメモリ効率が高くなります。イテレータは一度に1つの入力レコードのみをメモリに保持しますが、クエリ文字列を作成することにより、ある時点でPythonプロセスまたはPostgresのメモリが不足します。
これらの手法はすべて、Postgresの用語では「拡張挿入」と呼ばれ、2016年11月24日の時点で、psychogpg2のexecutemany()およびこのスレッドにリストされている他のすべてのメソッド(これに来る前に試したすべてのメソッド)よりもはるかに高速です回答)。
Cur.mogrifyを使用せず、ニースであり、単に頭を動かすためのコードを次に示します。
valueSQL = [ '%s', '%s', '%s', ... ] # as many as you have columns.
sqlrows = []
rowsPerInsert = 3 # more means faster, but with diminishing returns..
for row in getSomeData:
# row == [1, 'a', 'yolo', ... ]
sqlrows += row
if ( len(sqlrows)/len(valueSQL) ) % rowsPerInsert == 0:
# sqlrows == [ 1, 'a', 'yolo', 2, 'b', 'Swag', 3, 'c', 'selfie' ]
insertSQL = 'INSERT INTO "Twitter" VALUES ' + ','.join(['(' + ','.join(valueSQL) + ')']*rowsPerInsert)
cur.execute(insertSQL, sqlrows)
con.commit()
sqlrows = []
insertSQL = 'INSERT INTO "Twitter" VALUES ' + ','.join(['(' + ','.join(valueSQL) + ')']*len(sqlrows))
cur.execute(insertSQL, sqlrows)
con.commit()
ただし、copy_from()を使用できる場合は、copy_fromを使用する必要があることに注意してください;)
ユーザーが指定したバッチサイズとpsycopg2を使用して、DBに行のリストを適切に挿入するには!
def get_batch(iterable, size=100):
for i in range(0, len(iterable), size):
yield iterable[i: i + size]
def insert_rows_batch(table, rows, batch_size=500, target_fields=None):
"""
A utility method to insert batch of tuples(rows) into a table
NOTE: Handle data type for fields in rows yourself as per your table
columns' type.
:param table: Name of the target table
:type table: str
:param rows: The rows to insert into the table
:type rows: iterable of tuples
:param batch_size: The size of batch of rows to insert at a time
:type batch_size: int
:param target_fields: The names of the columns to fill in the table
:type target_fields: iterable of strings
"""
conn = cur = None
if target_fields:
target_fields = ", ".join(target_fields)
target_fields = "({})".format(target_fields)
else:
target_fields = ''
conn = get_conn() # get connection using psycopg2
if conn:
cur = conn.cursor()
count = 0
for mini_batch in get_batch(rows, batch_size):
mini_batch_size = len(mini_batch)
count += mini_batch_size
record_template = ','.join(["%s"] * mini_batch_size)
sql = "INSERT INTO {0} {1} VALUES {2};".format(
table,
target_fields,
record_template)
cur.execute(sql, mini_batch)
conn.commit()
print("Loaded {} rows into {} so far".format(count, table))
print("Done loading. Loaded a total of {} rows".format(count))
if cur:cur.close()
if conn:conn.close()
UPSERT(Insert + Update)とバッチを使用するpostgresが必要な場合: postgres_utilities
別のニースで効率的なアプローチ-1つの引数として挿入する行を渡すことです。これはjsonオブジェクトの配列です。
例えば。引数を渡す:
[ {id: 18, score: 1}, { id: 19, score: 5} ]
これは配列であり、内部に任意の量のオブジェクトを含めることができます。次に、SQLは次のようになります。
INSERT INTO links (parent_id, child_id, score)
SELECT 123, (r->>'id')::int, (r->>'score')::int
FROM unnest($1::json[]) as r
注意:jsonをサポートするには、postgressが十分に新しい必要があります
私は数年前から上記のant32の回答を使用しています。ただし、mogrify
はバイト文字列を返すため、python 3でエラーが発生することがわかりました。
明示的にbytse文字列に変換することは、コードpython 3と互換性を持たせるための簡単なソリューションです。
args_str = b','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) for x in tup)
cur.execute(b"INSERT INTO table VALUES " + args_str)
SQLAlchemyを使用している場合、SQLAlchemy は単一のVALUES
ステートメントに対して複数行のINSERT
句の生成をサポートするため、文字列を手作業で作成する必要はありません :
rows = []
for i, name in enumerate(rawdata):
row = {
'id': i,
'name': name,
'valid': True,
}
rows.append(row)
if len(rows) > 0: # INSERT fails if no rows
insert_query = SQLAlchemyModelName.__table__.insert().values(rows)
session.execute(insert_query)