Pandas: 使用多行插入在 to_sql 上通过高延迟连接实现大规模加速

创建于 2014-12-01  ·  48评论  ·  资料来源: pandas-dev/pandas

我一直在尝试使用 pandas-0.15.1、oursql-0.9.3.1 和 sqlalchemy-0.9.4 将 ~30k 行插入到 mysql 数据库中。 因为这台机器与我隔着大西洋,调用data.to_sql需要 1 小时以上的时间来插入数据。 在使用wireshark进行检查时,问题是它为每一行发送一个插入,然后在发送下一个之前等待ACK,长话短说,ping时间让我很生气。

但是,按照SQLAlchemy的说明,我改变了

def _execute_insert(self, conn, keys, data_iter):
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement(), data)

def _execute_insert(self, conn, keys, data_iter):
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement().values(data))

整个操作不到一分钟就完成了。 (为了节省您的点击次数,不同之处在于多次调用insert into foo (columns) values (rowX)和一次大量调用insert into foo (columns) VALUES (row1), (row2), row3) )。 考虑到人们使用 pandas 插入大量数据的频率,这感觉像是一个巨大的胜利,如果能被更广泛地包含在内,那就太好了。

一些挑战:

  • 并非每个数据库都支持多行插入(SQLite 和 SQLServer 过去不支持,但现在支持)。 我不知道如何通过 SQLAlchemy 检查这个
  • 我使用的 MySQL 服务器不允许我一次插入所有数据,我必须设置块大小(5k 工作正常,但我猜全部 30k 太多了)。 如果我们将此设置为默认插入,大多数人将不得不添加一个块大小(这可能很难计算,因为它可能由服务器的最大数据包大小决定)。

最简单的方法是在to_sql函数中添加一个multirow=布尔参数(默认False ),然后让用户负责设置块大小,但也许有更好的方法?

想法?

IO SQL Performance

最有用的评论

我们已经找到了猴子补丁的方法——可能对其他人有用。 在导入熊猫之前有这个代码。

from pandas.io.sql import SQLTable

def _execute_insert(self, conn, keys, data_iter):
    print "Using monkey-patched _execute_insert"
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement().values(data))

SQLTable._execute_insert = _execute_insert

所有48条评论

这似乎是合理的。 感谢您对此进行调查!

