【问题标题】:MySQL Whole Word Match – Multiple wordsMySQL 全词匹配 - 多个词
【发布时间】:2019-07-17 07:15:04
【问题描述】:

我看过不少类似的帖子,但都没有解决我的问题,这很可能是因为我缺乏足够的知识,所以请多多包涵。

我的术语词典中的一个搜索选项是“仅全词”。一开始我用的是

WHERE ".$source." RLIKE '[[:<:]]".$keyword."[[:>:]]'

但是,当有多个单词时,这无法匹配第一个或第二个 $keyword 的整个单词。然后我发现了

WHERE ".$source." REGEXP '[[:<:]]".$keyword."[[:>:]]'

WHERE ".$source." REGEXP '(^| )".$keyword."( |$)'

搜索时these forums

我刚刚在我的 PhpMyAdmin 中测试了以上两个,发现前者在 0.0740 秒内执行,而后者需要两倍的时间,0.1440 秒,所以我想我应该坚持前者。

最困扰我的是结果的巨大差异,例如搜索单个词(“工具”):

  1. 在 PhpMyAdmin 中使用 [[:&lt;:]][[:&gt;:]] 单词边界会返回 34 个结果。

  2. 在 PhpMyAdmin 中使用 (^| )( |$) 返回 26 个结果。

  3. 在我的 PHP 脚本中运行 #1 正则表达式会返回 34 个结果(这是正确的数字)。

这是整个 MySQL 块:

foreach($keywords as $keyword) {
    $query = $db->query("SELECT * FROM ".DICTIONARY_TABLE." " .
    "JOIN ".DICTIONARY_THEMES." ON ".DICTIONARY_TABLE.".theme_id = ".DICTIONARY_THEMES.".theme_id ".
    "LEFT JOIN ".DICTIONARY_DEFINITIONS." ON ".DICTIONARY_TABLE.".term_id = ".DICTIONARY_DEFINITIONS.".term_id ".
    "WHERE ".DICTIONARY_TABLE.".".$source." REGEXP '(^| )".$keyword."( |$)'".
    //"WHERE ".DICTIONARY_TABLE.".".$source." REGEXP '[[:<:]]".$keyword."[[:>:]]'".
    " ORDER BY ".DICTIONARY_TABLE.".theme_id, ".DICTIONARY_TABLE.".".$source."");
}

我已经注释掉了我不使用的搜索选项。

现在,如果我尝试两个关键字,例如“切割工具”,我仍然在页面中得到 34 个结果。我不确定我在 PhpMyAdmin 中是否正确:

SELECT * FROM `asphodel_dictionary_terms` WHERE english REGEXP '[[:<:]]cutting[[:>:]]';
SELECT * FROM `asphodel_dictionary_terms` WHERE english REGEXP '[[:<:]]tool[[:>:]]'

这将为“切割”返回 44 个结果,为“工具”返回 34 个结果。使用(^| )...的查询分别返回37 + 26个结果。

任何可以帮助我解决问题的反馈都将不胜感激!

数据库主表共包含109,000个条目,DICTIONARY_THEMES表中有82个主题,DICTIONARY_DEFINITIONS表中有727个条目。不是一个巨大的数据库,它不会变得更大。

【问题讨论】:

  • keyword( |$) 不匹配 keyword.keyword,
  • keyword(\.|,| |$) 会这样做吗?我不擅长正则表达式,对不起,如果这是一个愚蠢的问题。
  • 我不是正则表达式专家。将 [regex] 标签添加到您的问题中,并希望@WiktorStribiżew 能找到它:-)
  • 这个帖子没有问题。您正在将苹果与橙子进行比较,因为这两个正则表达式旨在匹配不同上下文中的单词。一种基于单词边界,另一种使用空白边界。由您决定您需要什么,只有在此之后您才能尝试优化。在 MySQL 中,您将无法进行太多优化,它的正则表达式引擎太“基础”。

标签: php mysql sql regex


【解决方案1】:

您得到不同的结果,因为这两个正则表达式不相同。

(^| ) 表示:字符串的开头或空格(( |$) 在字符串的末尾具有相同的含义)。

[[:&lt;:]][[:&gt;:]]单词边界:从概念上讲,这是指分隔单词的字符,通常正则表达式引擎将其解释为:除了数字、字母或下划线之外的任何内容.

所以基本上第一种模式比第二种限制更多(空格、字符串的开头和结尾字边界,但还有其他的)。

如果您要搜索多个关键字,则需要重复正则表达式匹配,例如:

WHERE 
    ".$source." RLIKE '[[:<:]]".$keyword1."[[:>:]]'
    OR ".$source." RLIKE '[[:<:]]".$keyword2."[[:>:]]'

或者通过组合关键字创建一个新的正则表达式:

WHERE 
    ".$source." RLIKE '[[:<:]](".$keyword1.")|(".$keyword2.")[[:>:]]'

注意:对于搜索要求,您应该考虑使用MySQL Full Text Search,它主要用于搜索完整的单词(但有先决条件)。

【讨论】:

  • 感谢您解释这两个正则表达式之间的区别。然后我会坚持单词边界——也因为它更快。但是,我认为我无法按照您在帖子第二部分中的建议进行操作,因为我采用了$keywords 数组并将其循环到foreach 中,也就是说,我只有$keyword。另外,如果存在于同一条目中,此方法将永远不会返回所有关键字的匹配项,例如当我搜索“止损”时,这将返回包含“止损”或“亏损”但不包含“止损 [订单]”的条目。
  • 刚刚注意到一些奇怪的事情:单词边界将连字符的单词视为单独的单词,例如WHERE english REGEXP '[[:&lt;:]]stop[[:&gt;:]]' AND english REGEXP '[[:&lt;:]]loss[[:&gt;:]]'会返回“止损条约”和“止损指令”。
  • @cheeseus :是的,这就是我所期望的,正则表达式引擎通常以这种方式工作
猜你喜欢
  • 1970-01-01
  • 2011-12-21
  • 2010-12-15
  • 1970-01-01
  • 2015-07-25
  • 1970-01-01
  • 2010-09-24
  • 1970-01-01
  • 2012-06-22
相关资源
最近更新 更多