使用sqlalchemy的python pandas to_sql:如何加快导出到MS SQL?

Pyt*_*ous 21 python sql sqlalchemy pyodbc pandas

我有一个大约155,000行和12列的数据帧.如果我使用dataframe.to_csv将其导出到csv,则输出为11MB文件(即时生成).

但是,如果我使用to_sql方法导出到Microsoft SQL Server,则需要5到6分钟!没有列是文本:只有int,float,bool和日期.我见过ODBC驱动程序设置nvarchar(max)的情况,这会减慢数据传输速度,但这不是这种情况.

有关如何加快出口流程的任何建议?导出11 MB数据需要6分钟,这使得ODBC连接几乎无法使用.

谢谢!

我的代码是:

import pandas as pd
from sqlalchemy import create_engine, MetaData, Table, select
ServerName = "myserver"
Database = "mydatabase"
TableName = "mytable"

engine = create_engine('mssql+pyodbc://' + ServerName + '/' + Database)
conn = engine.connect()

metadata = MetaData(conn)

my_data_frame.to_sql(TableName,engine)
Run Code Online (Sandbox Code Playgroud)

fir*_*ynx 13

DataFrame.to_sql方法为ODBC连接器生成插入语句,然后ODBC连接器将其视为常规插入.

当这很慢时,它不是大熊猫的错.

DataFrame.to_sql方法的输出保存到文件,然后通过ODBC连接器重播该文件将花费相同的时间.

将数据批量导入数据库的正确方法是生成csv文件,然后使用load命令,该命令在SQL数据库的MS风格中被调用 BULK INSERT

例如:

BULK INSERT mydatabase.myschema.mytable
FROM 'mydatadump.csv';
Run Code Online (Sandbox Code Playgroud)

语法参考如下:

BULK INSERT 
   [ database_name . [ schema_name ] . | schema_name . ] [ table_name | view_name ] 
      FROM 'data_file' 
     [ WITH 
    ( 
   [ [ , ] BATCHSIZE = batch_size ] 
   [ [ , ] CHECK_CONSTRAINTS ] 
   [ [ , ] CODEPAGE = { 'ACP' | 'OEM' | 'RAW' | 'code_page' } ] 
   [ [ , ] DATAFILETYPE = 
      { 'char' | 'native'| 'widechar' | 'widenative' } ] 
   [ [ , ] FIELDTERMINATOR = 'field_terminator' ] 
   [ [ , ] FIRSTROW = first_row ] 
   [ [ , ] FIRE_TRIGGERS ] 
   [ [ , ] FORMATFILE = 'format_file_path' ] 
   [ [ , ] KEEPIDENTITY ] 
   [ [ , ] KEEPNULLS ] 
   [ [ , ] KILOBYTES_PER_BATCH = kilobytes_per_batch ] 
   [ [ , ] LASTROW = last_row ] 
   [ [ , ] MAXERRORS = max_errors ] 
   [ [ , ] ORDER ( { column [ ASC | DESC ] } [ ,...n ] ) ] 
   [ [ , ] ROWS_PER_BATCH = rows_per_batch ] 
   [ [ , ] ROWTERMINATOR = 'row_terminator' ] 
   [ [ , ] TABLOCK ] 
   [ [ , ] ERRORFILE = 'file_name' ] 
    )] 
