MySQL中模糊匹配like的一个坑

最开始展示一下两个极其类似的SQL查询语句

sql 复制代码
SELECT *
 FROM saas_knowledge_container kc
 WHERE kc.tenant_id = '1953660902800752640'
 AND type <> 'PERSONAL_KNOWLEDGE'
 AND (kc.visibility_range = 'ENTERPRISE_PUBLIC'
 OR (kc.visibility_range = 'MEMBERS_ONLY'
 AND EXISTS (SELECT 1
 FROM saas_knowledge_container_permission p
 LEFT JOIN kb_department d
 ON d.code = p.membership_code
 WHERE p.container_id = kc.code
 AND p.tenant_id = kc.tenant_id
 AND ((p.membership_type = 'USER'
 AND p.membership_code = '1958104631190691840')
 OR (p.membership_type = 'ORGANIZATION'
 AND ((d.full_path LIKE concat('%', '1958103924488216576,1958103969639899136,1958104007833231360', '%'))))))));
sql 复制代码
 select *
 FROM saas_knowledge_container kc
 WHERE kc.tenant_id = '1953660902800752640'
 AND type <> 'PERSONAL_KNOWLEDGE'
 AND ( kc.visibility_range = 'ENTERPRISE_PUBLIC'
 OR ( kc.visibility_range = 'MEMBERS_ONLY'
 AND exists (
 SELECT 1
 FROM saas_knowledge_container_permission p
 LEFT JOIN kb_department d
 ON d.code = p.membership_code
 WHERE p.container_id = kc.code
 AND p.tenant_id = kc.tenant_id
 AND ( (p.membership_type = 'USER'
 AND p.membership_code = '1958104631190691840')
 OR ( p.membership_type = 'ORGANIZATION'
 AND ( ( d.full_path like concat('%', '1958103924488216576', '%')
 OR d.full_path like concat('%', '1958103969639899136', '%')
 OR d.full_path like concat('%', '1958104007833231360', '%') ) ) ) ) ) ) ) order by kc.create_time desc

上述两个SQL,除了最后的full_path路径匹配规则不一样,其余都相同。上述两个SQL的效果是SQL1查询不出数据📊,SQL2可以查询出对于的数据。一开始看对着SQL的寓意进行分析没有发现明显的问题。。。。

为什么会出现数据一个有一个无,需要了解一下mysql中的like用法。

  • 第一条语句把 3 个组织 ID 用逗号串成一个整体去 LIKE: LIKE concat('%', '1958103924488216576,1958103969639899136,1958104007833231360', '%') 等价于 LIKE '%1958103924488216576,1958103969639899136,1958104007833231360%'

  • 第二条语句把 3 个 ID 拆开,各自独立 LIKE: ... LIKE '%1958103924488216576%' OR ... LIKE '%1958103969639899136%' OR ... LIKE '%1958104007833231360%'

    还是问问AI给我分析一下是为什么,我迷茫了

所以按照上述AI提供的排查思路,我开始对数据进行分析,查询当前记录是否真的存在特殊字符

sql 复制代码
SELECT hex(full_path)
FROM kb_department
WHERE code = '1958104007833231360';

如果看到 20(空格)、0A(换行)、EF BB BF(BOM)、E2808B(零宽空格)等,就说明肉眼看不见的东西混进去了。 解决:UPDATE 去掉这些字符,或把 LIKE 模式前后各加一个 %

但是期待的结果并没有出现

于是后续继续按照别的思路进行排查,是不是visibility_range 没有命中,还是COUNT(*) > 0 但带 LIMIT 36 的语句不返回 → 就是 ORDER BY kc.create_time DESC 把这条记录挤到 36 行之外。结果尝试之后都不是。。。。

问题解决

思考了半天,终于在最后想到了原因。话不多说,继续来看两个SQL。

sql 复制代码
select * from kb_department where code = '1958104007833231360';
sql 复制代码
select * from kb_department where code = '1958103924488216576';

看到这里应该也发现原因,用户所在的部门编码是1958103924488216576,对应的全路径是1958103924488216576。使用like进行全匹配的规则不服务第一个查询不出的sql中全路径匹配规则,所以需要在路径获取到之后,其中之一也是比较好的解决方案的方式,可以通过代码进行一个字符串逗号分隔~,使用这种能避免一个字符集等问题,要是mysql中有Java一样的contains语法就好了!

java 复制代码
// 在调用Mapper之前,先处理字符串分割
List<String> processedOrgCodes = new ArrayList<>();
for (String orgCode : orgPermissions.getOrganizationCodes()) {
    if (orgCode != null && orgCode.contains(",")) {
        // 分割逗号分隔的字符串
        String[] codes = orgCode.split(",");
        for (String c : codes) {
            if (c != null && !c.trim().isEmpty()) {
                processedOrgCodes.add(c.trim());
            }
        }
    } else if (orgCode != null && !orgCode.trim().isEmpty()) {
        processedOrgCodes.add(orgCode.trim());
    }
}
相关推荐
爱勇宝10 分钟前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
AskHarries26 分钟前
工具失败时怎么办:重试、回滚、人工确认和风险提示
后端·程序员
苏三说技术2 小时前
Claude Code从失控到起飞,只用了这些技巧
后端
长栎3 小时前
写 for 循环写了十年,你却从没用过迭代器模式最狠的那一面
后端
LiaCode3 小时前
Redis 在生产项目的使用
前端·后端
用户559822481223 小时前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode3 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战3 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha3 小时前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn3 小时前
Docker 容器管理入门 — 从镜像到容器编排
后端