1. 从一段"拼接字符串"的 SQL 说起
如果你写过 Python 连 MySQL 做模糊查询,大概率见过甚至写过这样的代码:把用户输入或业务字段切片后,用%拼进 SQL 字符串里,再cursor.execute(sql)一把梭。上面那段con1 = '%%' + b[0:14] + '%%'就是典型写法——它确实能跑,但一旦条件从 1 个变成 3 个、5 个,字段名和切片位置散落在各处,改一个条件要翻半天;更麻烦的是,字符串拼接天然带着 SQL 注入风险,测试环境里随便输个带引号的值就可能报语法错。
这篇要解决的就是这件事:多个模糊查询条件怎么在 Python 里工程化地拼、怎么参数化、怎么把数据库连接配置和模型调用配置统一收进一个settings.json。适合正在做本地开发、测试环境联调,手里有一张报表/日志/订单表,需要按多个字段做 LIKE 匹配的同学。我会先给配置骨架,再给可复制的查询代码,最后跑一条验证动作,打印命中行数和实际 SQL 参数,确认多条件真的生效。
顺带说一句,很多同学在做这类脚本时会顺手接一个大模型来生成或解释 SQL,这时候 Key 的管理就容易乱:数据库密码一个地方、模型 Key 另一个地方。所以我把 TaoToken 的统一 Key 接入也放进同一份settings.json里,让配置集中、代码干净。
2. TaoToken 统一 Key 与 settings.json 配置骨架
TaoToken 是一个统一的大模型 API 接入通道,你可以在一个平台上拿到 Key,然后按 OpenAI 兼容的方式调用不同模型。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api 。它的价值在于:你不用为每个模型单独维护一套鉴权和地址,一个 Key 走天下,特别适合本地脚本里"顺手调个模型"的场景。
先说清楚:数据库连接和模型调用是两回事,TaoToken 不碰你的 MySQL,它只负责模型侧。把两者放同一份配置文件,纯粹是为了工程上少维护几个文件、少几处硬编码。
下面是我在本地项目里用的settings.json骨架,放在项目根目录:
{ "database": { "host": "127.0.0.1", "port": 3306, "user": "dev_user", "password": "dev_pass", "charset": "utf8mb4", "database": "report_db" }, "taotoken": { "base_url": "https://taotoken.net/api", "api_key": "sk-你的统一Key", "default_model": "gpt-4o-mini", "timeout": 30 }, "query": { "table": "report", "like_fields": ["field_a", "field_b"], "batch_size": 500 } }几个要点解释一下。database段就是标准的 PyMySQL 连接参数,charset一定写utf8mb4,否则中文模糊匹配容易出乱码。taotoken段的base_url固定用https://taotoken.net/api,api_key换成你在控制台生成的即可,生成入口在 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。query段是我特意抽出来的:把表名和参与模糊匹配的字段列成数组,后面拼 SQL 时直接遍历这个数组,加字段只改配置不改代码。
注意:
settings.json里含密码和 Key,务必加进.gitignore,别提交到仓库。本地可以再放一份settings.example.json作为模板。
读取配置的代码很短:
import json def load_settings(path="settings.json"): with open(path, "r", encoding="utf-8") as f: return json.load(f) settings = load_settings() db_conf = settings["database"] tk_conf = settings["taotoken"] q_conf = settings["query"]到这里配置层就绪。接下来是核心:怎么把"多个模糊条件"变成安全的参数化 SQL。
3. 多个 LIKE 条件的参数化拼接
先明确目标:给定一个关键词列表,比如["Z001B191108002R100", "2024", "华东"],要生成WHERE field_a LIKE %s AND field_b LIKE %s AND field_c LIKE %s这样的结构,并且每个%s对应的参数是%关键词%。字段名从配置的like_fields里取,参数值单独放进一个列表,绝不拼进 SQL 字符串。
def build_like_query(table, fields, keywords): """ table: 表名 fields: 参与模糊匹配的字段列表,如 ["field_a", "field_b"] keywords: 与 fields 一一对应的关键词列表 返回 (sql, params) """ if len(fields) != len(keywords): raise ValueError("字段数与关键词数不一致") conditions = [] params = [] for field, kw in zip(fields, keywords): conditions.append(f"`{field}` LIKE %s") params.append(f"%{kw}%") where_clause = " AND ".join(conditions) sql = f"SELECT * FROM `{table}` WHERE {where_clause}" return sql, params调用一下看看:
sql, params = build_like_query( q_conf["table"], q_conf["like_fields"], ["Z001B191108002R100", "2024"] ) print(sql) print(params)输出会是:
SELECT * FROM `report` WHERE `field_a` LIKE %s AND `field_b` LIKE %s ['%Z001B191108002R100%', '%2024%']注意这里字段名用了反引号包裹,防止字段名和 MySQL 关键字冲突;关键词统一加%前后缀,实现"包含"语义。如果你想要"前缀匹配",把f"%{kw}%"改成f"{kw}%"就行,这是个小但常用的开关。
对比一下开头那段老代码:老写法把con1、con2直接拼进 SQL,字段名写死,切片位置写死;新写法字段来自配置、参数走占位符,加一个条件只是往like_fields和keywords里各加一项。这就是"工程化"和"能跑就行"的区别。
4. 执行查询并打印命中行数与 SQL 参数
配置和拼接都好了,现在把它接到真实连接上跑一遍。用 PyMySQL:
import pymysql def run_query(sql, params): conn = pymysql.connect( host=db_conf["host"], port=db_conf["port"], user=db_conf["user"], password=db_conf["password"], charset=db_conf["charset"], database=db_conf["database"], cursorclass=pymysql.cursors.DictCursor, ) try: with conn.cursor() as cursor: cursor.execute(sql, params) rows = cursor.fetchall() return rows finally: conn.close() if __name__ == "__main__": sql, params = build_like_query( q_conf["table"], q_conf["like_fields"], ["Z001B191108002R100", "2024"] ) rows = run_query(sql, params) print("命中行数:", len(rows)) print("SQL:", sql) print("参数:", params) for r in rows[:3]: print(r)cursorclass=DictCursor让结果以字典返回,字段名直接可读,调试时比元组友好得多。cursor.execute(sql, params)是参数化执行的关键——PyMySQL 会把params安全地转义后填入%s,你完全不用自己处理引号。
跑起来后,控制台应该打印类似:
命中行数: 17 SQL: SELECT * FROM `report` WHERE `field_a` LIKE %s AND `field_b` LIKE %s 参数: ['%Z001B191108002R100%', '%2024%']这条验证动作就是本篇的验收标准:命中行数大于 0,且参数列表里每个关键词都带上了%前后缀,说明多个模糊条件正确生效。如果命中 0 行,先别怀疑代码,去数据库里手动SELECT COUNT(*)用同样的%关键词%试一下,确认数据本身存在。
如果你想让脚本更聪明一点,比如把自然语言"帮我查 field_a 包含 Z001 且 field_b 包含 2024 的记录"转成上面的fields和keywords,可以调一次模型。用 TaoToken 的话,走 OpenAI 兼容接口:
from openai import OpenAI client = OpenAI( base_url=tk_conf["base_url"], api_key=tk_conf["api_key"], timeout=tk_conf["timeout"], ) resp = client.chat.completions.create( model=tk_conf["default_model"], messages=[ {"role": "system", "content": "把用户查询意图拆成字段和关键词,输出 JSON。"}, {"role": "user", "content": "field_a 包含 Z001,field_b 包含 2024"}, ], ) print(resp.choices[0].message.content)这样模型只负责"理解意图",真正的 SQL 拼接和执行仍然由你的参数化代码完成,职责清晰,也安全。想直接在线试模型效果,可以用 https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
5. 本篇常见报错排查
报错一:pymysql.err.ProgrammingError: (1064, "You have an error in your SQL syntax")八成是字段名撞了 MySQL 关键字,比如字段叫order、group。解决办法就是上面代码里的反引号`{field}`,别省。另一种可能是你手动拼了字符串,某个关键词里带了单引号,参数化能彻底避免这类问题。
报错二:TypeError: not all arguments converted during string formatting参数个数和%s占位符数量对不上。检查build_like_query里fields和keywords长度是否一致——我在函数开头加了ValueError校验,就是为了提前拦住。如果你在 SQL 里还写了别的%s(比如分页LIMIT %s),记得把对应参数也 append 进params。
报错三:中文模糊匹配查不到结果先确认连接charset是utf8mb4,再确认表和字段的排序规则(collation)不是latin1。可以用SHOW FULL COLUMNS FROM report;看字段的 Collation。如果表本身是 latin1,中文 LIKE 基本没戏,得改表结构。
报错四:pymysql.err.OperationalError: (2003, "Can't connect to MySQL server")本地开发最常见的是 MySQL 没启动,或者host写成了localhost但实际走的是 socket 而非 TCP。改成127.0.0.1通常能解决。端口不是默认 3306 的话,检查settings.json里的port。
报错五:模型调用返回 401说明api_key不对或没带上。确认settings.json里填的是 TaoToken 控制台生成的 Key,且base_url是https://taotoken.net/api(注意结尾没有多余斜杠)。Key 的生成和管理在 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
6. 把配置和代码收进你的项目
到这里,一份能跑的多条件模糊查询骨架就齐了:settings.json管配置,build_like_query管拼接,run_query管执行,模型调用作为可选的"意图解析"层。加字段、改关键词、换表名,都只动配置或调用参数,不用再碰 SQL 字符串。
如果你后续要把这套脚本接到更长期的编码任务或 Agent 流程里,比如让模型自动生成查询、批量跑报表,可以了解一下 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,它更适合持续性的开发场景。接入细节和参数说明在文档里:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
最后留个实用习惯:每次改完like_fields,先跑一遍打印sql和params的那两行,肉眼确认占位符和参数一一对应,再连数据库。这个动作花不了三秒,但能省掉大部分"为什么查不到"的排查时间。