Python实现SQL查询:返回同时包含所有指定流派的动漫记录
解决同时匹配多个流派的SQL查询问题
这个需求很典型——从存储多流派的字段里筛选出同时包含所有目标流派的记录,而不是只要有一个就返回。我根据不同的字段存储格式和数据库类型,给你几种实用的解决方案:
场景1:genre是逗号分隔的字符串(比如"Action,Comedy,Romance")
这种情况要注意精确匹配,不能直接用LIKE '%Action%',否则会误匹配类似"Action-adventure"的流派。
方案1:用数据库内置的多值判断函数
- MySQL/MariaDB:用
FIND_IN_SET,它会精准匹配逗号分隔列表中的值:
SELECT * FROM anime WHERE FIND_IN_SET('Action', genre) > 0 AND FIND_IN_SET('Comedy', genre) > 0 AND FIND_IN_SET('Romance', genre) > 0;
FIND_IN_SET返回目标值在列表中的位置,大于0就表示存在,三个条件同时满足就说明记录包含所有三个流派。
- PostgreSQL:把字符串转成数组后用包含操作符
@>:
SELECT * FROM anime WHERE STRING_TO_ARRAY(genre, ',') @> ARRAY['Action', 'Comedy', 'Romance'];
@>表示左边的数组完全包含右边的所有元素,正好符合你的需求。
方案2:用正则表达式精准匹配
如果你的数据库支持正则,可以用正则来匹配每个流派的独立存在:
-- MySQL示例 SELECT * FROM anime WHERE genre REGEXP '(^|,)Action(,|$)' AND genre REGEXP '(^|,)Comedy(,|$)' AND genre REGEXP '(^|,)Romance(,|$)';
(^|,)匹配字符串开头或逗号,(,|$)匹配逗号或字符串结尾,确保不会匹配到部分包含的流派名称。
场景2:genre是数组类型(比如PostgreSQL的text[]、MySQL的JSON数组)
这种原生多值类型的查询会更简单:
- PostgreSQL数组:直接用
@>操作符:
SELECT * FROM anime WHERE genre @> ARRAY['Action', 'Comedy', 'Romance'];
- MySQL JSON数组:多次使用
JSON_CONTAINS判断每个流派是否存在:
SELECT * FROM anime WHERE JSON_CONTAINS(genre, '"Action"') AND JSON_CONTAINS(genre, '"Comedy"') AND JSON_CONTAINS(genre, '"Romance"');
注意这里要给流派名称加双引号,因为JSON字符串必须用双引号包裹。
通用方案:拆分多值字段后分组统计
如果上面的方法不适用,你可以先把多值字段拆分成单独的行,再通过分组统计来筛选:
-- MySQL示例,用递归CTE拆分逗号分隔字符串 WITH split_genres AS ( SELECT id, genre, SUBSTRING_INDEX(SUBSTRING_INDEX(genre, ',', n), ',', -1) AS single_genre FROM anime -- 这里的numbers表要包含足够多的数字,覆盖最多的流派数量 JOIN (SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) numbers ON CHAR_LENGTH(genre) - CHAR_LENGTH(REPLACE(genre, ',', '')) >= n - 1 ) SELECT id, genre FROM split_genres WHERE single_genre IN ('Action', 'Comedy', 'Romance') GROUP BY id, genre HAVING COUNT(DISTINCT single_genre) = 3;
思路是把每条记录的流派拆成单独行,筛选出目标流派后,分组统计不同流派的数量等于3,就说明原记录同时包含所有三个流派。
优化建议:规范化数据库设计
如果经常需要做这种多流派的精确查询,最稳妥的方式是规范化你的数据库:
- 创建一个
anime_genres关联表,字段为anime_id(关联动漫表的主键)和genre(单一流派) - 每个动漫的每个流派单独存一行
这样查询会更高效,也避免了字符串操作的各种坑:
SELECT a.* FROM anime a JOIN anime_genres ag ON a.id = ag.anime_id WHERE ag.genre IN ('Action', 'Comedy', 'Romance') GROUP BY a.id HAVING COUNT(DISTINCT ag.genre) = 3;
内容的提问来源于stack exchange,提问作者user9347721
相关产品推荐
相关产品推荐

