【问题标题】:Failing bulk insert data from Pandas dataframe into Sybase database table using to_sql使用 to_sql 将 Pandas 数据帧中的数据批量插入 Sybase 数据库表失败
【发布时间】:2020-10-21 23:19:51
【问题描述】:

我下面代码的目的是从一个 RESTful 服务中获取数据,对其进行规范化,将其存储在具有必要列的数据框中,然后最终使用 Pandas 的 to_sql 将其加载到 Sybase 表中。

错误:

文件“C:\Program Files\Anaconda3\lib\site-packages\sqlalchemy\engine\default.py”,第 467 行,在 do_executemany cursor.executemany(语句,参数) sqlalchemy.exc.ProgrammingError: (pyodbc.ProgrammingError) ('42000', "[42000] [Sybase][ODBC Driver][Adaptive Server Enterprise]',' 附近的语法不正确。\n (102) (SQLExecDirectW)") [ SQL: 'INSERT INTO dbo.contract_test ("CONTRACT_ID", "EXCHANGE_ID", "CURRENCY", "TRADING_CODE") VALUES (?, ?, ?, ?)'] [参数: (('0050/TAIEX', 'TAIEX ', 'TWD', 0), ('035420/KORE', 'KORE', 'KRW', 0), ('0TL/LIF', 'LIF', 'NOK', 1), ('100FTSE/LIF ', 'LIF', 'GBP', 0), ('101FTSE/LIF', 'LIF', 'GBP', 0), ('10STAT/OM', 'OM', 'SEK', 0), ( '10TB/KFX', 'KFX', 'KRW', 0), ('10TBA/KFX', 'KFX', 'KRW', 0) ...显示 4525 个总绑定参数集中的 10 个 ... (' ZURF/DTB', 'DTB', 'CHF', 0), ('ZX/NYCE', 'NYCE', 'USD', 0))]

进程以退出代码 1 结束

代码:

from sqlalchemy.engine.url import *               
from sqlalchemy.connectors.pyodbc import *             
from sqlalchemy import create_engine                       
import urllib.request as request                  
import json                         
import pandas as pd                      
from pandas.io.json import json_normalize, DataFrame      
           
response = request.urlopen('http://tfsdscsw5XX/mdsclass/CONTFUTURES--O.json')            
output=response.read()                              
data=json.loads(output)           
df=json_normalize(data)                           
df1=(df[['CONTRACT_ID','EXCHANGE_ID','CURRENCY','TRADING_CODE']])                
df2=pd.DataFrame(df1)           
print(df2)                
print(df2.CONTRACT_ID)          
            
connector =  PyODBCConnector()                 
url = make_url("sybase+pyodbc://myhost/mydatabase?driver=Adaptive Server Enterprise&port=2306")              
print(connector.create_connect_args(url))                         
engine=create_engine(url)

#it is failing here**
df2.to_sql("contract_test",engine,index=False,if_exists="append",schema="dbo")   

response.close()               

数据帧 df2 中的数据示例:

      CONTRACT_ID EXCHANGE_ID CURRENCY  TRADING_CODE
0      0050/TAIEX       TAIEX      TWD             0
1     035420/KORE        KORE      KRW             0
2         0TL/LIF         LIF      NOK             1
3     100FTSE/LIF         LIF      GBP             0
4     101FTSE/LIF         LIF      GBP             0

表contract_test定义:

CREATE TABLE contract_test (
    CONTRACT_ID char(12) NOT NULL,
    EXCHANGE_ID char(12),
    CURRENCY char(4) NOT NULL,
    TRADING_CODE smallint
) 
GO

请帮助解决这个问题?我被困在这里了。

