一、问题描述


在使用python编写后端时,占位符%s常常会与sql参数化查询中的%字符冲突导致错误,情况如下:

# 执行PeelForce查询
                cursor.execute("""
                    SELECT [AdHocWIPDataRecordId]
                    ,[WIPDataNameName]
                    ,[WIPDataValue]
                    ,[ObjectName]
                    FROM [ETL].[dbo].[Mes_WIPDataRecord]
                    where [ObjectName] like '%s%'
                """, [mfg_order_name])

上述代码产生报错:

{

"success": false,

"message": "查询Lot信息时出错: not enough arguments for format string"

}

这个错误是 Python 格式化字符串参数不匹配 导致的,代码本意希望将参数mfg_order_name传递给%s占位符,但是字符串冲突的问题,导致SQL 查询字符串中的 '%s%' 被错误解析了。

这里有两个问题:

  • %s 在 SQL LIKE 中表示匹配任意字符的 's'

  • 但 Python 的 cursor.execute() 会把这个 %s 当作 参数占位符

# 你的代码
sql = "WHERE [ObjectName] like '%s%'"
params = [mfg_order_name]

# Python 尝试解析时:
# 发现字符串中有 %s(在 '%s%' 中)
# 但你已经提供了一个参数 [mfg_order_name]
# 所以它认为:字符串中有1个占位符,你提供了1个参数 → ✅ 数量匹配
# 但实际上,这个 %s 不是参数占位符,是 LIKE 模式的一部分!
# 占位符 %s 在字符串 '...' 内部
sql = "WHERE name LIKE '%s%'"
cursor.execute(sql, ['123'])
# Python 看到:字符串内有 %s,但它在引号里
# 这会被误解为:你想匹配包含字母 's' 的字符串
# 实际执行的可能是:WHERE name LIKE '%s%'(永远匹配不到)

为什么会出现这个错误?

因为 Django/PyODBC 会扫描整个 SQL 字符串查找 %s 占位符,当它找到 '%s%' 中的 %s 时,认为这是一个参数占位符,但:

  1. 这个 %s 被单引号包围,应该是字符串字面量

  2. 但参数化查询的实现可能还是把它当作了占位符

  3. 导致实际参数数量计算错误

尝试更改代码

# 使用字符串拼接构建LIKE条件
                like_pattern = f'%{mfg_order_name}%'
                cursor.execute("""
                    SELECT [AdHocWIPDataRecordId]
                    ,[WIPDataNameName]
                    ,[WIPDataValue]
                    ,[ObjectName]
                    FROM [ETL].[dbo].[Mes_WIPDataRecord]
                    where [ObjectName] like ?
                """, [like_pattern])
# 执行PeelForce查询
                cursor.execute("""
                    SELECT [AdHocWIPDataRecordId]
                    ,[WIPDataNameName]
                    ,[WIPDataValue]
                    ,[ObjectName]
                    FROM [ETL].[dbo].[Mes_WIPDataRecord]
                    where [ObjectName] like '%%%s%%'
                """, [mfg_order_name])

都没有解决。

二、解决方式

多次尝试后,更改代码为

# 使用字符串拼接构建LIKE条件
                like_pattern = f'{mfg_order_name}%'
                cursor.execute("""
                    SELECT [AdHocWIPDataRecordId]
                    ,[WIPDataNameName]
                    ,[WIPDataValue]
                    ,[ObjectName]
                    FROM [ETL].[dbo].[Mes_WIPDataRecord]
                    where [ObjectName] like %s
                """, [like_pattern])

问题解决。

三、思考

✅ 为什么这样修改能解决问题?

对比分析:

python

# ❌ 之前错误的写法
sql = "WHERE [ObjectName] like '%s%'"
params = [mfg_order_name]
# 问题:%s 在引号内,被视为字符串的一部分

# ✅ 现在正确的写法  
sql = "WHERE [ObjectName] like %s"
params = [like_pattern]  # like_pattern = '123%'
# 正确:%s 在 SQL 语法位置,参数中提供完整模式

执行过程:

text

你的代码:
    SQL 发送给数据库:WHERE [ObjectName] like ?
    参数单独发送:'123%'
    数据库组合后:WHERE [ObjectName] like '123%'

之前的错误代码:
    SQL 发送给数据库:WHERE [ObjectName] like '%s%'
    参数单独发送:'123'
    数据库组合后:WHERE [ObjectName] like '%s%'(参数被忽略)

📊 这种写法的优劣分析

✅ 优点:

1. 安全性高(防止SQL注入)

python

# 即使参数包含特殊字符也是安全的
like_pattern = f"'; DROP TABLE users; --%"
cursor.execute("WHERE name LIKE %s", [like_pattern])
# 安全:参数会被正确转义为普通字符串
# 不会执行 DROP TABLE 命令
2. 代码清晰

python

# 一目了然:占位符是 %s,参数是 like_pattern
sql = "WHERE name LIKE %s"
params = [like_pattern]
3. 类型安全
  • 数据库知道参数是字符串类型

  • 会自动进行类型转换和转义处理

⚠️ 缺点/注意事项:

1. 通配符处理需要手动

python

# 需要手动构建模式
like_pattern = f"%{search_term}%"      # 包含
like_pattern = f"{search_term}%"       # 开头
like_pattern = f"%{search_term}"       # 结尾
like_pattern = f"_{search_term}%"      # 第二个字符开始
2. 特殊字符转义

python

# 如果要搜索包含 % 或 _ 的文字
search_term = "100%"  # 包含百分号
# 需要转义
like_pattern = f"%{search_term.replace('%', '\\%').replace('_', '\\_')}%"
3. 性能考虑(通配符位置)

python

# ✅ 好的性能:以固定前缀开头
like_pattern = "ABC%"    # 可以使用索引

# ⚠️ 差的性能:以通配符开头  
like_pattern = "%XYZ"    # 无法使用索引,全表扫描

# ❌ 很差的性能:两边都有通配符
like_pattern = "%MNO%"   # 全表扫描
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