You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Python更新Firebird(fdb)时的Token unknown DatabaseError?

解决Firebird(fdb)Python更新含特殊字符字符串的DatabaseError问题

错误详情

DatabaseError                             Traceback (most recent call last)
Cell In[13], line 8
      6         num = int(num.strip())
      7         cur.execute(f'''UPDATE PRODUCTOS SET DEPT = {dict_prov[row["Provedor"]]} WHERE CODIGO = {num};''')
----> 8         cur.execute(f'''UPDATE PRODUCTOS SET DESCRIPCION = {row["Producto"]} WHERE CODIGO = {num};''')
      9 else :
     10     cur.execute(f'''UPDATE PRODUCTOS SET DEPT = {dict_prov[row["Provedor"]]} WHERE CODIGO = {row["Código de Barras"]};''')

File c:\Users\casa\miniconda3\envs\test1\lib\site-packages\fdb\fbcore.py:3694, in Cursor.execute(self, operation, parameters)
   3692     self._ps = weakref.proxy(operation, _weakref_callback(self.__ps_deleted))
   3693 else:
-> 3694     self._ps = PreparedStatement(operation, self, True)
   3695 self._ps._execute(parameters)
   3696 # return self so `execute` call could be used as iterable

File c:\Users\casa\miniconda3\envs\test1\lib\site-packages\fdb\fbcore.py:2309, in PreparedStatement.__init__(self, operation, cursor, internal)
   2305 api.isc_dsql_prepare(self._isc_status, self.cursor._transaction._tr_handle,
   2306                      self._stmt_handle, len(op), op, self.__sql_dialect,
   2307                      ctypes.cast(ctypes.pointer(self._out_sqlda), XSQLDA_PTR))
   2308 if db_api_error(self._isc_status):
-> 2309     raise exception_from_status(DatabaseError, self._isc_status,
   2310                                 "Error while preparing SQL statement:")
   2311 # Determine statement type
   2312 info = b(' ') * 20

DatabaseError: ('Error while preparing SQL statement:\n- SQLCODE: -104\n- Dynamic SQL Error\n- SQL error code = -104\n- Token unknown - line 1, column 42\n- Jalape', -104, 335544569)

运行代码

for index, row in excel_df.iterrows():
    if pd.notna(row['Código de Barras']):
        if isinstance(row['Código de Barras'], str):
            x = row['Código de Barras'].split(',')
            for num in x:
                num = int(num.strip())
                cur.execute(f'''UPDATE PRODUCTOS SET DEPT = {dict_prov[row["Provedor"]]} WHERE CODIGO = {num};''')
                cur.execute(f'''UPDATE PRODUCTOS SET DESCRIPCION = {row["Producto"]} WHERE CODIGO = {num};''')
        else :
            cur.execute(f'''UPDATE PRODUCTOS SET DEPT = {dict_prov[row["Provedor"]]} WHERE CODIGO = {row["Código de Barras"]};''')
            cur.execute(f'''UPDATE PRODUCTOS SET DESCRIPCION = {row["Producto"]} WHERE CODIGO = {row["Código de Barras"]};''')
    if pd.notna(row['Código SuperFuentes']):
        cur.execute(f'''UPDATE PRODUCTOS SET DEPT = {dict_prov[row["Provedor"]]} WHERE CODIGO = {row["Código SuperFuentes"]};''')
        cur.execute(f'''UPDATE PRODUCTOS SET DESCRIPCION = {row["Producto"]} WHERE CODIGO = {row["Código SuperFuentes"]};''')
    con.commit()

相关环境信息

  • Excel文件编码:cp1252
  • 数据库字符集:NONE
  • 字符串列字符集:NONE
  • 本地首选编码:cp1252
  • Firebird文件版本:2.0.1
  • Firebird服务器版本:2.0.7

问题原因

  1. SQL语法错误:直接用f-string拼接SQL语句,字符串类型的DESCRIPCION值未被引号包裹,当值包含特殊字符(如ñ)时,数据库会将其视为SQL语法的一部分,导致解析失败(错误中的Jalape是被截断的字符串,数据库将其识别为未知语法token)。
  2. 编码适配问题:Firebird 2.0的NONE字符集表示依赖客户端编码传递的字节流,若客户端与数据库的编码处理不一致,可能引发字符解析异常。

解决方案

1. 使用参数化查询(核心解决方法)

禁止直接拼接外部来源的字符串到SQL语句中,参数化查询可自动处理字符串的引号、特殊字符转义,同时避免SQL注入风险。

修改后的代码示例:

for index, row in excel_df.iterrows():
    dept_value = dict_prov[row["Provedor"]]
    descripcion_value = row["Producto"]
    
    if pd.notna(row['Código de Barras']):
        if isinstance(row['Código de Barras'], str):
            x = row['Código de Barras'].split(',')
            for num in x:
                num = int(num.strip())
                # 参数化更新DEPT
                cur.execute('UPDATE PRODUCTOS SET DEPT = ? WHERE CODIGO = ?;', (dept_value, num))
                # 参数化更新DESCRIPCION
                cur.execute('UPDATE PRODUCTOS SET DESCRIPCION = ? WHERE CODIGO = ?;', (descripcion_value, num))
        else:
            codigo = row["Código de Barras"]
            cur.execute('UPDATE PRODUCTOS SET DEPT = ? WHERE CODIGO = ?;', (dept_value, codigo))
            cur.execute('UPDATE PRODUCTOS SET DESCRIPCION = ? WHERE CODIGO = ?;', (descripcion_value, codigo))
    
    if pd.notna(row['Código SuperFuentes']):
        codigo = row["Código SuperFuentes"]
        cur.execute('UPDATE PRODUCTOS SET DEPT = ? WHERE CODIGO = ?;', (dept_value, codigo))
        cur.execute('UPDATE PRODUCTOS SET DESCRIPCION = ? WHERE CODIGO = ?;', (descripcion_value, codigo))
    
    con.commit()

2. 连接时显式指定编码

连接Firebird数据库时,指定编码为cp1252,匹配本地编码与Excel文件编码,确保字节流传递正确:

import fdb

# 连接时指定charset参数
con = fdb.connect(
    host='你的数据库地址',
    database='路径/你的数据库.fdb',
    user='用户名',
    password='密码',
    charset='cp1252'
)
cur = con.cursor()

3. 验证字符串编码一致性

若仍存在编码问题,可将读取的Excel字符串做编码转换后再传递(参数化查询通常已自动处理,此为备选方案):

descripcion_value = row["Producto"].encode('cp1252').decode('cp1252')

说明

参数化查询是解决这类问题的根本方案,既解决了特殊字符导致的语法错误,也提升了代码安全性;编码适配则确保字符在客户端与数据库之间正确传递。

内容的提问来源于stack exchange,提问作者Tezcatlipoca0000

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 04:00:56