含多单引号输入场景下如何使用参数化查询实现LIKE/ILIKE查询
带LIKE/ILIKE的参数化查询构造规范
核心原则:参数化查询的驱动会自动处理所有输入内容中的特殊字符(包括单引号、反斜杠等)的转义流程,完全不需要手动对输入做转义或字符串拼接,从根源上避免SQL语法错误和注入风险。
1. 基础LIKE/ILIKE参数化的正确实现
构造这类查询时,不要将通配符%直接写在SQL语句的占位符两侧硬编码,优先在应用代码层完成通配符和用户输入的拼接,再将完整的匹配值传入参数占位符,这种写法对所有数据库、所有数据库驱动都有最好的兼容性。
正确写法示例
以PostgreSQL(支持原生ILIKE不区分大小写匹配)、Python psycopg2驱动为例:
- 拿到用户的原始输入值
- 在代码层完成通配符拼接,生成最终的匹配参数
match_param = f"%{user_input}%" - 执行完全参数化的查询,SQL语句中不嵌入任何用户输入的原始内容:
cursor.execute( "SELECT * FROM device_records WHERE device_name ILIKE %s", (match_param,) )
这种写法下,哪怕用户输入中包含大量单引号(比如枚举格式字符串'ACCESS_POINT', 'DESKTOP', 'GATEWAY'),驱动都会自动将单引号转义为SQL安全的格式,既不会触发语法错误,也不会有注入风险。
绝对禁止的错误写法
- 不要手动拼接原始SQL字符串:例如直接通过字符串拼接生成
"SELECT * FROM device_records WHERE device_name LIKE '%" + user_input + "%'",这类写法完全失去了参数化查询的防护作用,只要输入包含单引号就会直接触发SQL语法错误,还存在严重的注入风险。 - 不要手动对输入中的单引号做转义处理(例如将单引号替换为两个单引号再拼接):参数化流程已经完成了所有必要的转义,手动处理反而会导致最终匹配的内容和用户原始输入不一致。
2. 处理带单引号的枚举格式输入的场景
针对输入为'ACCESS_POINT', 'DESKTOP', 'GATEWAY'...这类带单引号、逗号分隔的枚举字符串场景,可根据业务需求选择两种实现方案:
方案1:需要对完整枚举串做模糊匹配
直接将用户传入的完整原始枚举串,在代码层拼好通配符后作为参数值传入占位符即可,不需要手动修改枚举串里的单引号:
# 原始输入:"'ACCESS_POINT', 'DESKTOP', 'GATEWAY'" raw_enum_input = get_user_input() match_param = f"%{raw_enum_input}%" cursor.execute( "SELECT * FROM device_records WHERE associated_tags ILIKE %s", (match_param,) )
方案2:需要匹配枚举串中任意一个枚举项的值
相比LIKE模糊匹配,这种场景优先用精确匹配的IN查询,效率更高、避免通配符带来的全表扫描问题:
- 在代码层将原始枚举串按逗号拆分,逐个去掉每个枚举项前后的空白和包裹的单引号,生成纯值列表
value_list = [item.strip().strip("'") for item in raw_enum_input.split(",")] - 将列表作为参数值传入驱动支持的数组占位符语法,完成参数化查询:
# PostgreSQL 场景,用ANY匹配数组值 cursor.execute( "SELECT * FROM device_records WHERE device_type = ANY(%s)", (value_list,) )
3. 常见避坑提示
- 不要为了省事在SQL层硬编码拼接通配符:例如写
ILIKE '%' || :param || '%'这类逻辑,虽然在部分数据库下可以运行,但不同数据库的字符串拼接语法存在差异(例如SQL Server用+拼接、MySQL用CONCAT()函数拼接),可移植性很差,优先在代码层完成通配符拼接是更稳妥的选择。 - 跨数据库实现不区分大小写匹配时,不需要硬编码ILIKE:如果你的查询需要兼容不支持ILIKE关键字的数据库(例如MySQL),可以通过同时包裹字段和参数值为小写的方式实现,同样用参数化传值即可:
SELECT * FROM device_records WHERE LOWER(device_name) LIKE LOWER(%s)
内容的提问来源于stack exchange,提问作者Arif Shariar
相关产品推荐
相关产品推荐

