如何解决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
问题原因
- SQL语法错误:直接用f-string拼接SQL语句,字符串类型的
DESCRIPCION值未被引号包裹,当值包含特殊字符(如ñ)时,数据库会将其视为SQL语法的一部分,导致解析失败(错误中的Jalape是被截断的字符串,数据库将其识别为未知语法token)。 - 编码适配问题: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
相关产品推荐
相关产品推荐

