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());
    }
}
相关推荐
白露与泡影11 分钟前
SpringBoot前后端token自动续期方案
spring boot·后端·状态模式
青梅主码19 分钟前
重磅!《人工智能和大型语言模型的研究前景:应用、挑战和未来方向》:代理型 AI 和大语言模型是否可以整合?
后端
hui函数35 分钟前
Flask-WTF表单验证全攻略
后端·python·flask·web·表单验证
喵手38 分钟前
Java异常处理最佳实践:如何避免捕获到不必要的异常?
java·后端·java ee
helloyaren1 小时前
Docker Desktop里搭建Mysql 9.4主从复制的保姆级教程
mysql·技术·主从复制
猿java1 小时前
精通MySQL却不了解OLAP和 OLTP,正常吗?
java·后端·面试
喵手1 小时前
Java中的HashMap:你了解它的工作原理和最佳实践吗?
java·后端·java ee
weixin_456588151 小时前
【java面试day16】mysql-覆盖索引
java·mysql·面试
冷月半明1 小时前
把离线 Python 项目塞进 Docker:从 0 到 1 的踩坑实录,一口气讲透 10 个最常见困惑
后端
用户298698530141 小时前
如何使用 Spire.Doc 在 Word 中查找和替换文本?
后端