使用GROUP BY进行Mysql FULLTEXT搜索,保持得分最高的行值

时间:2012-09-25 04:16:00

标签: mysql group-by left-join inner-join full-text-search

在两个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_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 个答案:

答案 0 :(得分:2)

SELECT MATCH_SCORE_TITLE, MAX(MATCH_SCORE_TAGS), ID FROM <tablename>........GROUP BY ID

答案 1 :(得分:1)

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

例如:

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字段。

如果你按照我的建议添加一个groups_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

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

答案 2 :(得分:0)

您可以将SELECT DISTINCT ID与ORDER BY MATCH_SCORE_TITLE和MATCH_SCORE_TAGS

一起使用

答案 3 :(得分: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

another answer中查看LEFT JOIN技巧的快速解释。

修改:删除了不必要的GROUP BY子句。