Run Code Online (Sandbox Code Playgroud)

  • 有兴趣通过Python对SQL Server进行BULK INSERT的人也可能有兴趣看一下[我对相关问题的回答](http://stackoverflow.com/a/29649340/2144390). (4认同)

Vis*_*pta 9

使用SQLAlchemy>=1.3,在创建engine对象时,设置fast_executemany=True. 参考

  • 帮助其他人的“多”解决方案并没有帮助我,但这却帮助了我。谢谢。 (3认同)

gle*_*452 8

您可以使用它:使它更快的是methodpandas的参数to_sql。我希望这有帮助。

根据我的经验,结果是从无限时间到 8 秒。


df = pd.read_csv('test.csv')

conn = create_engine(<connection_string>)

start_time = time.time()
df.to_sql('table_name', conn, method='multi',index=False, if_exists='replace')
print("--- %s seconds ---" % (time.time() - start_time))
Run Code Online (Sandbox Code Playgroud)


Nem*_*sMF 7

我最近遇到了同样的问题,并想为其他人添加一个答案。 to_sql似乎INSERT为每行发送一个查询,这确实很慢。但是由于0.24.0有一个method参数pandas.to_sql(),您可以在其中定义自己的插入函数,或者仅用于method='multi'告诉熊猫在单个INSERT查询中传递多行,这使其速度大大提高。

请注意,您的数据库可能有参数限制。在这种情况下,您还必须定义一个块大小。

因此解决方案应该看起来像这样:

my_data_frame.to_sql(TableName, engine, chunksize=<yourParameterLimit>, method='multi')
Run Code Online (Sandbox Code Playgroud)

如果您不知道数据库参数限制,请尝试不使用chunksize参数。它会运行或提示您限制的错误。

  • 我在将数据加载到 Postgres 时包含了 method='multi',它的加载速度加快了 1000 倍:) 900k 行的数据无法在 6 小时内完成。但当我使用“multi”时,需要 5 分钟。谢谢你的提示。 (11认同)
  • 正如 @Fips 所提到的,对于 SQL Server,您需要 2100//len(df.columns) 作为您的块大小,如果您不这样做,您收到的错误消息可能会非常神秘。另外,对于我的具有大量列和大约 10,000 个插入的表,没有加速,因此您的里程可能会有所不同。 (4认同)
  • 我遇到了 MS-SQL 的问题,它返回“最大参数 2100”,因此我将 2100 除以列数,结果约为 56,并将 50 作为块大小。仍然是一个极大的加速(表有大约 3000 行要插入) (3认同)
  • 相同的。这就像一个魅力。有人可以在答案中解释 chunksize 吗? (2认同)
  • @technazi 正如我的回答中所述,一次发送所有行可能会超出数据库参数限制并导致错误。为了避免这种情况,您可以指定_chunksize_。这会将插入分为您在 chunksize 中指定的行数的块。如果您的数据库的参数限制为 100 000,并且您的 DataFrame 有 100 万行,那么除非您添加 `chunksize=100000`,否则它将失败。 (2认同)
  • 我正在做的许多上传工作都会完全超时。现在我添加了 method='multi' 并且脚本完美运行。非常感谢你的帖子! (2认同)
  • 为什么这不是默认参数;-( (2认同)

cit*_*man 6

您可以使用d6tstack,它具有快速 pandas 到 SQL 功能,因为它使用本机数据库导入命令。它支持 MS SQL、Postgres 和 MYSQL

uri_psql = 'postgresql+psycopg2://usr:pwd@localhost/db'
d6tstack.utils.pd_to_psql(df, uri_psql, 'table')
uri_mssql = 'mssql+pymssql://usr:pwd@localhost/db'
d6tstack.utils.pd_to_mssql(df, uri_mssql, 'table', 'schema') # experimental
Run Code Online (Sandbox Code Playgroud)

对于在写入数据库之前导入多个包含数据模式更改和/或使用 pandas 进行预处理的 CSV 也很有用,请参阅示例笔记本中的进一步内容

d6tstack.combine_csv.CombinerCSV(glob.glob('*.csv'), 
    apply_after_read=apply_fun).to_psql_combine(uri_psql, 'table')
Run Code Online (Sandbox Code Playgroud)


Uwe*_*orn 6

为什么pandas.DataFrame.to_sql慢?

\n

将数据上传到pandasMicrosoft SQL Server 时,大部分时间实际上花在从pandasPython 对象转换为 MS SQL ODBC 驱动程序所需的表示形式上。分析速度比基本 Python 代码快得多的原因之一pandas是,它适用于整数/浮点数/\xe2\x80\xa6 的精简本机数组,这些数组与各自的 Python 对应项没有相同的开销。该to_sql方法实际上是将所有这些精简列转换为许多单独的 Python 对象,因此不会像其他pandas操作那样获得通常的性能处理。

\n

用于turbodbc.Cursor.insertmanycolumns加快速度

\n

给定 a pandas.DataFrame,您可以使用turbodbcpyarrow插入数据,其转换开销比转换为 Python 对象时要少。

\n
import pyarrow as pa\nimport turbodbc\n\ncursor = \xe2\x80\xa6  # cursor to a MS SQL connection initiated with turbodbc\ndf = \xe2\x80\xa6  # the pd.DataFrame to be inserted\n\n# Convert the pandas.DataFrame to a pyarrow.Table, most of the columns\n# will be zero-copy and thus this is quite fast.\ntable = pa.Table.from_pandas(table)\n\n# Insert into the database\ncursor.executemanycolumns("INSERT INTO my_table VALUES (?, ?, ?)",\n                           table)\n
Run Code Online (Sandbox Code Playgroud)\n

为什么这样更快?

\n

我们不是进行 -> Python 对象集合 -> ODBC 数据结构的转换,而是进行路径-> -> ODBC 结构pd.DataFrame的转换。由于以下原因,它的性能更高:pd.DataFramepyarrow.Table

\n
    \n
  • a 的大部分列无需复制pandas.DataFrame即可转换为 the 的列。pyarrow.Table表的列将引用相同的内存。所以没有进行实际的转换。
  • \n
  • 转换完全在本机代码中使用本机类型完成。这意味着只要我们没有类型化列,就不会在任何阶段出现 Python 对象的开销object
  • \n
\n


小智 6

对于sqlalchemy>= 1.3,不要使用 的to_sql()方法参数,而是使用fast_executemany=Truein sqlalchemy's create_engine()。这应该至少与避免 T-SQL 对存储过程的 2100 个参数值的限制一样快,这会导致此处method="multi"出现的错误。

来自同一链接的戈德·汤普森 (Gord Thompson)。

  • 对于 MS ODBC 来说,最好将 to_sql 保留为默认值(而不是 multi)并在引擎中使用 fast_executemany。 (3认同)