我有一个连接到 MySQL 歌曲数据库的网站,并且正在努力改进该网站上的全文搜索。使用 MATCH() 的简单查询可以按预期工作,但是一旦我向 SELECT 语句添加聚合函数,MATCH() 就不再返回相关结果。
我使用的查询有点复杂,但我能够将其简化为一个最小的示例:
CREATE TABLE `Song` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(300) DEFAULT NULL,
PRIMARY KEY (`id`),
FULLTEXT KEY `FTS_Songs` (`title`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO Song (`title`)
VALUES ('The Morning Breaks'), ('The Spirit of God'), ('Now Let Us Rejoice');
SELECT id, title,
MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE) AS exactTitleRelevance,
MATCH(title) AGAINST ('+spirit* +god*' IN BOOLEAN MODE) AS titleRelevance
FROM Song
GROUP BY id
ORDER BY exactTitleRelevance DESC, titleRelevance DESC;
SELECT id, title,
MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE) AS exactTitleRelevance,
MATCH(title) AGAINST ('+spirit* +god*' IN BOOLEAN MODE) AS titleRelevance,
COUNT(*) AS aggregateColumn
FROM Song
GROUP BY id
ORDER BY exactTitleRelevance DESC, titleRelevance DESC;
我做错了什么?是否可以在同一查询中同时使用 MATCH() 和 COUNT() 或 GROUP_CONCAT() 等聚合函数?
我在两台不同的服务器上尝试并得到了相同的结果 - 一台使用 MySQL 8.0.37,另一台使用 MySQL 8.3.0。
您可以使用
EXPLAIN
来检查MySQL如何执行查询:
EXPLAIN SELECT id, title,
MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE) AS exactTitleRelevance,
MATCH(title) AGAINST ('+spirit* +god*' IN BOOLEAN MODE) AS titleRelevance,
COUNT(*) AS aggregateColumn
FROM Song
GROUP BY id
ORDER BY exactTitleRelevance DESC, titleRelevance DESC;
这将给出:
id | 选择类型 | 桌子 | 分区 | 类型 | 可能的键 | 键 | key_len | 参考 | 行 | 过滤 | 额外 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 简单 | 歌曲 | 空 | 索引 | 主要,FTS_歌曲 | 小学 | 4 | 空 | 3 | 100.00 | 临时使用;使用文件排序 |
聚合函数
COUNT()
和 GROUP BY
改变了 MySQL 处理查询的方式。 MySQL 使用“id”(主)作为索引,而不是 FTS_Songs(全文),导致 MATCH()
无法按预期工作。
为了确保
MATCH()
准确计算相关性:
例如
WITH lookup AS (
SELECT
id,
title,
MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE) AS exactTitleRelevance,
MATCH(title) AGAINST ('+spirit* +god*' IN BOOLEAN MODE) AS titleRelevance
FROM Song
WHERE MATCH(title) AGAINST ('"the spirit of god"' IN BOOLEAN MODE)
)
SELECT
l.id,
l.title,
l.exactTitleRelevance,
l.titleRelevance,
COUNT(*) AS aggregateColumn
FROM lookup l
GROUP BY l.id, l.title, l.exactTitleRelevance, l.titleRelevance;