【问题标题】:Issue with inserting data into Sql server using Python Pandas Dataframe使用 Python Pandas Dataframe 将数据插入 Sql 服务器的问题
【发布时间】:2019-04-10 13:34:54
【问题描述】:

我正在尝试从 REST API 中提取数据并将其插入 SQL Server。如果我们让脚本一起执行 PhotoBinary,Filetype 它可以工作,但是一旦我添加了整数 ID,我们就会得到下面的错误。另外,如果我让它自己从 API 中提取 ID,它就可以工作。

我正在尝试提取 3 条信息

  1. EmployeeID 是一个整数。
  2. 图像的二进制字符串表示
  3. 原始文件的文件类型例如:.jpg

目标表设置为:

Create table Employee_Photo
( 
    EmployeeID  int,
    PhotoBinary varchar(max),
    FileType varchar(10)
)

我得到的错误是:

Traceback (most recent call last):
  File "apiphotopullwithid.py", line 64, in <module>
    cursor.execute("INSERT INTO dbo.Employee_Photo([EmployeeID],[PhotoBinary],[FileType]) values (?,?,?)", row['EMPID'],row['Photo'],row['PhotoType'])
pyodbc.ProgrammingError: ('42000', '[42000] [Microsoft][ODBC SQL Server Driver][SQL Server]The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Parameter 5 (""): The supplied value is not a valid instance of data type float. Check the source data for invalid values. An example of an invalid value is data of numeric type with scale greater than precision. (8023) (SQLExecDirectW)')
import json
import pandas as pd
import sqlalchemy
import pyodbc
import requests

url = "https://someurl.com/api/PersonPhoto"

headers = {
    'Accept': "application/json",
    'Authorization': "apikey XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    'Content-Type': "application/json",
    'cache-control': "no-cache"
}

response = requests.request("GET", url, headers=headers)
data = json.loads(response.text)


ID,Photo,PhotoType = [],[],[]

for device in data['PersonPhoto']:
    ID.append(device[u'ID'])

    Photo.append(device[u'Photo'])

    PhotoType.append(device[u'PhotoType'])


df = pd.DataFrame([ID,Photo,PhotoType]).T
df.columns = ['EMPID','Photo','PhotoType']
df = df.astype({'EMPID':'Int64'})



connStr = pyodbc.connect(
    "DRIVER={SQL Server};"
    "SERVER=SQLTest;"
    "Database=Intranet123;"
    "Trusted_Connection=yes;"
    #"UID=ConnectME;"
    #"PWD={Password1}"
)
cursor = connStr.cursor()

for index,row in df.iterrows():
cursor.execute("INSERT INTO dbo.Employee_Photo([EmployeeID],[PhotoBinary],[FileType]) values (?,?,?)", row['EMPID'],row['Photo'],row['PhotoType']) 
    connStr.commit()
    cursor.close()
connStr.close()

【问题讨论】:

  • 我确实做到了。如果我自己为 3 列中的任何一个运行脚本,则该过程有效。问题似乎是当它们组合时我得到了错误。当第二列和第三列为空时,这似乎是 Pandas 对数据所做的事情。它似乎将 Int 转换为 Float。
  • 你能修改python程序,使错误以独立的方式重现吗? EG 删除 API 调用并插入文字值。
  • 我已经尝试将 api 的响应输出到屏幕上,它显示正确,即作为 int 而不是 float。看来PANDAS部分正在进行转换。

标签: python sql-server pandas


【解决方案1】:

在大多数 Python 数据库 API 中,包括遵循 PEP 249 规范的 pyodbc,cursor.execute() 中的 parameters 参数通常是一个序列(即元组、列表)。因此,将所有值绑定到一个可迭代对象中,而不是作为三个单独的参数值:

sql = "INSERT INTO dbo.Employee_Photo ([EmployeeID],[PhotoBinary],[FileType]) VALUES (?,?,?)"

# TUPLE
cursor.execute(sql, (row['EMPID'], row['Photo'], row['PhotoType']))

# LIST
cursor.execute(sql, [row['EMPID'], row['Photo'], row['PhotoType']])

顺便说一句,避免显式的 iterrows 循环并使用带有 executemany 的隐式循环,使用 Pandas 的 DataFrame.values

# EXECUTE PARAMETERIZED QUERY
sql_cols = ['EMPID', 'Photo', 'PhotoType']
cursor.executemany(sql, df[sql_cols].values.tolist())   
conn.commit()

实际上,您甚至不需要 Pandas 作为中间层(仅将库用于数据科学)并与原始返回的 json 交互:

# NESTED LIST OF TUPLES
vals = [(int(device[u'ID']), device[u'Photo'], device[u'PhotoType']) \
           for device in data['PersonPhoto']]

cursor.executemany(sql, vals)   
conn.commit()

【讨论】:

  • 感谢代码 sn-ps 和脚本上方的信息现在可以工作了。
【解决方案2】:

您正在使用 Windows 内置 SQL Server 驱动程序。尝试较新的版本,您可以从 here 获得适用于多个平台的版本。

不要过多阅读错误消息。网络协议层出现格式错误。

您能否转储导致问题的参数的类型和值。我的猜测是驱动程序设置的参数类型不正确。

EG:

for index,row in df.iterrows():
  empid =  row['EMPID']
  photo = row['Photo']
  photoType = row['PhotoType']

  print("empid is ",type(empid), " photo is ", type(photo), " photoType is ", type(photoType))
  print("empid: ",empid, " photo: ", photo, " photoType: ", photoType)

  cursor.execute("INSERT INTO dbo.Employee_Photo([EmployeeID],[PhotoBinary],[FileType]) values (?,?,?)", empid,photo,photoType) 

connStr.commit()
cursor.close()
connStr.close()

【讨论】:

  • 我尝试切换到安装 Python 和 PYODBC 时添加到笔记本电脑的“ODBC Driver 13 for SQL Server”。
  • 目标 SQL Server 运行的是 2014 标准版。
  • 我也刚刚尝试了最新的驱动程序,它产生了相同的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
  • 2018-10-28
  • 2019-10-10
相关资源
最近更新 更多