【问题标题】:mysql search where clause with each condition having different scoremysql搜索where子句,每个条件都有不同的分数
【发布时间】:2011-10-11 20:53:07
【问题描述】:

总和

我有两列,name 和 name_searchable。我想在这些列(有数百万行)中搜索并根据它们的匹配率返回结果。我有两个重要的标准;搜索应该是高效和快速的。我怎样才能做到这一点?

详细介绍

我打算创建一个包含数百万行的表。所以基本上,我创建了一个转储表来测试一百万行的查询。该表使用MyISAM 存储引擎,它的索引和主键是id 号。我要进行的搜索与名称字段有关,该字段是varchar 列。现在,基于查询,我想返回与查询部分或全部匹配的所有结果。因此,当用户搜索“björn borg”时,我想同时返回:

  • 比约恩博格
  • 比约恩博格斯
  • bjorn borg(注意 o)

等等……

这里的重要因素是= 运算符应始终返回比LIKE 运算符更高的排名。因此,'björn borg' 应该总是出现在 'bjorn borgus' 之前。

最近,我问了关于如何在变音符号不敏感模式下返回结果的问题,但不幸的是我无法让它工作。因此,我沿名称列创建了另一列,该列仅以英文字符存储名称。所以我们有namename_searchable 字段。

好吧,我用存储过程尝试了整个过程,但与普通查询相比,它显然真的很慢。因此,我想知道我是否可以根据它们匹配的 where 子句对结果进行排序。换句话说:

SELECT * FROM myUsers WHERE name = 'björn borg' OR name_searchable = 'bjorn borg' OR name LIKE '%björn borg%' OR name_searchable LIKE '%bjorn borg%'; 

所以基本上,这个想法是给每个条件不同的分数。我的意思是,虽然 name = 'björn borg' 应该有排名,比如 5,name_searchable LIKE '%bjorn borg%' 应该有 2 (第二个 4 分,第三个 3 分......)我怎么能使用 MySql 完成这项工作? (效率和速度对我来说很重要)

【问题讨论】:

  • 您可能最好运行多个独立查询,而不是尝试在一个中执行此操作。 where 子句不是进行此类计算的好地方。

标签: mysql performance search


【解决方案1】:

如果您不使用LIKE '%<text>%',您将获得更好的性能,因为这不会正确使用索引,您应该使用LIKE '<text>%'。我建议您考虑是否希望用户能够搜索 name_searchable LIKE '%s%' 以及当查询需要很长时间并返回太多结果时相关的性能损失。

你试过了吗

SELECT CASE WHEN name = 'björn borg' THEN 1 
  WHEN name_searchable = 'bjorn borg' THEN 2 
  WHEN name LIKE '%björn borg%' THEN 3 
  WHEN name_searchable LIKE '%bjorn borg%' THEN 4 ELSE 5 END AS rank, * 
FROM myUsers 
WHERE name = 'björn borg' 
  OR name_searchable = 'bjorn borg' 
  OR name LIKE '%björn borg%' 
  OR name_searchable LIKE '%bjorn borg%'
ORDER BY CASE WHEN name = 'björn borg' THEN 1 
  WHEN name_searchable = 'bjorn borg' THEN 2 
  WHEN name LIKE '%björn borg%' THEN 3 
  WHEN name_searchable LIKE '%bjorn borg%' THEN 4 ELSE 5 END

当然,最快的方法是添加LIMIT 1

另一种选择是仅在完全匹配失败时使用类似搜索:

SELECT CASE WHEN name = 'björn borg' THEN 1 
  WHEN name_searchable = 'bjorn borg' THEN 2 
  WHEN name LIKE '%björn borg%' THEN 3 
  WHEN name_searchable LIKE '%bjorn borg%' THEN 4 ELSE 5 END AS rank, * 
FROM myUsers 
WHERE name = 'björn borg' 
  OR name_searchable = 'bjorn borg' 
  OR (
    NOT EXISTS (SELECT TOP 1 1 FROM myUsers WHERE name = 'björn borg' OR name_searchable = 'bjorn borg' )
    AND (
    OR name LIKE '%björn borg%' 
    OR name_searchable LIKE '%bjorn borg%'
    )
  )
ORDER BY CASE WHEN name = 'björn borg' THEN 1 
  WHEN name_searchable = 'bjorn borg' THEN 2 
  WHEN name LIKE '%björn borg%' THEN 3 
  WHEN name_searchable LIKE '%bjorn borg%' THEN 4 ELSE 5 END

【讨论】:

    【解决方案2】:

    您是否考虑过将查询分开并UNIONing 他们?

    SELECT 5 AS rank, * FROM myUsers WHERE name = 'björn borg' UNION
    SELECT 4 AS rank, * FROM myUsers WHERE name_searchable = 'bjorn borg' UNION
    SELECT 3 AS rank, * FROM myUsers WHERE name LIKE '%björn borg%' UNION
    SELECT 2 AS rank, * FROM myUsers WHERE name_searchable LIKE '%bjorn borg%'
    ORDER BY 1 DESC
    

    【讨论】:

    • 如果我错了,请纠正我,但这不是意味着要搜索整个表 4 次吗?换句话说,对于 10m 条记录,这就像搜索 40m 条记录。还是mysql缓存了第一个select语句,事情处理得更快了?
    • 这不像搜索 40m 条记录,因为如果您的表中有 40m 条记录,该表上的索引会大大增加。我不是 MySQL 内部的专家。它们可能作为单独的查询执行,但相关数据可能会保存在内存中,直到所有查询完成。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-29
    • 2011-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多