【问题讨论】:

    标签: python sql pandas sqlalchemy sybase


    【解决方案1】:

    您的问题可能只是 Python 数据库 API 的不兼容问题。 Pandas 的to_sql 确实在运行来自pyodbcexecutemany() 呼叫。此模块更普遍地与 SQL Server 一起使用,尤其是在使用 SQLAlchemy 实现时。但是,不完全支持与 Sybase 的集成。正如 SQLAlchemy Sybase 上提到的 docs page:

    注意

    目前不支持 SQLAlchemy 中的 Sybase 方言。它是 没有在持续集成中测试,可能有很多 当前未处理的问题和警告。考虑使用外部 用方言代替。

    具体来说,executemany 似乎正在运行多个 VALUES 行插入,这在 SQL Server 中受支持,但在 Sybase 中不受支持(即使这两种方言都是具有已知连接历史的 TSQL 的变体):

    INSERT INTO dbo.contract_test ("CONTRACT_ID", "EXCHANGE_ID", "CURRENCY", "TRADING_CODE") 
    VALUES ('0050/TAIEX', 'TAIEX', 'TWD', 0), 
           ('035420/KORE', 'KORE', 'KRW', 0), 
           ('0TL/LIF', 'LIF', 'NOK', 1), 
    ...
    

    相反,Sybase 需要带有多个 INSERT INTO 调用的经典 ANSI-SQL:

    INSERT INTO dbo.contract_test ("CONTRACT_ID", "EXCHANGE_ID", "CURRENCY", "TRADING_CODE") 
    VALUES ('0050/TAIEX', 'TAIEX', 'TWD', 0) 
    INSERT INTO dbo.contract_test ("CONTRACT_ID", "EXCHANGE_ID", "CURRENCY", "TRADING_CODE") 
    VALUES ('035420/KORE', 'KORE', 'KRW', 0)
    INSERT INTO dbo.contract_test ("CONTRACT_ID", "EXCHANGE_ID", "CURRENCY", "TRADING_CODE") 
    VALUES ('0TL/LIF', 'LIF', 'NOK', 1)
    ...
    

    要解决,而不是 Pandas 方便的 to_sql 方法,考虑使用通过 DataFrame.to_numpy() 使用数据框行列表的参数直接 SQLAlchemy executemany 调用。下面假设contract_test 表总是预先存在。

    engine = create_engine(url)
    sql = """INSERT INTO dbo.contract_test ("CONTRACT_ID", "EXCHANGE_ID", "CURRENCY", "TRADING_CODE") 
             VALUES (?, ?, ?, ?)"""
    
    with engine.connect() as connection:
        result = connection.execute(sql, df2.to_numpy().tolist())
    

    如果上面仍然遇到同样的问题,请集成一个 for 循环:

    with engine.connect() as connection:
        for row in df2.to_numpy().tolist():
            result = connection.execute(sql, row)
    

    【讨论】:

    • 我已经使用了上面建议的代码,但出现以下错误:result = connection.executemany(sql, row) AttributeError: 'Connection' object has no attribute 'executemany' Process finished with exit code 1
    • 尝试使用execute 运行。见编辑。文档表明 DB-API 将通过参数(可迭代的列表/元组或仅单个可迭代)检测何时使用 executemany
    • 它现在可以与执行一起使用,但速度非常慢,需要 12 分钟才能在 sybase 数据库表中插入 4500 行。如何解决这个性能问题?
    • execute 的哪个版本(带或不带 for 循环)?尝试使用简单的pyodbc connection 的非 SQLAlchemy 版本。使用cursor.executemany
    • 使用 pyodbc 仍然非常慢。我尝试了以下组合:1)pyodbc:执行和executemany 2)sqlalhemy:执行和executemany。尝试了有和没有 for 循环的 1 和 2。插入 4500 行需要 9 分钟。 :(
    【解决方案2】:

    external SAP ASE (Sybase) dialect 现在是 Sybase 推荐的 SQLAlchemy 方言,如果您使用 SAP ASE ODBC 驱动程序,它确实支持 fast_executemany

    【讨论】:

      猜你喜欢
      • 2015-11-06
      • 2020-12-01
      • 2019-05-24
      • 2018-07-22
      • 2018-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-17
      相关资源
      最近更新 更多