SQLAlchemy, Psycopg2 и Postgresql КОПИРОВАТЬ

Похоже, в Psycopg есть специальная команда для выполнения COPY:

psycopg2 COPY с использованием cursor.copy_from () зависает с большими входными данными

Есть ли способ получить доступ к этой функции с помощью SQLAlchemy?


person EoghanM    schedule 29.10.2012    source источник


Ответы (6)


Не похоже.

Возможно, вам придется просто использовать psycopg2, чтобы раскрыть эту функциональность, и отказаться от возможностей ORM. Думаю, я все равно не вижу преимущества ORM в такой операции, поскольку это прямая массовая вставка и работа с отдельными объектами в стиле ORM не имеет большого смысла.

person swasheck    schedule 29.10.2012
comment
super - может попасть в psycopg через engine.raw_connection () - person EoghanM; 29.10.2012

принятый ответ правильный, но если вы хотите больше, чем просто комментарий EoghanM, для меня сработало следующее: КОПИРОВАНИЕ таблицы в CSV ...

from sqlalchemy import sessionmaker, create_engine

eng = create_engine("postgresql://user:pwd@host:5432/db")
ses = sessionmaker(bind=engine)

dbcopy_f = open('/tmp/some_table_copy.csv','wb')

copy_sql = 'COPY some_table TO STDOUT WITH CSV HEADER'

fake_conn = eng.raw_connection()
fake_cur = fake_conn.cursor()
fake_cur.copy_expert(copy_sql, dbcopy_f)

sessionmaker не требуется, но если у вас есть привычка создавать движок и сеанс одновременно, чтобы использовать raw_connection, вам нужно разделить их (если нет способа получить доступ к движку через объект сеанса, который я не знаю). Строка sql, предоставленная для copy_expert, также не единственный способ сделать это, существует базовая функция copy_to, которую вы можете использовать с подмножеством параметров, которые вы могли бы передать в обычный запрос COPY TO. Общая производительность команды мне кажется быстрой, копируя таблицу из ~ 20000 строк.

http://initd.org/psycopg/docs/cursor.html#cursor.copy_to http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.Engine.raw_connection

person dnfehren    schedule 27.02.2014
comment
Это была потрясающая находка. Это сократило мое время сохранения данных с 8 часов + за ночь до 4 минут или около того. Боже мой! - person trench; 05.07.2016
comment
Сработало у меня, но в конце пришлось сделать fake_conn.commit() - person Michael; 06.01.2017

Если ваш движок настроен со строкой подключения psycopg2 (которая используется по умолчанию, поэтому либо "postgresql://...", либо "postgresql+psycopg2://..."), вы можете создать курсор psycopg2 из сеанса SQL Alchemy, используя

cursor = session.connection().connection.cursor()

который вы можете использовать для выполнения

cursor.copy_from(...)

Курсор будет активен в той же транзакции, что и текущий сеанс. Если произойдет commit или rollback, при дальнейшем использовании курсора с throw psycopg2.InterfaceError вам придется создать новый.

person dtheodor    schedule 17.06.2014
comment
Проголосовали за то, чтобы показать, как на самом деле получить курсор, используя традиционный сеанс. - person Pedro Lourenço; 12.03.2019
comment
Есть ли способ сделать это с помощью диалекта asyncpg? В самом драйвере есть метод connection.copy_from_table, но он недоступен для курсора. Если я получу необработанное соединение, как в других ответах, оно не будет в той же транзакции. - person tommyip; 20.01.2021

Ты можешь использовать:

def to_sql(engine, df, table, if_exists='fail', sep='\t', encoding='utf8'):
    # Create Table
    df[:0].to_sql(table, engine, if_exists=if_exists)

    # Prepare data
    output = cStringIO.StringIO()
    df.to_csv(output, sep=sep, header=False, encoding=encoding)
    output.seek(0)

    # Insert data
    connection = engine.raw_connection()
    cursor = connection.cursor()
    cursor.copy_from(output, table, sep=sep, null='')
    connection.commit()
    cursor.close()

Я вставляю 200000 строк за 5 секунд вместо 4 минут

