SuperAPI AI 工具集

like-wildcard-escaping-ids

数据与分析

查看项目

更新于 10-17 16:16

技能介绍

用途

本页聚焦一个具体的 SQL 陷阱:当使用 LIKE 在文本或 JSON 列中匹配机器生成的 ID 时,ID 中若包含 _ 或 %,会被 LIKE 当成通配符,从而静默地扩大匹配范围。文章旨在说明问题成因、给出正确的模式构造方式,并列出常见陷阱与验证方法。

主要能力

  • 解释 LIKE 并非子串匹配:_ 匹配任意单个字符,% 匹配任意长度字符序列,且默认就是生效的。
  • 提供嵌套 replace() 转义方案:先转义反斜杠,再转义 _,再转义 %。
  • 强调必须使用 ESCAPE 子句声明转义字符,否则在许多引擎上转义无效。
  • 说明参数绑定不会转义 LIKE 通配符,绑定参数同样需要处理。
  • 指出当值来自列时,宿主语言无法转义,必须将 replace() 写入 SQL。
  • 说明裸子串匹配不是身份匹配,应加上存储格式保证的定界符。
  • 提醒多层转义需要逐层确认,建议记录驱动最终收到的 SQL 文本。
  • 指出前导 % 会导致全表扫描且索引无法帮助,并给出替代方案(规范化到连接表)。
  • 提供暴露量统计与近失配测试的验证方法。

使用方式

文章给出了一段示例 SQL(适配场景:检查某歌曲 ID 是否仍出现在任何播放列表的 JSON ID 数组中):

NOT EXISTS (
  SELECT 1 FROM playlist
   WHERE playlist.tracks LIKE
         '%"' || replace(replace(replace(song.trackId, '\', '\\'), '_', '\_'), '%', '\%') || '"%'
         ESCAPE '\'
)

其中嵌套 replace() 用于中和通配符,'%"' || … || '"%' 用于将匹配固定到完整 token。文章还给出统计暴露量的查询:

SELECT COUNT(*) FROM song WHERE trackId LIKE '%\_%' ESCAPE '\';

以及测试建议:插入两个仅在通配符位置不同的 ID(如 abc_def 与 abcXdef),正确转义应只返回一行,未转义会返回两行。

适用场景

  • 使用 LIKE 在文本列或 JSON 列中匹配机器生成 ID 的 SQL 查询。
  • 需要判断“某 ID 是否仍被引用”的清理/扫描任务。
  • 包含查询中需要精确匹配某个 ID 的场景。
  • 使用 SQLite、SQL Server、Oracle、PostgreSQL、MySQL 等数据库且涉及 LIKE 模式构造的开发与审查。

限制

  • 前导 % 导致全表扫描,索引无法使用;仅适合扫描表行数较少(如几百行)的情况,百万行级别应改为规范化连接表。
  • 不同数据库对 LIKE 默认转义字符的定义不同:SQLite、SQL Server、Oracle 和 SQL 标准未定义默认转义字符;PostgreSQL 和 MySQL 默认反斜杠。
  • 转义顺序必须正确:反斜杠必须最先转义。
  • 多层转义(宿主语言字符串、SQL、其他处理层)需要逐层确认,建议记录最终 SQL 文本。
  • 定界符需根据实际存储格式选择:JSON 字符串数组可用引号;逗号分隔列需要不同定界符;裸 ID 存储应使用 = 而非 LIKE。