[python] psycopg2 : 하나의 쿼리로 여러 행 삽입

하나의 쿼리로 여러 행을 삽입해야합니다 (행 수가 일정하지 않음). 다음과 같이 쿼리를 실행해야합니다.

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)

그러나 나는 더 간단한 방법을 원합니다.



답변

다른 도시에있는 서버에 여러 줄을 삽입하는 프로그램을 만들었습니다.

이 방법을 사용하는 것보다 약 10 배 빠릅니다 executemany. 제 경우에는 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)


답변

Psycopg 2.7의 새로운 execute_values방법 :

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에서 그것을하는 pythonic 방법 :

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')]

이미 정확한 형식으로되어 있습니다.

  1. values의 구문 insert절에서와 같은 기록의 목록을 기대

    insert into t (a, b) values (1, 'x'),(2, 'y')

  2. PsycopgPython 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)])

이전 답변 :

여러 행을 삽입하려면 다중 행 VALUES구문을 execute()사용하면 psycopg2를 사용하는 것보다 약 10 배 빠릅니다 executemany(). 실제로 executemany()많은 개별 INSERT진술을 실행 합니다.

@ ant32의 코드는 Python 2에서 완벽하게 작동하지만 Python 3에서는 cursor.mogrify()바이트를 반환 cursor.execute()하고 바이트 또는 문자열을 취하며 인스턴스를 ','.join()기대 str합니다.

따라서 Python 3에서는 다음을 추가하여 @ ant32 코드를 수정해야 할 수도 있습니다 .decode('utf-8').

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) 


답변

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'))

이 작은 크기의 args의 경우 속도 차이가 크지 않지만 수천 개 이상의 행을 처리 할 때 속도가 크게 향상됩니다. 또한 거대한 쿼리 문자열을 작성하는 것보다 메모리 효율성이 더 뛰어납니다. 반복자는 메모리에 한 번에 하나의 입력 레코드 만 보유합니다.이 시점에서 쿼리 프로세스를 빌드하여 Python 프로세스 또는 Postgres의 메모리가 부족합니다.


답변

Postgresql.org 의 Psycopg2 튜토리얼 페이지의 스 니펫 (아래 참조) :

마지막으로 보여 드리고 싶은 것은 사전을 사용하여 여러 행을 삽입하는 방법입니다. 다음과 같은 경우 :

namedict = ({"first_name":"Joshua", "last_name":"Drake"},
            {"first_name":"Steven", "last_name":"Foo"},
            {"first_name":"David", "last_name":"Bar"})

다음을 사용하여 사전 내에 세 개의 행을 모두 쉽게 삽입 할 수 있습니다.

cur = conn.cursor()
cur.executemany("""INSERT INTO bar(first_name,last_name) VALUES (%(first_name)s, %(last_name)s)""", namedict)

많은 코드를 저장하지는 않지만 확실히 좋아 보입니다.


답변

이러한 모든 기술을 Postgres 용어에서 ‘확장 삽입’이라고하며 2016 년 11 월 24 일 현재 psychopg2의 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;)을 사용해야합니다.


답변

나는 몇 년 동안 위의 ant32의 대답을 사용해 왔습니다. 그러나 mogrify바이트 문자열을 반환 하기 때문에 파이썬 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)