person Fabien Vauchelles    schedule 25.05.2017
comment
Не могли бы вы подробнее рассказать, что такое объект df? - person EoghanM; 14.06.2017
comment
df - это фреймворк pandas - person Fabien Vauchelles; 18.06.2017
comment
Это золото! (с использованием pandas, sqlalchemy и postgres) - person Sander van den Oord; 05.04.2018
comment
Я знаю, что это было давно, но не могли бы вы объяснить, почему вы используете pandas to_sql И sqlalchemy copy_from? Разве to_sql не создает таблицу И не записывает в нее содержимое df? если да, то зачем тогда вставлять данные после еще раз? Я использую это для массовой вставки данных, но в целевой таблице есть серийный идентификатор в качестве индекса, и, как бы я ни пытался, я не могу просто вставить данные без индекса (без сообщения об ошибке, что отсутствует данные столбца) - person JustinMoser; 05.07.2018
comment
@JustinMoser Как это часто бывает, pandas.DataFrame имеет to_sql, но этот метод здесь не используется. Вместо этого для сохранить df в текстовом потоке в памяти, output, который затем передается psycopg2 с помощью _7 _ . - person Jonas Dahlbæk; 24.07.2018
comment
@ JonasDahlbæk Вскоре после того, как я оставил этот комментарий, я понял, что использование to_sql в первой строке просто записывает заголовки в целевую таблицу. Это правильно? - person JustinMoser; 09.08.2018
comment
@JustinMoser О, я полностью пропустил эту строчку! Да, эта строка CREATE TABLE ... будет содержать соответствующие столбцы (или завершится ошибкой, если таблица уже существует), но не будет вставлять какие-либо данные. - person Jonas Dahlbæk; 09.08.2018
comment
Есть ли способ просто создать модель SQLAlchemy вместо таблицы (мне нравится ваш код для создания исходной таблицы без данных). Я пытаюсь использовать Alembic и хочу создать наиболее вероятную модель, которую я затем могу отредактировать. - person trench; 05.01.2019

Если вы начинаете с SQLAlchemy, вам нужно сначала перейти к механизму подключения (также известному по имени свойства bind в некоторых объектах SQLAlchemy):

engine = create_engine('postgresql+psycopg2://myuser:password@localhost/mydb')
# or 
engine = session.engine
# or any other way you know to get to the engine

Из движка вы можете изолировать соединение psycopg2:

# get a psycopg2 connection
connection = engine.connect().connection

# get a cursor on that connection
cursor = connection.cursor()

Вот несколько шаблонов для оператора COPY, которые можно использовать с cursor.copy_expert(), более полным и гибким вариантом, чем copy_from() или copy_to(), как указано здесь: https://www.psycopg.org/docs/cursor.html#cursor.copy_expert.

# to dump to a file
dump_to = """
COPY mytable 
TO STDOUT
WITH (
    FORMAT CSV,
    DELIMITER ',',
    HEADER
);
"""

# to copy from a file:
copy_from = """
COPY mytable 
FROM STDIN
WITH (
    FORMAT CSV,
    DELIMITER ',',
    HEADER
);
"""

Узнайте, что означают приведенные выше параметры и другие, которые могут быть интересны в вашей конкретной ситуации https://www.postgresql.org/docs/current/static/sql-copy.html.

ВАЖНОЕ ПРИМЕЧАНИЕ. Ссылка на документацию cursor.copy_expert() указывает на использование STDOUT для записи в файл и STDIN для копирования из файла. Но если вы посмотрите на синтаксис в руководстве PostgreSQL, вы заметите, что вы также можете указать файл для записи или из него непосредственно в операторе COPY. Не делайте этого, вы, скорее всего, просто напрасно тратите свое время, если вы не работаете с правами root (кто запускает Python как root во время разработки?). Просто делайте то, что указано в документации psycopg2, и укажите STDIN или STDOUT в своем заявлении с помощью cursor.copy_expert() , все должно быть хорошо.

# running the copy statement
with open('/path/to/your/data/file.csv') as f:
     cursor.copy_expert(copy_from, file=f)

# don't forget to commit the changes.
connection.commit()
person Michael Ekoka    schedule 21.12.2017

Вам не нужно опускаться до psycopg2, использовать raw_connection или курсор.

Просто выполните sql как обычно, вы даже можете использовать параметры привязки с _ 1_:

engine.execute(text('''copy some_table from :csv
                       delimiter ',' csv'''
                   ).execution_options(autocommit=True),
               csv='/tmp/a.csv')

Вы можете отказаться от execution_options(autocommit=True), если этот PR будет принят

person berdario    schedule 19.12.2015
comment
Просто сообщите, что это ваш репозиторий кода, который выглядит как - person Drew; 19.12.2015
comment
Нет, ссылка на этот PR не является моим репозиторием, это обычный / официальный репозиторий sqlalchemy. OTOH, PR явно мой, это не секрет: я использую одно и то же имя пользователя как в stackoverflow, так и в bitbucket. Как бы то ни было, я наткнулся на этот вопрос, исследуя примеры этого изменения, и все, что я написал, фактически верно. Я мог бы избежать привязки PR, но (в случае, если он будет принят), тогда этот ответ предложит устаревший фрагмент, если только я или кто-то не забуду обновить его постфактум - person berdario; 21.12.2015
comment
Добавление примечания к старому ответу, но копирование имени файла требует привилегий суперпользователя базы данных, что может быть не очень желательно. - person Ilja Everilä; 13.08.2018