SQLAlchemy、Psycopg2和Postgresql复制

SQLAlchemy、Psycopg2和Postgresql复制,postgresql,sqlalchemy,psycopg2,Postgresql,Sqlalchemy,Psycopg2,看起来Psycopg有一个自定义命令用于执行: 有没有办法使用SQLAlchemy从访问此功能? 您可能需要使用psycopg2来公开此功能,并放弃ORM功能。我想我并没有真正看到ORM在这样的操作中的好处,因为它是一个直接的批量插入,并且处理单个对象。一个简单的ORM并没有真正意义。接受的答案是正确的,但是如果你想要的不仅仅是EoghanM的评论,那么下面的内容对于我复制一个表到CSV from sqlalchemy import sessionmaker, create_engine e

看起来Psycopg有一个自定义命令用于执行:

有没有办法使用SQLAlchemy从访问此功能?


您可能需要使用psycopg2来公开此功能,并放弃ORM功能。我想我并没有真正看到ORM在这样的操作中的好处,因为它是一个直接的批量插入,并且处理单个对象。一个简单的ORM并没有真正意义。

接受的答案是正确的,但是如果你想要的不仅仅是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\u连接
,则需要将它们分开(除非有某种方式可以通过会话对象访问引擎,我不知道)。提供给
copy_expert
的sql字符串也不是唯一的方法,有一个基本的
copy_to
函数,可以与参数子集一起使用,这些参数可以通过正常的
copy
进行查询。对我来说,该命令的总体性能似乎很快,复制了一个约20000行的表


如果您的引擎配置了psycopg2连接字符串(这是默认值,因此
“postgresql://...“
”postgresql+psycopg2://…”
),您可以使用从SQL炼金术会话创建psycopg2游标

cursor = session.connection().connection.cursor()
您可以使用它来执行

cursor.copy_from(...)

光标将在当前会话所在的同一事务中处于活动状态。如果发生
commit
rollback
,任何进一步使用光标的操作都会抛出
psycopg2.InterfaceError
,您必须创建一个新的指针。

您不需要下拉到psycopg2,使用原始连接或光标

只需像往常一样执行sql,您甚至可以将绑定参数用于:

如果将被接受,您可以删除执行选项(autocommit=True),您可以使用:

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

我在5秒钟而不是4分钟内插入200000行

如果您从SQLAlchemy开始,您需要首先进入连接引擎(在某些SQLAlchemy对象上也可以通过属性名
bind
):

从引擎中,您可以隔离psycopg2连接:

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

# get a cursor on that connection
cursor = connection.cursor()
以下是复制语句的一些模板,用于
游标。COPY\u expert()
,这是一个比
COPY\u from()
COPY\u to()
更完整、更灵活的选项,如下所示:

查看上述选项的含义以及您的特定情况可能感兴趣的其他选项

重要提示:指向cursor.copy_expert()文档的链接表示使用标准输出写入文件,使用标准输入从文件复制。但是,如果您查看PostgreSQL手册上的语法,您会注意到,您还可以在COPY语句中直接指定要写入或从中写入的文件。不要这样做,如果您不是以root用户身份运行(谁在开发过程中以root用户身份运行Python?),那么您很可能只是在浪费时间。只需按照psycopg2文档中的指示操作,并在语句中使用
游标指定STDIN或STDOUT。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()

super-可以通过engine.raw_connection()访问psycopg,只需公开它是您的代码库,在beNo看来,链接为“此PR”的不是我的存储库,它是sqlalchemy的正常/官方存储库。当然,PR是我的,这不是秘密:我在stackoverflow和bitbucket上使用相同的用户名。无论如何,我在研究这种变化的例子时偶然发现了这个问题,我所写的一切事实上都是正确的。我本可以避免链接PR,但是(以防被接受)这个答案会提示一个过时的代码片段,除非我或其他人会记得在事件发生后更新它,在旧答案中添加注释,但是复制命名文件需要数据库超级用户权限,这可能不是很理想。这是一个了不起的发现。这将我节省时间的数据从8小时+一夜减少到4分钟左右。天哪!为我工作,但我不得不在结尾处做
fake_conn.commit()
你能详细介绍一下
df
对象是什么吗?df是一个数据框架这是金子!(使用pandas和sqlalchemy以及postgres)我知道已经有一段时间了,但是你能解释一下为什么要使用pandas来复制sql和sqlalchemy吗?sql不创建表并将df的内容写入表吗?如果是这样,为什么还要再次插入数据?我用它来大容量插入数据,但目标表有一个序列id作为索引,无论我用哪种方法,似乎都无法在没有索引的情况下插入数据(而不会得到一个错误,说缺少列数据)@JustinMoser,
pandas.DataFrame
有一个方法,但这里没有使用这种方法。相反,该方法用于将
df
存储在,
输出中,然后使用.Upvoted将其传递给
psycopg2
,以显示如何使用传统会话实际获取光标。是否有办法使用asyncpg方言实现这一点?驱动程序本身有一个
连接。从\u table
方法复制\u,但该方法在光标上不可用。如果我得到一个原始连接,就像其他答案一样,它将不在同一个事务中。
# get a psycopg2 connection
connection = engine.connect().connection

# get a cursor on that connection
cursor = connection.cursor()
# 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
);
"""
# 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()