对于实现,这将取决于 sqlalchemy 如何处理不支持此功能的数据库风格(我目前无法对此进行测试,但似乎 sqlalchemy 引发了错误(例如 http://stackoverflow.com/questions/ 23886764/multiple-insert-statements-in-mssql-with-sqlalchemy). 另外,如果它有很多人必须设置 chunksize 的后果,这确实不是一个好主意,默认(除非我们设置默认情况下,chunksize 为一个值)。
因此,添加关键字似乎更好。

@artemyk @mangecoeur @hayd @danielballan

显然 SQLAlchemy 有一个标志dialect.supports_multivalues_insert (参见例如 http://pydoc.net/Python/SQLAlchemy/0.8.3/sqlalchemy.sql.compiler/ ,在其他版本中可能称为supports_multirow_insert ,https ://www.mail-archive.com/[email protected]/msg202880.html)。

由于这有可能大大加快插入速度,而且我们可以轻松检查支持,我想也许我们可以默认这样做,并将 chunksize 设置为默认值(例如 16kb 块......不确定是什么在大多数情况下太大)。 如果多行插入失败,我们可以抛出异常建议降低块大小?

现在我只需要说服 SQLAlchemy 人员在 SQL Server >2005 上将supports_multivalues_insert设置为 true (我将它破解到代码中,它工作正常,但默认情况下不启用)。

在更主题的注释中,我认为块大小可能很棘手。 在我的 mysql 设置上(我可能配置为允许大数据包),我可以设置 chunksize=5000,在我的 SQLServer 设置上,500 太大,但 100 工作正常。 然而,这项技术的大部分好处可能来自一次插入 1 行到 100 行,而不是从 100 到 1000 行。

如果chunksize=None的意思是“自适应地选择一个块大小”怎么办? 尝试 5000、500、50、1 之类的值。用户可以通过指定块大小来关闭它。 如果这些尝试的开销太大,我喜欢@maxgrenderjones 的建议: chunksize=10是比chunksize=1更好的默认值。

在最后一条评论“ chunksize=10chunksize=1更好的默认值”-> 我认为这并不完全正确。 目前的情况是做_one_执行由多行单行插入语句组成的语句(不是1的块大小),而chunksize=10意味着每次执行一个多行执行很多语句插入。
而且我不知道这是否一定会更快,但这在很大程度上取决于情况。 例如,使用当前代码和本地 sqlite 数据库:

In [4]: engine = create_engine('sqlite:///:memory:') #, echo='debug')

In [5]: df = pd.DataFrame(np.random.randn(50000, 10))

In [6]: %timeit df.to_sql('test_default', engine, if_exists='replace')
1 loops, best of 3: 956 ms per loop

In [7]: %timeit df.to_sql('test_default', engine, if_exists='replace', chunksize=10)
1 loops, best of 3: 2.23 s per loop

但是当然这不使用多行功能

我们已经找到了猴子补丁的方法——可能对其他人有用。 在导入熊猫之前有这个代码。

from pandas.io.sql import SQLTable

def _execute_insert(self, conn, keys, data_iter):
    print "Using monkey-patched _execute_insert"
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement().values(data))

SQLTable._execute_insert = _execute_insert

也许我们可以从通过一个新的multirow=True关键字(现在默认为 False)添加这个功能开始,然后我们以后总能看看我们是否可以默认启用它?

@maxgrenderjones @nhockham有兴趣做一个公关来添加这个吗?

@jorisvandenbossche我认为开始添加关键字参数来解决特定的性能配置文件是有风险的。 如果您可以保证它在所有情况下都更快(如果有必要,让它根据输入确定最佳方法),那么您根本不需要标志。

不同的数据库设置可能有不同的性能优化(不同的数据库性能配置文件、本地与网络、大内存与快速 SSD 等),如果您开始为每个设置添加关键字标志,就会变得一团糟。

我建议创建 SQLDatabase 和 SQLTable 的子类来解决特定于性能的实现,它们将通过面向对象的 API 使用。 也许可以添加一个“后端切换”方法,但坦率地说,使用 OO api 非常简单,所以这对于已经是一个专门的用例来说可能是矫枉过正。

我创建了一个用于将大型数据集加载到 Postgres 的子类(实际上将数据保存到 CSV 然后使用内置的非标准 COPY FROM sql 命令比使用插入要快得多,请参阅 https://gist.github。 com/mangecoeur/1fbd63d4758c2ba0c470#file-pandas_postgres-py)。 要使用它,您只需执行PgSQLDatabase(engine, <args>).to_sql(frame, name,<kwargs>)

仅供参考,我尝试使用多行功能运行@jorisvandenbossche (12 月 3 日发布)的代码。 它慢了很多。 所以这里的速度权衡不是微不足道的:

In [4]: engine = create_engine('sqlite:///:memory:') #, echo='debug')

In [5]: df = pd.DataFrame(np.random.randn(50000, 10))

In [6]: 

In [6]: %timeit df.to_sql('test_default', engine, if_exists='replace')
1 loops, best of 3: 1.05 s per loop

In [7]: 

In [7]: from pandas.io.sql import SQLTable

In [8]: 

In [8]: def _execute_insert(self, conn, keys, data_iter):
   ...:         data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
   ...:         conn.execute(self.insert_statement().values(data))
   ...:     

In [9]: SQLTable._execute_insert = _execute_insert

In [10]: 

In [10]: reload(pd)
Out[10]: <module 'pandas' from '/usr/local/lib/python2.7/site-packages/pandas/__init__.pyc'>

In [11]: 

In [11]: %timeit df.to_sql('test_default', engine, if_exists='replace', chunksize=10)
1 loops, best of 3: 9.9 s per loop

另外,我同意添加关键字参数是有风险的。 但是,多行功能似乎非常基本。 此外,'monkey-patching' 对 API 更改可能并不比关键字参数更健壮。

正如我所怀疑的那样。 Monkey patching 不是我建议的解决方案 - 而是我们发布了许多面向性能的子类,知情的用户可以通过 OO 接口使用它们(以避免加载具有太多选项的功能 api)

- - -原始信息 - - -
来自:“Artemy Kolchinsky” [email protected]
发送:‎26/‎02/‎2015 17:13
收件人:“pydata/pandas” [email protected]
抄送:“mangecoeur”乔恩。 [email protected]
主题:回复:[pandas] 使用多行插入在 to_sqlover 高延迟连接上实现大规模加速(#8953)

仅供参考,我尝试使用多行功能运行@jorisvandenbossche (12 月 3 日发布)的代码。 它慢了很多。 所以这里的速度权衡不是微不足道的:
在 [4] 中:engine = create_engine('sqlite:///:memory:') #, echo='debug')

在 [5] 中:df = pd.DataFrame(np.random.randn(50000, 10))

在[6]中:

在 [6] 中:%timeit df.to_sql('test_default', engine, if_exists='replace')
1 个循环,最好的 3 个:每个循环 1.05 秒

在[7]中:

[7]中:从pandas.io.sql导入SQLTable

在[8]中:

在 [8] 中: def _execute_insert(self, conn, keys, data_iter):
...: data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
...: conn.execute(self.insert_statement().values(data))
...:

在 [9] 中:SQLTable._execute_insert = _execute_insert

在[10]中:

在 [10] 中:重新加载(pd)
输出[10]:

在[11]中:

在 [11] 中:%timeit df.to_sql('test_default', engine, if_exists='replace', chunksize=10)
1 个循环,最好的 3 个:每个循环 9.9 秒
另外,我同意添加关键字参数是有风险的。 但是,多行功能似乎非常基本。 此外,'monkey-patching' 对 API 更改可能并不比关键字参数更健壮。

直接回复此邮件或在 GitHub 上查看。

根据最初的票标题,我认为这种方法在所有情况下都不会更可取,所以我不会将其设为默认值。 但是,没有它,pandas to_sql对我来说无法使用,所以对我来说继续请求更改非常重要。 (当我升级我的熊猫版本时,这也成为我改变的第一件事)。 至于合理的chunksize值,我认为没有一个真正的n ,因为数据包的大小将取决于难以预测的方式有多少列(以及其中的内容) . 不幸的是,如果您将chunksize设置得太高,SQLServer 会失败并显示一条看起来完全不相关(但实际上并非如此)的错误消息(这可能是为什么除了 SQLAlchemy 中的补丁之外没有打​​开多行插入的原因),但是它适用于mysql 。 用户可能需要尝试确定n的值可能会导致可接受的大数据包大小(无论他们的支持数据库是什么)。 让 pandas 选择n可能会使我们在实现细节上比我们想要的更深入(即与最大可能抽象 SQLALchemy 方法相反的方向)

简而言之,我的建议是将其添加为关键字,并附上一些关于如何使用它的有用评论。 这不是第一次使用关键字来选择实现(参见:http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html),但这可能不是t 最好的例子,因为我不知道raw=是什么意思,即使阅读了解释!

我注意到它也消耗了大量的内存。 就像一个有 700,000 行和 301 列的 1.6+ GB DataFrame 在插入过程中需要将近 34 GB! 这就像过度低效。 关于为什么会这样的任何想法? 这是一个屏幕剪辑:

image

嗨,大家好,
在这个问题上有什么进展吗?

我尝试使用 to_sql 插入大约 200K 行,但它需要很长时间并且消耗大量内存! 使用 chunksize 有助于内存,但速度仍然很慢。

我的印象是,查看 MSSQL DBase 跟踪时,插入实际上是一次执行的。

现在唯一可行的方法是转储到共享文件夹上的 csv 文件并使用 BULK INSERT。 但它非常烦人和不雅!

@andreacassioli您可以使用odo通过中间 CSV 文件将 DataFrame 插入 SQL 数据库。 请参阅将 CSV 加载到 SQL 数据库中。

我认为使用 ODBC 甚至无法BULK INSERT的性能。

@ostrokach谢谢,确实我现在正在使用 csv 文件。 如果我能接近,我会为了简单而牺牲一点时间!

我认为这可能对某人有所帮助:
http://docs.sqlalchemy.org/en/latest/faq/performance.html#i -m-inserting-400-000-rows-with-the-orm-and-it-s-really-slow

@indera pandas 不使用 ORM,只使用 sqlalchemy Core(这是那里的文档条目建议用于大型插入的内容)

在此期间是否就如何解决这个问题达成共识? 我在 postgres 中插入了几百万行,这需要很长时间。 CSV / odo 是要走的路吗?

@russlamba解决此问题的一种实用方法就是批量上传。 虽然这是特定于数据库的人,所以我认为odopostgresl的解决方案(可能是mysql )。 对于像 sqlserver 这样的东西,你必须“自己做”(IOW 你必须写它)。

对于 sqlserver,我使用了带有 SQLAlchemy 实体的 FreeTDS 驱动程序(http://www.freetds.org/software.html 和 https://github.com/mkleehammer/pyodbc),这导致插入速度非常快(每个数据帧 20K 行) :

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()


class DemographicEntity(Base):
    __tablename__ = 'DEMOGRAPHIC'

    patid = db.Column("PATID", db.Text, primary_key=True)
    """
    patid = db.Column("PATID", db.Text, primary_key=True, autoincrement=False, nullable=True)
    birth_date = db.Column("BIRTH_DATE", db.Date)
    birth_time = db.Column("BIRTH_TIME", db.Text(5))
    sex = db.Column("SEX", db.Text(2))

def get_db_url(db_host, db_port, db_name, db_user, db_pass):
    params = parse.quote(
        "Driver={{FreeTDS}};Server={};Port={};"
        "Database={};UID={};PWD={};"
        .format(db_host, db_port, db_name, db_user, db_pass))
    return 'mssql+pyodbc:///?odbc_connect={}'.format(params)

def get_db_pool():
    """
    Create the database engine connection.
    <strong i="6">@see</strong> http://docs.sqlalchemy.org/en/latest/core/engines.html

    :return: Dialect object which can either be used directly
            to interact with the database, or can be passed to
            a Session object to work with the ORM.
    """
    global DB_POOL

    if DB_POOL is None:
        url = get_db_url(db_host=DB_HOST, db_port=DB_PORT, db_name=DB_NAME,
                         db_user=DB_USER, db_pass=DB_PASS)
        DB_POOL = db.create_engine(url,
                                   pool_size=10,
                                   max_overflow=5,
                                   pool_recycle=3600)

    try:
        DB_POOL.execute("USE {db}".format(db=DB_NAME))
    except db.exc.OperationalError:
        logger.error('Database {db} does not exist.'.format(db=DB_NAME))

    return DB_POOL


def save_frame():
    db_pool = get_db_pool()
    records = df.to_dict(orient='records')
    result = db_pool.execute(entity.__table__.insert(), records)

CSV / odo 是要走的路吗?

无论多行/块大小设置如何,我认为这个解决方案几乎总是会更快。

但是, @russlamba ,听到这样的多行关键字是否会改善您的情况总是很有趣。 请参阅例如https://github.com/pandas-dev/pandas/issues/8953#issuecomment -76139975 以轻松测试这一点。

我认为我们希望有一种方法来指定这一点已达成一致(不必更改默认值)。 因此,如果有人想为此进行 PR,那当然是受欢迎的。
只有一些关于如何添加此功能的讨论(新关键字与使用 OO api 的子类)。

@jorisvandenbossche我在上面链接的文档提到“或者,SQLAlchemy ORM 提供了批量操作方法套件,它提供了工作单元过程子部分的挂钩,以便发出具有少量 ORM 的核心级 INSERT 和 UPDATE 构造——基于自动化。”

我的建议是为to_sql实现一个特定于 sqlserver 的版本,它在后台使用 SQLAlchemy ORM 来加速,就像我上面发布的代码一样。

这是之前提出的。 你去的方式是实现一个pandas sql
为后端优化的类。 我过去发布了一个要点以供使用
postgres COPY FROM 命令更快。 然而类似的东西
现在可以在 odo 中使用,并且以更健壮的方式构建。 没有多少
恕我直言,从 odo 复制工作。

2017 年 3 月 7 日 00:53,“Andrei Sura” [email protected]写道:

@jorisvandenbossche https://github.com/jorisvandenbossche文档
我链接上面提到“或者,SQLAlchemy ORM 提供 Bulk
方法的操作套件,它提供了到子部分的钩子
工作流程单元,以便发出核心级别的 INSERT 和 UPDATE
具有少量基于 ORM 的自动化的构造。”

我的建议是为
“to_sql”在后台使用 SQLAlchemy 核心来加速。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/pandas-dev/pandas/issues/8953#issuecomment-284437587 ,
或使线程静音
https://github.com/notifications/unsubscribe-auth/AAtYVDXKLuTlsh9ycpMQvU5C0hs_RxuYks5rjCwBgaJpZM4DCjLh
.

还注意到您提到 sqlalchemy 可以改为核心。 除非有什么
变化很大,反正只用了sqlalchemy核心,没有orm。 如果你
想要比使用核心更快,你必须去更低的级别,db
具体优化

2017 年 3 月 7 日 00:53,“Andrei Sura” [email protected]写道:

@jorisvandenbossche https://github.com/jorisvandenbossche文档
我链接上面提到“或者,SQLAlchemy ORM 提供 Bulk
方法的操作套件,它提供了到子部分的钩子
工作流程单元,以便发出核心级别的 INSERT 和 UPDATE
具有少量基于 ORM 的自动化的构造。”

我的建议是为
“to_sql”在后台使用 SQLAlchemy 核心来加速。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/pandas-dev/pandas/issues/8953#issuecomment-284437587 ,
或使线程静音
https://github.com/notifications/unsubscribe-auth/AAtYVDXKLuTlsh9ycpMQvU5C0hs_RxuYks5rjCwBgaJpZM4DCjLh
.

这是否得到修复/照顾? 截至目前,将 pandas 数据帧插入 SQL 数据库非常慢,除非它是一个玩具数据帧。 让我们决定一个解决方案并推动它?

@dfernan如上所述,您可能想查看odo 。 使用中间 CSV 文件总是比通过 sqlalchemy 快几个数量级,不管这里发生了什么样的改进......

@ostrokach ,我不相信 odo 的行为是典型的 Pandas 用户想要的。 对于大多数分析人员来说,通过 ODBC 进行多行插入可能已经足够快了。

为自己说话,我只花了几个小时从上面的猴子补丁切换到 odo。 普通的 pandas 运行时间是 10 多个小时,RBAR。 猴子补丁在同一数据集上运行 2。
正如预期的那样,odo/CSV 路线更快,但还不足以让它值得付出努力。 我摆弄了我不太关心的 CSV 转换问题,所有这些都是为了避免使用猴子补丁。 我将 250K 行从 ~10 mysql 和 PG DB 导入 Postgres 的公共区域,用于 NLP 分析。

我非常熟悉 odo 支持的批量加载方法。 我已经使用它们多年了,我从 CSV 数据开始。 它们有一些主要限制:

  1. 对于 df->CSV->Postgres 的情况,需要 shell 访问和 scp 步骤来获取 PG 主机上的 CSV。 看起来@mangecoeur已经通过到 STDIN 的流解决了这个问题。
  2. 出于我的目的(250K 行评论,文本内容中有很多特殊情况),我努力让 CSV 参数正确。 我不希望性能提升到足以继续投资于此。

我切换回补丁,这样我就可以开始分析工作了。

我同意@jorisvandenbossche ,@maxgrenderjones。 选择此选项的选项(不是默认选项)将非常有用。 @artemyk关于 dialect.supports_multivalues_insert 的观点甚至可能使它成为一个合理的默认值。

如果这能推动这件事,我很高兴提交 PR。

只是为了增加我对 odo 的经验,由于已知的编码问题,它不适用于 MS Sql 批量插入。 恕我直言,m-row insert 对于大多数人来说是很好的实用解决方案。

@markschwarz非常欢迎选择让它更快地工作!

使用 sqlite 跟踪查询,我在使用chunksize时似乎确实进入了多插入:

2017-09-28 00:21:39,007 INFO sqlalchemy.engine.base.Engine INSERT INTO country_hsproduct_year (location_id, product_id, year, export_rca, import_value, cog, export_value, distance, location_level, product_level) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2017-09-28 00:21:39,007 INFO sqlalchemy.engine.base.Engine ((75, 1237, 1996, 1.7283086776733398, 273487116.0, 0.0, 514320160.0, 0.5413745641708374, 'country', '4digit'), (75, 1237, 1997, 1.7167805433273315, 312047528.0, 0.0, 592372864.0, 0.5314807891845703, 'country', '4digit'), (75, 1237, 1998, 1.2120152711868286, 341676961.0, 0.0, 468860608.0, 0.5472233295440674, 'country', '4digit'), (75, 1237, 1999, 1.236651062965393, 334604240.0, 0.0, 440722336.0, 0.5695921182632446, 'country', '4digit'), (75, 1237, 2000, 1.189828872680664, 383555023.0, 0.0, 426384832.0, 0.5794379711151123, 'country', '4digit'), (75, 1237, 2001, 0.9920380115509033, 374157144.0, 0.3462945520877838, 327031392.0, 0.6234743595123291, 'country', '4digit'), (75, 1237, 2002, 1.0405025482177734, 471456583.0, 0.0, 377909376.0, 0.6023964285850525, 'country', '4digit'), (75, 1237, 2003, 1.147829532623291, 552441401.0, 0.0, 481313504.0, 0.5896202325820923, 'country', '4digit')  ... displaying 10 of 100000 total bound parameter sets ...  (79, 1024, 2015, 0.0, None, 0.8785018920898438, 0.0, 0.9823430776596069, 'country', '4digit'), (79, 1025, 1995, 0.0, None, 0.5624096989631653, 0.0, 0.9839603304862976, 'country', '4digit'))

(没有猴子补丁,即)

有趣的是,对于猴子补丁,当我给它块大小为 10^5 而不是 10^3 时它会中断。 错误是sqlite上的“sql variables太多”。

@makmanalp ,我还没有追踪 PG 来检查这种行为,但我几乎总是在插入时设置块大小。 在上面的示例中,我将其随机设置为 200-5000 之间的 5 个值。 在没有猴子补丁的情况下,我没有看到这些选择之间存在巨大的经过时间差异。 使用补丁后,经过的时间下降了约 80%。

https://github.com/pandas-dev/pandas/issues/8953#issuecomment -76139975

这个猴子补丁还在工作吗? 我在 MS SQL Server 上尝试过,但没有看到改进。 此外,它抛出一个异常:

(pyodbc.Error) ('07002', '[07002] [Microsoft][SQL Server Native Client 11.0]COUNT field incorrect or syntax error (0) (SQLExecDirectW)')

@hangyao我认为该补丁是特定于实现的,这是python DBAPI留给DBAPI驱动程序如何处理的那些事情之一。 所以它可能会更快,也可能不会。 RE:语法错误,不确定。

我正在考虑在第 521 行的新 IF clouse 中的_engine_builder _engine_builder函数中的/io/sql.py文件中的第 507 行添加以下函数片段。 我已经在我的环境中对其进行了简单的测试,它非常适用于 MSSQL 数据库,实现了 100 倍以上的加速。 我还没有在其他数据库上测试过。

让我做 PR 的事情是,我认为让它整洁和安全比像下面那样插入它更努力,这可能并不总是需要的规范并添加一个布尔开关,它可以打开/关闭这个设置, (例如fast_executemany=True )在to_sql中似乎有点太大的努力,我认为不问就做。

所以我的问题是:

  • 下面的函数是否有效并且还提高了 PostgreSQL 的 INSERT 速度?

  • pandas 事件是否希望在其源代码中使用此代码段? 如果是这样的话:

  • 是否希望将此功能添加到默认的sql.py功能中,还是有更好的地方添加它?

喜欢听一些评论。

答案来源: https ://stackoverflow.com/questions/48006551/speeding-up-pandas-dataframe-to-sql-with-fast-executemany-of-pyodbc/48861231#48861231

@event.listens_for(engine, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
    if executemany:
        cursor.fast_executemany = True
def _engine_builder(con):
    """
    Returns a SQLAlchemy engine from a URI (if con is a string)
    else it just return con without modifying it.
    """
    global _SQLALCHEMY_INSTALLED
    if isinstance(con, string_types):
        try:
            import sqlalchemy
        except ImportError:
            _SQLALCHEMY_INSTALLED = False
        else:
            con = sqlalchemy.create_engine(con)

    @event.listens_for(engine, 'before_cursor_execute')
    def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
        if executemany:
            cursor.fast_executemany = True
return con

@tsktsktsk123最近有一个与此相关的 PR 合并: https://github.com/pandas-dev/pandas/pull/19664。 我还没有详细查看你的帖子,它肯定不完全相同(它使用 sqlalchemy 引擎的supports_multivalues_insert属性),但只是为了确保你知道它,以防这已经有帮助也是。

这真是个好消息! 我还没有查看 PR,但会在本周末进行比较并返回结果。 感谢您的提醒。

我只是在尝试 0.23.0 RC2(在 postgresql 上),而不是提高性能,我的脚本明显变慢了。 数据库查询变得更快,但测量to_sql()的时间实际上变得慢了 1.5 倍(比如从 7 到 11 秒)......

虽然我刚刚测试了 RC,但不确定减速是否来自这个 PR。

还有其他人遇到过同样的问题吗?

@schettino72你插入了多少数据?

大约 30K 行,10 列。 但实际上我尝试的几乎所有东西都比较慢(SQL 更快但总体上更慢)。 它正在创建一个巨大的 SQL 语句,其中每个值都有值插值。 就像是

 %(user_id_m32639)s, %(event_id_m32639)s, %(colx_m32639)s,

我发现d6tstack使用起来要简单得多,它是单行d6tstack.utils.pd_to_psql(df, cfg_uri_psql, 'benchmark', if_exists='replace')并且比df.to_sql()快得多。 支持 postgres 和 mysql。 见https://github.com/d6t/d6tstack/blob/master/examples-sql.ipynb

我一直在使用猴子补丁解决方案:

from pandas.io.sql import SQLTable

def _execute_insert(self, conn, keys, data_iter):
    print "Using monkey-patched _execute_insert"
    data = [dict((k, v) for k, v in zip(keys, row)) for row in data_iter]
    conn.execute(self.insert_statement().values(data))

SQLTable._execute_insert = _execute_insert

现在有一段时间了,但现在我收到一个错误:

TypeError: insert_statement() missing 2 required positional arguments: 'data' and 'conn'

有其他人得到这个吗? 我在 Python 3.6.5 (Anaconda) 和 pandas==0.23.0

这得到修复了吗? 目前,df.to_sql 非常慢,根本无法用于许多实际用例。 Odo 项目似乎已经被放弃了。
我在金融时间序列中有以下用例,其中 df.to_sql 几乎不可用:
1) 将历史 csv 数据复制到 postgres 数据库 - 不能使用 df.to_sql 并且必须使用围绕 psycopg2 copy_from 功能的自定义代码
2) 流数据(以每秒约 500-3000 行的批次)转储到 postgres 数据库 - df.to_sql 的性能再次令人失望,因为将这些自然批次的数据插入 postgres 需要花费太多时间。
我发现 df.to_sql 现在唯一有用的地方是自动创建表!!! - 这不是它设计的用例。
我不确定其他人是否也有同样的担忧,但这个问题需要一些注意,以使“数据帧到数据库”接口能够顺利工作。
期待。

嘿,当我尝试对 SQLite 数据库执行多插入操作时出现此错误:

这是我的代码:
df.to_sql("financial_data", con=conn, if_exists="append", index=False, method="multi")

我得到这个错误:

Traceback (most recent call last):

  File "<ipython-input-11-cf095145b980>", line 1, in <module>
    handler.insert_financial_data_from_df(data, "GOOG")

  File "C:\Users\user01\Documents\Code\FinancialHandler.py", line 110, in insert_financial_data_from_df
    df.to_sql("financial_data", con=conn, if_exists="append", index=False, method="multi")

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\core\generic.py", line 2531, in to_sql
    dtype=dtype, method=method)

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 460, in to_sql
    chunksize=chunksize, dtype=dtype, method=method)

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 1547, in to_sql
    table.insert(chunksize, method)

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 686, in insert
    exec_insert(conn, keys, chunk_iter)

  File "C:\Users\user01\AppData\Local\Continuum\anaconda3\lib\site-packages\pandas\io\sql.py", line 609, in _execute_insert_multi
    conn.execute(self.table.insert(data))

TypeError: insert() takes exactly 2 arguments (1 given)

为什么会这样? 我正在使用 Python 3.7.3 (Anaconda)、pandas 0.24.2 和 sqlite3 2.6.0。

非常感谢您!

@jconstanzo你能把它作为一个新问题打开吗?
如果可能的话,你能尝试提供一个可重复的例子吗? (例如一个可以显示问题的小示例数据框)

@jconstanzo这里有同样的问题。 当您尝试插入 SQLite 数据库时,使用method='multi' (在我的情况下,与chunksize结合使用)似乎会触发此错误。

不幸的是,我无法真正提供示例数据框,因为我的数据集很大,这就是我首先使用methodchunksize的原因。

我很抱歉耽搁了。 我刚刚为这个问题打开了一个问题: https ://github.com/pandas-dev/pandas/issues/29921

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

ericdf picture ericdf  ·  3评论

andreas-thomik picture andreas-thomik  ·  3评论

nathanielatom picture nathanielatom  ·  3评论

Abrosimov-a-a picture Abrosimov-a-a  ·  3评论

MatzeB picture MatzeB  ·  3评论