sourcecode

psycopg2: 하나의 쿼리로 여러 행을 삽입합니다.

copyscript 2023. 1. 30. 22:15
반응형

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)

하지만 좀 더 간단한 방법을 원해요

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

하는 것이 되었습니다.executemany ★★★★★★★★★★★★★★★★★★★★★★★★★.tup2000년입니다.을 사용하면 걸렸습니다.

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의 새로운 방법:

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에서는 버마적인 방법:

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. valuesinsert은 기록합니다.

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

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

해서 에에 the and에 and and and the the 。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 업데이트:

인 래식 theexecutemany()는 @ant32의 실장(「module」이라고 불린다)보다 약 60배 느립니다.https://www.postgresql.org/message-id/20170130215151.GA7081%40deb76.aryehleib.com

2.되어 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()보다 약 .executemany()이지, . . . . . . . . . . . . . 。executemany()을 할 수 있습니다.INSERT★★★★★★★★★★★★★★★★★★.

2ant32에서는 하게 동작합니다. Python에서는 지지에 에에3 but butcursor.mogrify()cursor.execute() 스트링 중 를 사용합니다.또, 「바이트 또는 스트링」은 「바이트 또는 「바이트 또는 스트링」이 됩니다.','.join()str★★★★★★ 。

Python 에서는 "Python 3"을 추가하여 @..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은 대량 삽입을 위한 지금까지 발견된 솔루션 중 가장 빠른 솔루션입니다.여기 Iterator File이라는 클래스를 포함하는 Gist를 소개합니다.이 클래스는 문자열을 생성하는 반복기를 파일처럼 읽을 수 있도록 합니다.제너레이터 식을 사용하여 각 입력 레코드를 문자열로 변환할 수 있습니다.그래서 해결책은

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

이 작은 크기의 Arg라면 속도 차이는 크지 않지만 수천 개 이상의 행을 처리할 때 속도가 크게 향상됩니다.또한 대용량 쿼리 문자열을 작성하는 것보다 메모리 효율이 높습니다.반복기는 한 번에 하나의 입력 레코드만 메모리에 저장할 수 있으며, 쿼리 문자열을 구축하면 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의 답변을 사용하고 있습니다. 나는 한다는 것을 왜냐하면 비단뱀 3의 오류이기 때문이다.mogrify이치노

코드 python 3을 호환하기 위한 간단한 솔루션은 bythe 문자열로 명시적으로 변환하는 것입니다.

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)

execute any accept tuples 배열

https://www.postgresqltutorial.com/postgresql-python/insert/

    """ array of tuples """
    vendor_list = [(value1,)]

    """ insert multiple vendors into the vendors table  """
    sql = "INSERT INTO vendors(vendor_name) VALUES(%s)"
    conn = None
    try:
        # read database configuration
        params = config()
        # connect to the PostgreSQL database
        conn = psycopg2.connect(**params)
        # create a new cursor
        cur = conn.cursor()
        # execute the INSERT statement
        cur.executemany(sql,vendor_list)
        # commit the changes to the database
        conn.commit()
        # close communication with the database
        cur.close()
    except (Exception, psycopg2.DatabaseError) as error:
        print(error)
    finally:
        if conn is not None:
            conn.close()

cursor.copy from 솔루션 @jopseph에 의해 제공됩니다.sheedy(https://stackoverflow.com/users/958118/joseph-sheedy) 위의 https://stackoverflow.com/a/30721460/11100064)는 정말 빠른 속도입니다.

그러나 그가 제시한 예는 필드 수가 많은 레코드에는 일반적으로 사용할 수 없기 때문에 올바르게 사용하는 방법을 파악하는 데 시간이 걸렸습니다.

은 다음과(Iterator File)로 인스턴스화해야 .r는 딕트 목록입니다.각 딕트는 레코드입니다).

    f = IteratorFile("{0}\t{1}\t{2}\t{3}\t{4}".format(r["id"],
        r["type"],
        r["item"],
        r["month"],
        r["revenue"]) for r in records)

하기 위해 먼저 플레이스 홀더를 행 .「 」 。"{}\t{}\t{}....\t{}"그런 다음 사용.format()필드 값을 입력합니다.*list(r.values())) for r in records:

        line = "\t".join(["{}"] * len(records[0]))

        f = IteratorFile(line.format(*list(r.values())) for r in records)

