mysql find_in_set(建议收藏)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

在数据库查询中,我们常常需要根据某个字段中的多个值进行筛选。例如,一个学生选修了多门课程,这些课程可能被存储为逗号分隔的字符串。此时,MySQLFIND_IN_SET()函数便能大显身手。它像一把精准的“字符串筛子”,帮助开发者快速定位符合条件的数据。本文将从基础语法、进阶用法、性能优化到常见问题,逐步解析这一函数的使用场景与技巧,适合编程初学者和中级开发者深入理解。


基础语法与核心原理

什么是 FIND_IN_SET?

FIND_IN_SET(str, str_list)MySQL 提供的字符串函数,用于在逗号分隔的字符串列表中查找某个字符串的位置。其返回值为:

  • 正整数:表示该字符串在列表中的位置(从1开始计数);
  • 0:表示未找到;
  • NULL:当任意参数为 NULL 时返回。

形象比喻
可以将 FIND_IN_SET 想象为一个“字符串筛子”,将输入的字符串列表(如 "a,b,c")拆解为多个独立项,逐项检查目标字符串是否存在于其中。

基础语法示例

SELECT FIND_IN_SET('b', 'a,b,c,d'); -- 返回 2  
SELECT FIND_IN_SET('e', 'a,b,c,d'); -- 返回 0  
SELECT FIND_IN_SET('b', NULL);      -- 返回 NULL  

实际案例:学生选课系统

假设有一个学生表 students,其中 courses 字段存储学生选修的课程(如 "math,physics"),我们需要查询选修了“math”课程的学生:

CREATE TABLE students (  
  id INT PRIMARY KEY,  
  name VARCHAR(50),  
  courses TEXT  
);  

INSERT INTO students VALUES  
(1, 'Alice', 'math,physics'),  
(2, 'Bob', 'physics'),  
(3, 'Charlie', 'math,biology');  

SELECT * FROM students  
WHERE FIND_IN_SET('math', courses) > 0;  
-- 返回 Alice 和 Charlie 的记录  

进阶用法与逻辑扩展

条件筛选的多种组合

FIND_IN_SET 可与其他查询条件结合,实现复杂筛选。例如,查找同时选修了“math”和“physics”的学生:

SELECT * FROM students  
WHERE  
  FIND_IN_SET('math', courses) > 0  
  AND FIND_IN_SET('physics', courses) > 0;  
-- 返回 Alice  

子查询与动态参数

若目标字符串需动态生成,可结合子查询使用。例如,查询选修了其他学生选修过的所有课程的学生:

SELECT * FROM students s  
WHERE  
  FIND_IN_SET(  
    (SELECT courses FROM students WHERE id = 1),  
    s.courses  
  ) > 0;  
-- 假设学生1的courses是 "math,physics",则筛选出包含这两个课程的学生  

注意事项:参数顺序与空格

  • 参数顺序不可颠倒FIND_IN_SET('math', 'math,physics') 正确,而 FIND_IN_SET('math,physics', 'math') 会返回 0。
  • 空格不影响匹配FIND_IN_SET('math', 'math ,physics') 仍返回 1,但建议存储时保持格式统一。

性能优化与替代方案

为何使用 FIND_IN_SET 可能影响性能?

FIND_IN_SET 的核心问题在于:无法利用索引。因为其操作依赖于字符串拆分,数据库无法通过索引快速定位数据,导致查询可能全表扫描。例如,当表数据量极大时,以下查询可能变慢:

SELECT * FROM large_table  
WHERE FIND_IN_SET('rare_value', tags) > 0;  

优化策略:重构数据结构

为避免性能瓶颈,可考虑以下替代方案:

1. 使用关联表(规范化设计)

将多对多关系(如“学生-课程”)拆分为三张表:

CREATE TABLE students (id INT PRIMARY KEY, name VARCHAR(50));  
CREATE TABLE courses (id INT PRIMARY KEY, name VARCHAR(50));  
CREATE TABLE student_courses (  
  student_id INT,  
  course_id INT,  
  PRIMARY KEY (student_id, course_id),  
  FOREIGN KEY (student_id) REFERENCES students(id),  
  FOREIGN KEY (course_id) REFERENCES courses(id)  
);  

此方案允许通过索引快速查询,例如:

SELECT s.*  
FROM students s  
JOIN student_courses sc ON s.id = sc.student_id  
JOIN courses c ON sc.course_id = c.id  
WHERE c.name = 'math';  

2. 使用 JSON 类型(MySQL 5.7+)

若需保留“字符串列表”特性,可改用 JSON 类型存储,利用 JSON_CONTAINS 函数查询:

ALTER TABLE students MODIFY courses JSON;  
INSERT INTO students (id, name, courses) VALUES  
(1, 'Alice', '["math", "physics"]'),  
(2, 'Bob', '["physics"]');  

SELECT * FROM students  
WHERE JSON_CONTAINS(courses, '"math"', '$');  

此方法支持索引优化,但需注意 JSON 类型的存储限制。


常见问题与解决方案

问题1:为什么返回结果为空?

可能原因

  • 目标字符串的大小写或空格与存储值不一致(如查询 'Math' 而存储的是 'math')。
  • 字段值为 NULL,导致返回 NULL 而非 0。

解决方法

  • 使用 LOWER()TRIM() 函数统一格式:
    WHERE FIND_IN_SET(LOWER('Math'), LOWER(courses)) > 0  
    
  • 处理 NULL 值:
    WHERE COALESCE(FIND_IN_SET('math', courses), 0) > 0  
    

问题2:如何避免全表扫描?

策略

  • 小数据量场景:若表数据量较小(如 < 10万条),FIND_IN_SET 可接受。
  • 添加虚拟列索引(MySQL 8.0+):
    ALTER TABLE students  
    ADD COLUMN has_math INT AS (FIND_IN_SET('math', courses) > 0) VIRTUAL;  
    CREATE INDEX idx_has_math ON students (has_math);  
    

    此时查询可直接使用 WHERE has_math = 1,利用索引加速。


结论

MySQL FIND_IN_SET 是处理逗号分隔字符串的实用工具,尤其适用于简单场景的快速开发。然而,随着数据规模增长,需谨慎评估其性能影响,并考虑通过规范化设计或 JSON 类型优化架构。开发者应根据实际需求,权衡“开发效率”与“查询性能”,合理选择实现方案。

掌握这一函数后,读者可进一步探索 LOCATE()SUBSTRING_INDEX() 等字符串函数,逐步构建对复杂数据操作的系统认知。

最新发布