2

在两次 FULLTEXT 搜索中,我在书名和标签中查找搜索词,得到以下结果:

rScore tScore ID
...
1.235689725827653 0 406
0.928482055664062 2.37063312530518 406
0.928482055664062 0 406
0.453363467548853 0 520
...

我想拥有什么,所有重复的 ID 都已连接并获得最高分:

rScore tScore ID
...
1.235689725827653 2.37063312530518 406
0.453363467548853 0 520
...

,但在 GROUP BY 之后,ID 406 被分组在此行列中:

...
MATCH_SCORE_TITLE MATCH_SCORE_TAGS ID
0.928482055664062 0 406
0.453363467548853 0 520
...

如何对所有这些结果进行分组并保持每个 MATCH 的最大值?我知道这已经被问过并且可以通过 JOIN 来完成,但是我没有找到两行的组合,而且我的查询中已经有 JOINS,因为 TITLE 和 TAGS 位于两个不同的表中。

更新: 我有 3 个表,“registrants”(带有要搜索的标题的左表)、“registrants_tags”(左右表之间的关系表)和“tags”(带有要搜索的标签的右表。这是 SQL 查询的简化版本:


SELECT
 tags.tag,    (Also tried (GROUP_CONCAT(`tags`.`tag`) AS tags)
 MAX(MATCH(registrants.story_title) AGAINST('bob')) as rScore,
 MAX(MATCH(tags.tag) AGAINST('bob')) as tScore,
 registrants.id, registrants.story_title
FROM registrants 
LEFT JOIN registrants_tags ON registrants.id = registrants_tags.registrant 
LEFT JOIN tags ON registrants_tags.tag = tags.id
WHERE MATCH(registrants.story_title) AGAINST('bob')
 OR MATCH(tags.tag) AGAINST('bob')
GROUP BY registrants.id
ORDER BY (rScore + tScore) DESC

这给了我错误消息:“#1247 - 不支持引用'tscore'(引用组函数)”

4

4 回答 4

2

您可以按分数排序,并max在子查询上使用 a 来获取最终的首选行。

例如:

SELECT  id, story_title,
    max(match_score_title) as titleScore,
    max(match_score_tags) as as tagScore
FROM (
    SELECT
        MATCH(registrants.story_title) AGAINST('bob') as rScore,
        MATCH(tags.tag) AGAINST('bob') as tScore,
        registrants.id, registrants.story_title
    FROM
    registrants 
    LEFT JOIN registrant_tags on registrant_tags.registrant=registrant.id
    LEFT JOIN tags on tags.id=registrant_tags.tag
    WHERE rScore > 0 or tScore > 0
) AS score_matcher
group by ID
ORDER BY (rScore + tScore) DESC

那应该对你有用。它可能不是宇宙中最快的查询,因为它依赖于子查询,根据我的经验,在 MySQL 中这些子查询并没有得到很好的优化,但它应该可以得到你的结果。

您还可以将其重新设计为不同的子查询,以利用以下优势group_concat

SELECT
    MATCH(registrants.story_title) AGAINST('bob') as rScore,
    MATCH(tags.tag) AGAINST('bob') as tScore,
    registrants.id, registrants.story_title
FROM
registrants 
LEFT JOIN (
    FROM rtags.registrant, GROUP_CONCACT(DISTINCT tags.tag SEPARATOR ',') as tags
    FROM registrants_tags AS rtags
    INNER JOIN tags on tags.id=registrants_tags.id
    GROUP BY rtags.registrant
) AS grouped_tags ON registrants.id = grouped_tags.registrant 
WHERE rScore > 0 or tScore > 0
ORDER BY (rScore + tScore) DESC

如果在您的数据库中,您将“grouped_tags”字段添加到您的注册人表中,这将有所帮助,然后可以在其中添加一个全文索引 - 这将消除对 grouped_tags 子查询的需要。然后,每当有人更新特定注册人的标签时,grouped_tags 字段就会使用当前正确标签列表进行更新。

如果你按照我的建议添加一个 grouped_tags 字段(在界面中填充),你可以用这个替换整个查询,使用全文索引会非常快(但是全文索引需要使用 MyISAM,这是不太好)。

如果你这样做了,那么这肯定是我在这里列出的最快的查询。

SELECT
    MATCH(story_title) AGAINST('bob') as rScore,
    MATCH(grouped_tags) AGAINST('bob') as tScore,
    id, story_title
FROM
registrants 
WHERE rScore > 0 or tScore > 0
GROUP BY ID
ORDER BY (rScore + tScore) DESC

因此,有很多建议可以帮助您进行此查询,您使用哪种解决方案在很大程度上取决于您的数据集的大小以及查询需要多快。我建议做一些基准测试来找到最适合你的

于 2012-09-25T04:25:18.857 回答
2
SELECT MATCH_SCORE_TITLE, MAX(MATCH_SCORE_TAGS), ID FROM <tablename>........GROUP BY ID
于 2012-09-25T04:25:32.460 回答
0

您可以将 SELECT DISTINCT ID 与 ORDER BY MATCH_SCORE_TITLE 和 MATCH_SCORE_TAGS 一起使用

于 2012-09-25T04:18:57.923 回答
0

试试这个模式:

SELECT
    *
FROM registrants r1
LEFT JOIN registrants t2 ON r1.id = r2.id AND r1.MATCH_SCORE_TITLE > r2.MATCH_SCORE_TITLE
LEFT JOIN registrants_tags ON r1.registrants.id = registrants_tags.registrant 
LEFT JOIN tags ON registrants_tags.tag = tags.id
WHERE
    r2.id IS NULL AND
    (MATCH(r1.registrants.full_name) AGAINST('bob')
    OR MATCH(tags.tag) AGAINST('bob'))
ORDER BY (tscore + ascore) DESC

请参阅另一个答案LEFT JOIN中对技巧的快速解释。

编辑:删除了不必要的 GROUP BY 子句。

于 2012-09-25T14:48:12.023 回答