Мне нужно вставить несколько строк одним запросом (количество строк не является постоянным), поэтому мне нужно выполнить запрос, подобный этому:
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)
но я хочу какой-нибудь более простой способ.
Переведено автоматически
Ответ 1
Я создал программу, которая вставляет несколько строк на сервер, расположенный в другом городе.
Я обнаружил, что использование этого метода было примерно в 10 раз быстрее, чем executemany. В моем случае tup представляет собой кортеж, содержащий около 2000 строк. При использовании этого метода потребовалось около 10 секунд:
args_str = ','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) forx 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)
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')]
тогда это уже точно в требуемом формате, как
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)
Или просто тестирование того, что будет отправлено на сервер
Эта реализация была добавлена в psycopg2 в версии 2.7 и называется 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() примерно в 10 раз быстрее, чем с помощью psycopg2 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') forx 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) forx in tup) cur.execute(b"INSERT INTO table VALUES " + args_bytes)
Ответ 4
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'))
Для аргументов такого тривиального размера это не сильно повлияет на скорость, но я вижу значительное ускорение при работе с тысячами + строк. Это также будет более эффективно использовать память, чем создание гигантской строки запроса. Итератор будет хранить в памяти только одну входную запись одновременно, и в какой-то момент у вас закончится память в вашем процессе Python или в Postgres при создании строки запроса.