SQL多OR条件查询:如何返回匹配的关键词?
当然可以做到!其实核心思路就是在查询里额外判断每条记录到底匹配了哪个(或哪些)条件,把对应的关键词返回出来。下面我给你几种不同场景下的实现方式,你可以根据自己用的数据库和需求来选:
场景1:只需要返回第一个匹配的关键词
如果你的记录只会匹配一个关键词,或者你只关心最先匹配到的那个,用CASE语句就足够了。举个例子,假设你要匹配文章标题或内容里的多个关键词,同时返回post id、url,并且当匹配到"附件相关关键词"时返回attachment_id:
SELECT post_id, url, -- 逐个判断条件,返回对应的匹配关键词 CASE WHEN title LIKE '%数据分析%' THEN '数据分析' WHEN content LIKE '%Python教程%' THEN 'Python教程' WHEN title LIKE '%数据库优化%' OR content LIKE '%数据库优化%' THEN '数据库优化' -- 可以继续添加更多关键词的判断 END AS matched_keyword, -- 特定场景返回attachment_id:比如匹配到"数据库优化"时返回 CASE WHEN CASE WHEN title LIKE '%数据分析%' THEN '数据分析' WHEN content LIKE '%Python教程%' THEN 'Python教程' WHEN title LIKE '%数据库优化%' OR content LIKE '%数据库优化%' THEN '数据库优化' END = '数据库优化' THEN attachment_id ELSE NULL END AS attachment_id FROM posts WHERE title LIKE '%数据分析%' OR content LIKE '%Python教程%' OR (title LIKE '%数据库优化%' OR content LIKE '%数据库优化%');
这里要注意,CASE是从上到下判断的,一旦匹配到第一个条件就会停止,所以如果你的关键词有重叠,要把优先级高的放在前面。
场景2:返回所有匹配的关键词
如果一条记录可能匹配多个关键词,想要把所有匹配的都列出来,就需要用子查询+聚合函数的方式。不同数据库的聚合函数略有不同,我给你举两个常用的例子:
PostgreSQL 版本
用UNNEST把每个关键词的判断结果拆成行,再用STRING_AGG把它们合并成逗号分隔的字符串:
SELECT post_id, url, STRING_AGG(matched_keyword, ', ') AS matched_keywords, -- 判断是否包含特定关键词,返回attachment_id CASE WHEN '数据库优化' = ANY(ARRAY_AGG(matched_keyword)) THEN attachment_id ELSE NULL END AS attachment_id FROM ( SELECT post_id, url, attachment_id, -- 把每个关键词的判断结果放到数组里,再拆成单行 UNNEST(ARRAY[ CASE WHEN title LIKE '%数据分析%' THEN '数据分析' END, CASE WHEN content LIKE '%Python教程%' THEN 'Python教程' END, CASE WHEN title LIKE '%数据库优化%' OR content LIKE '%数据库优化%' THEN '数据库优化' END ]) AS matched_keyword FROM posts WHERE title LIKE '%数据分析%' OR content LIKE '%Python教程%' OR (title LIKE '%数据库优化%' OR content LIKE '%数据库优化%') ) AS subquery WHERE matched_keyword IS NOT NULL -- 过滤掉没有匹配的空值 GROUP BY post_id, url, attachment_id;
MySQL 版本
用GROUP_CONCAT来合并匹配的关键词,子查询里生成每个关键词的判断:
SELECT post_id, url, GROUP_CONCAT(DISTINCT matched_keyword SEPARATOR ', ') AS matched_keywords, -- 判断是否包含特定关键词 CASE WHEN FIND_IN_SET('数据库优化', GROUP_CONCAT(DISTINCT matched_keyword)) THEN attachment_id ELSE NULL END AS attachment_id FROM ( SELECT post_id, url, attachment_id, CASE WHEN title LIKE '%数据分析%' THEN '数据分析' END AS matched_keyword FROM posts WHERE title LIKE '%数据分析%' UNION ALL SELECT post_id, url, attachment_id, CASE WHEN content LIKE '%Python教程%' THEN 'Python教程' END AS matched_keyword FROM posts WHERE content LIKE '%Python教程%' UNION ALL SELECT post_id, url, attachment_id, CASE WHEN title LIKE '%数据库优化%' OR content LIKE '%数据库优化%' THEN '数据库优化' END AS matched_keyword FROM posts WHERE title LIKE '%数据库优化%' OR content LIKE '%数据库优化%' ) AS subquery WHERE matched_keyword IS NOT NULL GROUP BY post_id, url, attachment_id;
场景3:用全文搜索优化匹配
如果你的数据量很大,用LIKE会比较慢,建议用数据库的全文搜索功能。比如MySQL的MATCH AGAINST、PostgreSQL的tsvector,这些不仅更快,还能更精准地提取匹配的关键词。以PostgreSQL为例,用ts_headline可以直接高亮匹配的关键词,或者用ts_query拆分关键词来判断:
SELECT post_id, url, -- 提取所有匹配的关键词 array_to_string(tsvector_to_array(to_tsvector('english', title || ' ' || content) @@ plainto_tsquery('english', '数据分析 Python教程 数据库优化')), ', ') AS matched_keywords, CASE WHEN to_tsvector('english', title || ' ' || content) @@ plainto_tsquery('english', '数据库优化') THEN attachment_id ELSE NULL END AS attachment_id FROM posts WHERE to_tsvector('english', title || ' ' || content) @@ plainto_tsquery('english', '数据分析 Python教程 数据库优化');
总之,核心就是通过条件判断把匹配的关键词显性化,再结合你的需求返回对应的post id、url和attachment_id。根据你的数据库类型和数据规模选合适的方法就行~
内容的提问来源于stack exchange,提问作者Raf

