MySQL中FIND_IN_SET函数的深度解析与应用指南

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!


一、FIND_IN_SET基础用法

1. 函数语法

sql 复制代码
FIND_IN_SET(str, strlist)
  • str:待查找的字符串。
  • strlist:逗号分隔的字符串列表。
  • 返回值
    • 找到:返回位置索引(从1开始)。
    • 未找到:返回 0
    • 任一参数为 NULL:返回 NULL

2. 简单示例

sql 复制代码
SELECT FIND_IN_SET('apple', 'apple,banana,orange');  -- 返回 1
SELECT FIND_IN_SET('banana', 'apple,banana,orange'); -- 返回 2
SELECT FIND_IN_SET('grape', 'apple,banana,orange');  -- 返回 0

二、典型使用场景(及风险)

1. 简单标签匹配

假设有一个 articles 表,其中 tags 字段存储逗号分隔的标签:

sql 复制代码
SELECT * FROM articles 
WHERE FIND_IN_SET('tech', tags) > 0;

问题:全表扫描、无法利用索引、数据冗余。

2. 权限校验

用户表 users 包含 permissions 字段(如 'read,write,delete'):

sql 复制代码
SELECT * FROM users 
WHERE FIND_IN_SET('write', permissions) > 0;

隐患:权限管理耦合在字符串中,难以维护。


三、深度解析

1. 底层实现机制

  • 字符串分割 :将 strlist 按逗号拆分为临时数组。
  • 遍历匹配:逐个对比元素,直到找到匹配项。
  • 时间复杂度:O(n),对大字符串或大数据集性能急剧下降。

2. 性能问题实测

sql 复制代码
EXPLAIN SELECT * FROM large_table WHERE FIND_IN_SET('value', csv_column);
  • 执行计划 :通常显示 ALL(全表扫描),即使 csv_column 有索引。

3. 与替代方案的对比

方法 性能 可维护性 索引支持 符合范式
FIND_IN_SET 不支持
关联表 (JOIN) 支持
JSON_CONTAINS 部分支持

四、更优替代方案

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

sql 复制代码
-- 原始表
CREATE TABLE articles (
    id INT PRIMARY KEY,
    title VARCHAR(255)
);

-- 标签关联表
CREATE TABLE article_tags (
    article_id INT,
    tag VARCHAR(50),
    PRIMARY KEY (article_id, tag),
    FOREIGN KEY (article_id) REFERENCES articles(id)
);

-- 查询示例
SELECT a.* FROM articles a
JOIN article_tags t ON a.id = t.article_id
WHERE t.tag = 'tech';

优势:索引支持、易于统计、避免数据冗余。

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

sql 复制代码
ALTER TABLE articles ADD COLUMN tags JSON;

-- 查询示例
SELECT * FROM articles 
WHERE JSON_CONTAINS(tags, '"tech"', '$');

优势:支持JSON索引、查询效率较高。

3. 使用ES(ES主要是帮助业务检索负责数据)


五、何时可以考虑使用FIND_IN_SET?

  1. 临时数据分析:快速查询非规范化数据。
  2. 小型静态表:数据量小且不频繁查询。
  3. 遗留系统适配:暂时无法修改表结构时。

六、总结

  • 慎用场景FIND_IN_SET 在大多数生产环境中应避免使用,尤其是在高并发或大数据表上。
  • 设计原则
    • 优先遵循数据库第一范式(1NF)。
    • 使用关联表或JSON类型替代逗号分隔字符串。
  • 迁移建议:逐步将现有逗号分隔字段重构为关联表,结合业务需求评估代价。
相关推荐
爱丽_1 小时前
深入理解 Java Socket 编程与线程池:从阻塞 I/O 到高并发处理
java·开发语言
济南壹软网络科技有限公司1 小时前
云脉IM的高性能消息路由与离线推送机制摘要:消息的“零丢失、低延迟”之道
java·即时通讯源码·开源im·企业im
Seven971 小时前
剑指offer-46、孩⼦们的游戏(圆圈中最后剩下的数)
java
serendipity_hky1 小时前
互联网大厂Java面试故事:核心技术栈与场景化业务问题实战解析
java·spring boot·redis·elasticsearch·微服务·消息队列·内容社区
我真不会起名字啊1 小时前
C、C++中的sprintf和stringstream的使用
java·c语言·c++
十点摆码1 小时前
Spring Boot2 使用 Flyway 管理数据库版本
java·flyway·数据库脚本·springboo2·数据库脚本自动管理
望道同学1 小时前
PMP/信息系统项目管理师 9 张 思维导图【考试必备】
前端·后端·程序员
毕设源码-钟学长1 小时前
【开题答辩全过程】以 基于Javaweb的电动汽车充电桩管理系统为例,包含答辩的问题和答案
java·spring boot
码事漫谈1 小时前
C++11到C++23语法糖万字详解
后端
码事漫谈2 小时前
别人的C#看着难受?可能是你不清楚这些语法糖
后端