to_sql pyodbc 计数字段不正确或语法错误
to_sql pyodbc count field incorrect or syntax error
我正在从 api 网站下载 Json 数据,并使用 sqlalchemy、pyodbc 和 pandas' to_sql 函数将该数据插入 MSSQL 服务器。
我最多可以下载 10000 行,但是我必须将块大小限制为 10,否则会出现以下错误:
DBAPIError: (pyodbc.Error) ('07002', '[07002] [Microsoft][SQL Server
Native Client 11.0]COUNT field incorrect or syntax error (0)
(SQLExecDirectW)') [SQL: 'INSERT INTO [TEMP_producing_entity_details]
大约有 5 亿行要下载,它只是以这种速度爬行。关于解决方法的任何建议?
谢谢,
提出这个问题时,pandas 0.23.0 刚刚发布。该版本将 .to_sql()
的默认行为从调用 DBAPI .executemany()
方法更改为构建 table-value 构造函数 (TVC),该构造函数通过使用单个 [= 插入多行来提高上传速度16=] 调用 INSERT 语句。不幸的是,这种方法经常超过 T-SQL 对存储过程的 2100 个参数值的限制,导致问题中引用的错误。
此后不久,pandas 的后续版本向 .to_sql()
添加了一个 method=
参数。默认值 - method=None
- 恢复了以前使用 .executemany()
的行为,而指定 method="multi"
会告诉 .to_sql()
使用较新的 TVC 方法。
大约在同一时间,SQLAlchemy 1.3 发布,它向 create_engine()
添加了一个 fast_executemany=True
参数,这大大提高了使用 Microsoft 的 ODBC 驱动程序 SQL 的上传速度服务器。通过这种增强,method=None
被证明至少与 method="multi"
一样快,同时避免了 2100 个参数的限制。
因此,对于 pandas、SQLAlchemy 和 pyodbc 的当前版本,将 .to_sql()
与用于 SQL 服务器的 Microsoft ODBC 驱动程序一起使用的最佳方法是使用fast_executemany=True
和 .to_sql()
的默认行为,即
connection_uri = (
"mssql+pyodbc://scott:tiger^5HHH@192.168.0.199/db_name"
"?driver=ODBC+Driver+17+for+SQL+Server"
)
engine = create_engine(connection_uri, fast_executemany=True)
df.to_sql("table_name", engine, index=False, if_exists="append")
这是 Windows、macOS 和 Microsoft 支持其 ODBC 驱动程序的 Linux 变体上的应用程序 运行 的推荐方法。如果您需要使用 FreeTDS ODBC,则可以使用 method="multi"
和 chunksize=
调用 .to_sql()
,如下所述。
(原答案)
在 pandas 版本 0.23.0 之前,to_sql
会为 DataTable 中的每一行生成一个单独的 INSERT:
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
0,N'row000'
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
1,N'row001'
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
2,N'row002'
大概是为了提高性能,pandas 0.23.0 现在生成一个 table 值构造函数以在每次调用时插入多行
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6),@P3 int,@P4 nvarchar(6),@P5 int,@P6 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2), (@P3, @P4), (@P5, @P6)',
0,N'row000',1,N'row001',2,N'row002'
问题是SQL服务器存储过程(包括像sp_prepexec
这样的系统存储过程)被限制为2100个参数,所以如果DataFrame有100列那么to_sql
只能一次插入大约 20 行。
我们可以使用
计算所需的chunksize
# df is an existing DataFrame
#
# limit based on sp_prepexec parameter count
tsql_chunksize = 2097 // len(df.columns)
# cap at 1000 (limit for number of rows inserted by table-value constructor)
tsql_chunksize = 1000 if tsql_chunksize > 1000 else tsql_chunksize
#
df.to_sql('tablename', engine, index=False, if_exists='replace',
method='multi', chunksize=tsql_chunksize)
但是,最快的方法仍然可能是:
将 DataFrame 转储到 CSV 文件(或类似文件),然后
让 Python 调用 SQL 服务器 bcp
实用程序将该文件上传到 table.
根据 Gord Thompson 的回答做了一些修改。这将 auto-calculate 块大小并将其保持为符合 2100 个参数限制的最接近整数值:
import math
df_num_of_cols=len(df.columns)
chunknum=math.floor(2100/df_num_of_cols)
df.to_sql('MY_TABLE',con=engine,schema='myschema',chunksize=chunknum,if_exists='append',method='multi',index=False )
没有名气,所以我无法对 Amit S 发表评论。
我只是尝试这种方式,使用设置为 'multi' 的方法计算 chuknum
仍然向我显示错误:
[Microsoft][SQL Server Native Client 11.0][SQL Server]
传入请求的参数过多。服务器最多支持 2100 个参数。减少参数个数,重新发送请求
所以我刚刚修改了:
chunknum=math.floor(2100/df_num_of_cols)
到
chunknum=math.floor(2100/df_num_of_cols) - 1
它现在似乎运行良好。
我觉得应该是边缘问题...
对我来说解决方案是不使用:
engine = create_engine(connection_uri, fast_executemany=True)
相反,我只是玩了:
df.to_sql('tablename', engine, index=False, if_exists='replace',
method='multi', chunksize=100)
在这里我输入了 chunksize=90
而不是 chunksize=100
并且它开始工作了。显然,因为之前的 table 更小,对于更多的列,您可能需要更小的列数。如果您不想玩可能由于各种原因而出错的计算,请玩玩它。
我正在从 api 网站下载 Json 数据,并使用 sqlalchemy、pyodbc 和 pandas' to_sql 函数将该数据插入 MSSQL 服务器。
我最多可以下载 10000 行,但是我必须将块大小限制为 10,否则会出现以下错误:
DBAPIError: (pyodbc.Error) ('07002', '[07002] [Microsoft][SQL Server Native Client 11.0]COUNT field incorrect or syntax error (0) (SQLExecDirectW)') [SQL: 'INSERT INTO [TEMP_producing_entity_details]
大约有 5 亿行要下载,它只是以这种速度爬行。关于解决方法的任何建议?
谢谢,
提出这个问题时,pandas 0.23.0 刚刚发布。该版本将 .to_sql()
的默认行为从调用 DBAPI .executemany()
方法更改为构建 table-value 构造函数 (TVC),该构造函数通过使用单个 [= 插入多行来提高上传速度16=] 调用 INSERT 语句。不幸的是,这种方法经常超过 T-SQL 对存储过程的 2100 个参数值的限制,导致问题中引用的错误。
此后不久,pandas 的后续版本向 .to_sql()
添加了一个 method=
参数。默认值 - method=None
- 恢复了以前使用 .executemany()
的行为,而指定 method="multi"
会告诉 .to_sql()
使用较新的 TVC 方法。
大约在同一时间,SQLAlchemy 1.3 发布,它向 create_engine()
添加了一个 fast_executemany=True
参数,这大大提高了使用 Microsoft 的 ODBC 驱动程序 SQL 的上传速度服务器。通过这种增强,method=None
被证明至少与 method="multi"
一样快,同时避免了 2100 个参数的限制。
因此,对于 pandas、SQLAlchemy 和 pyodbc 的当前版本,将 .to_sql()
与用于 SQL 服务器的 Microsoft ODBC 驱动程序一起使用的最佳方法是使用fast_executemany=True
和 .to_sql()
的默认行为,即
connection_uri = (
"mssql+pyodbc://scott:tiger^5HHH@192.168.0.199/db_name"
"?driver=ODBC+Driver+17+for+SQL+Server"
)
engine = create_engine(connection_uri, fast_executemany=True)
df.to_sql("table_name", engine, index=False, if_exists="append")
这是 Windows、macOS 和 Microsoft 支持其 ODBC 驱动程序的 Linux 变体上的应用程序 运行 的推荐方法。如果您需要使用 FreeTDS ODBC,则可以使用 method="multi"
和 chunksize=
调用 .to_sql()
,如下所述。
(原答案)
在 pandas 版本 0.23.0 之前,to_sql
会为 DataTable 中的每一行生成一个单独的 INSERT:
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
0,N'row000'
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
1,N'row001'
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2)',
2,N'row002'
大概是为了提高性能,pandas 0.23.0 现在生成一个 table 值构造函数以在每次调用时插入多行
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(6),@P3 int,@P4 nvarchar(6),@P5 int,@P6 nvarchar(6)',
N'INSERT INTO df_to_sql_test (id, txt) VALUES (@P1, @P2), (@P3, @P4), (@P5, @P6)',
0,N'row000',1,N'row001',2,N'row002'
问题是SQL服务器存储过程(包括像sp_prepexec
这样的系统存储过程)被限制为2100个参数,所以如果DataFrame有100列那么to_sql
只能一次插入大约 20 行。
我们可以使用
计算所需的chunksize
# df is an existing DataFrame
#
# limit based on sp_prepexec parameter count
tsql_chunksize = 2097 // len(df.columns)
# cap at 1000 (limit for number of rows inserted by table-value constructor)
tsql_chunksize = 1000 if tsql_chunksize > 1000 else tsql_chunksize
#
df.to_sql('tablename', engine, index=False, if_exists='replace',
method='multi', chunksize=tsql_chunksize)
但是,最快的方法仍然可能是:
将 DataFrame 转储到 CSV 文件(或类似文件),然后
让 Python 调用 SQL 服务器
bcp
实用程序将该文件上传到 table.
根据 Gord Thompson 的回答做了一些修改。这将 auto-calculate 块大小并将其保持为符合 2100 个参数限制的最接近整数值:
import math
df_num_of_cols=len(df.columns)
chunknum=math.floor(2100/df_num_of_cols)
df.to_sql('MY_TABLE',con=engine,schema='myschema',chunksize=chunknum,if_exists='append',method='multi',index=False )
没有名气,所以我无法对 Amit S 发表评论。 我只是尝试这种方式,使用设置为 'multi' 的方法计算 chuknum 仍然向我显示错误:
[Microsoft][SQL Server Native Client 11.0][SQL Server]
传入请求的参数过多。服务器最多支持 2100 个参数。减少参数个数,重新发送请求
所以我刚刚修改了:
chunknum=math.floor(2100/df_num_of_cols)
到
chunknum=math.floor(2100/df_num_of_cols) - 1
它现在似乎运行良好。 我觉得应该是边缘问题...
对我来说解决方案是不使用:
engine = create_engine(connection_uri, fast_executemany=True)
相反,我只是玩了:
df.to_sql('tablename', engine, index=False, if_exists='replace',
method='multi', chunksize=100)
在这里我输入了 chunksize=90
而不是 chunksize=100
并且它开始工作了。显然,因为之前的 table 更小,对于更多的列,您可能需要更小的列数。如果您不想玩可能由于各种原因而出错的计算,请玩玩它。