여기서 gist의 기능을 완성합니다.

질문이 게시된 이후 execute_module이 psycopg2에 추가되었습니다.

execute_values보다 빠릅니다.

또 하나의 적절하고 효율적인 접근법은 json 객체의 배열인 1개의 인수로 삽입할 행을 전달하는 것입니다.

예: 인수 통과:

[ {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을 지원하기에 충분히 새로운 것이어야 합니다.

SQL Chemy를 사용하는 경우, SQL Chemy는 단일 스테이트먼트에 대해 여러 행의 절을 생성할 수 있기 때문에 스트링을 수작업으로 만들 필요가 없습니다.

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)

@ant32부터

def myInsertManyTuples(connection, table, tuple_of_tuples):
    cursor = connection.cursor()
    try:
        insert_len = len(tuple_of_tuples[0])
        insert_template = "("
        for i in range(insert_len):
            insert_template += "%s,"
        insert_template = insert_template[:-1] + ")"

        args_str = ",".join(
            cursor.mogrify(insert_template, x).decode("utf-8")
            for x in tuple_of_tuples
        )
        cursor.execute("INSERT INTO " + table + " VALUES " + args_str)
        connection.commit()

    except psycopg2.Error as e:
        print(f"psycopg2.Error in myInsertMany = {e}")
        connection.rollback()

하나의 삽입 스테이트멘에 여러 행을 삽입하는 경우(ORM을 사용하지 않는 경우) 사전 목록을 사용하는 것이 가장 쉬운 방법입니다.다음은 예를 제시하겠습니다.

 t = [{'id':1, 'start_date': '2015-07-19 00:00:00', 'end_date': '2015-07-20 00:00:00', 'campaignid': 6},
      {'id':2, 'start_date': '2015-07-19 00:00:00', 'end_date': '2015-07-20 00:00:00', 'campaignid': 7},
      {'id':3, 'start_date': '2015-07-19 00:00:00', 'end_date': '2015-07-20 00:00:00', 'campaignid': 8}]

conn.execute("insert into campaign_dates
             (id, start_date, end_date, campaignid) 
              values (%(id)s, %(start_date)s, %(end_date)s, %(campaignid)s);",
             t)

보시는 바와 같이 쿼리는 1개만 실행됩니다.

INFO sqlalchemy.engine.base.Engine insert into campaign_dates (id, start_date, end_date, campaignid) values (%(id)s, %(start_date)s, %(end_date)s, %(campaignid)s);
INFO sqlalchemy.engine.base.Engine [{'campaignid': 6, 'id': 1, 'end_date': '2015-07-20 00:00:00', 'start_date': '2015-07-19 00:00:00'}, {'campaignid': 7, 'id': 2, 'end_date': '2015-07-20 00:00:00', 'start_date': '2015-07-19 00:00:00'}, {'campaignid': 8, 'id': 3, 'end_date': '2015-07-20 00:00:00', 'start_date': '2015-07-19 00:00:00'}]
INFO sqlalchemy.engine.base.Engine COMMIT

마지막으로 SQLalchemy 1.2 버전에서는 이 새로운 구현이 psycopg2를 사용하기 위해 추가되었습니다.use_batch_mode=True로 엔진을 초기화할 때 executemany 대신 extra.batch_model()을 사용합니다.

engine = create_engine(
    "postgresql+psycopg2://scott:tiger@host/dbname",
    use_batch_mode=True)

http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109

SQLalchmey를 사용하면 SQL과 psycopg2의 다른 조합과 직접 SQL을 함께 사용할 필요가 없습니다.

aiopg 사용 - 아래 스니펫은 완벽하게 작동합니다.

    # items = [10, 11, 12, 13]
    # group = 1
    tup = [(gid, pid) for pid in items]
    args_str = ",".join([str(s) for s in tup])
    # insert into group values (1, 10), (1, 11), (1, 12), (1, 13)
    yield from cur.execute("INSERT INTO group VALUES " + args_str)

언급URL : https://stackoverflow.com/questions/8134602/psycopg2-insert-multiple-rows-with-one-query

반응형