为什么全文搜索返回的行少于LIKE


10

我没有像我想要的那样进行全文搜索,而且我不了解结果列表中的差异。

语句示例:

SELECT `meldungstext`
FROM `artikel`
WHERE `meldungstext` LIKE '%punkt%'

返回92行。我收到具有匹配项的行,例如,在列meldungstext中显示“ Punkten”,“ Zwei-Punkte-Vorsprung”和“ Treffpunkt”。

我在“ meldungstext”列上设置了全文索引,并尝试了以下操作:

SELECT `meldungstext`
FROM `artikel`
WHERE MATCH (`meldungstext`)
AGAINST ('*punkt*')

这仅返回8行。我只会收到与“ Punkt”本身匹配的行,或者与“ i-Punkt”一样被视为“ Punkt”的单词。

然后,我尝试了布尔模式:

SELECT `meldungstext`
FROM `artikel`
WHERE MATCH (`meldungstext`)
AGAINST ('*punkt*' IN BOOLEAN MODE)

返回44行。我收到在meldungstext列中包含“ Zwei-Punkte-Vorsprung”或“ Treffpunkt”的行,但没有包含“ Punkten”的行。

为什么会发生这种情况?如何设置“完全”的全文搜索,以防止在子句中使用LIKE'%%'?


1
这值得大+1,因为未真正检查此问题,并且通常认为FULLTEXT索引是理所当然的。
RolandoMySQLDBA 2012年

Answers:


13

我将您问题中的三个字符串添加到表中,再加上三个字符串,pankt而不是punkt

以下是使用Windows的MySQL 5.5.12执行的

mysql> CREATE TABLE artikel
    -> (
    ->     id INT NOT NULL AUTO_INCREMENT,
    ->     meldungstext MEDIUMTEXT,
    ->     PRIMARY KEY (id),
    ->     FULLTEXT (meldungstext)
    -> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT INTO artikel (meldungstext) VALUES
    -> ('Punkten'),('Zwei-Punkte-Vorsprung'),('Treffpunkt'),
    -> ('Pankten'),('Zwei-Pankte-Vorsprung'),('Treffpankt');
Query OK, 6 rows affected (0.00 sec)
Records: 6  Duplicates: 0  Warnings: 0

mysql>

我使用3种不同的方法针对表格运行了这些查询

  • MATCH ... AGAINST
  • LOCATE就像在LOCATE函数中一样
  • LIKE

请注意差异

mysql> SELECT id,meldungstext,
    -> COUNT(IF(MATCH (`meldungstext`) AGAINST ('*punkt*' IN BOOLEAN MODE),1,0)) PunktMatch,
    -> IF(LOCATE('punkt',meldungstext)>0,1,0) PunktLocate,
    -> meldungstext  LIKE '%punkt%' PunktLike
    -> FROM `artikel` GROUP BY id,meldungstext;
+----+-----------------------+------------+-------------+-----------+
| id | meldungstext          | PunktMatch | PunktLocate | PunktLike |
+----+-----------------------+------------+-------------+-----------+
|  1 | Punkten               |          1 |           1 |         1 |
|  2 | Zwei-Punkte-Vorsprung |          1 |           1 |         1 |
|  3 | Treffpunkt            |          1 |           1 |         1 |
|  4 | Pankten               |          1 |           0 |         0 |
|  5 | Zwei-Pankte-Vorsprung |          1 |           0 |         0 |
|  6 | Treffpankt            |          1 |           0 |         0 |
+----+-----------------------+------------+-------------+-----------+
6 rows in set (0.01 sec)

mysql>

所有PunktMatch值应为3 1和3 0。

现在看着我正常查询

mysql> SELECT `meldungstext` FROM `artikel`
    -> WHERE MATCH (`meldungstext`) AGAINST ('*punkt*' IN BOOLEAN MODE);
+-----------------------+
| meldungstext          |
+-----------------------+
| Zwei-Punkte-Vorsprung |
| Punkten               |
+-----------------------+
2 rows in set (0.01 sec)

mysql> SELECT `meldungstext` FROM `artikel`
    -> WHERE LOCATE('punkt',meldungstext)>0;
+-----------------------+
| meldungstext          |
+-----------------------+
| Punkten               |
| Zwei-Punkte-Vorsprung |
| Treffpunkt            |
+-----------------------+
3 rows in set (0.00 sec)

mysql> SELECT `meldungstext` FROM `artikel`
    -> WHERE `meldungstext` LIKE '%punk%';
+-----------------------+
| meldungstext          |
+-----------------------+
| Punkten               |
| Zwei-Punkte-Vorsprung |
| Treffpunkt            |
+-----------------------+
3 rows in set (0.00 sec)

mysql>

可以使用MATCH .. AGAINST和punkt正常运行。那潘克呢?

mysql> SELECT `meldungstext` FROM `artikel` WHERE `meldungstext` LIKE '%pankt%';
+-----------------------+
| meldungstext          |
+-----------------------+
| Pankten               |
| Zwei-Pankte-Vorsprung |
| Treffpankt            |
+-----------------------+
3 rows in set (0.00 sec)

mysql>

让我们GROUP BY对pankt 运行我的大查询

mysql> SELECT id,meldungstext,
    -> COUNT(IF(MATCH (`meldungstext`) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0)) PanktMatch,
    -> IF(LOCATE('pankt',meldungstext)>0,1,0) PanktLocate,
    -> meldungstext  LIKE '%pankt%' PanktLike
    -> FROM `artikel` GROUP BY id,meldungstext;
+----+-----------------------+------------+-------------+-----------+
| id | meldungstext          | PanktMatch | PanktLocate | PanktLike |
+----+-----------------------+------------+-------------+-----------+
|  1 | Punkten               |          1 |           0 |         0 |
|  2 | Zwei-Punkte-Vorsprung |          1 |           0 |         0 |
|  3 | Treffpunkt            |          1 |           0 |         0 |
|  4 | Pankten               |          1 |           1 |         1 |
|  5 | Zwei-Pankte-Vorsprung |          1 |           1 |         1 |
|  6 | Treffpankt            |          1 |           1 |         1 |
+----+-----------------------+------------+-------------+-----------+
6 rows in set (0.01 sec)

mysql>

这也是错误的,因为对于PanktMatch,我应该看到3 0和3 1。

我尝试了其他

mysql> SELECT id,meldungstext, MATCH (`meldungstext`) AGAINST ('+*pankt*' IN BOOLEAN MODE) PanktMatch, IF(LOCATE('pankt',meldungstext)>0,1,0) PanktLocate, meldungstext  LIKE '%pankt%' PanktLike FROM `artikel` GROUP BY id,meldungstext;
+----+-----------------------+------------+-------------+-----------+
| id | meldungstext          | PanktMatch | PanktLocate | PanktLike |
+----+-----------------------+------------+-------------+-----------+
|  1 | Punkten               |          0 |           0 |         0 |
|  2 | Zwei-Punkte-Vorsprung |          0 |           0 |         0 |
|  3 | Treffpunkt            |          0 |           0 |         0 |
|  4 | Pankten               |          1 |           1 |         1 |
|  5 | Zwei-Pankte-Vorsprung |          1 |           1 |         1 |
|  6 | Treffpankt            |          0 |           1 |         1 |
+----+-----------------------+------------+-------------+-----------+
6 rows in set (0.00 sec)

mysql>

我给pankt加了一个加号,得到了不同的结果。什么2而不是3 ???

根据MySQL文档,请注意有关通配符的内容:

*

星号用作截断(或通配符)运算符。与其他运算符不同,应将其附加到要受影响的单词上。如果单词以*运算符之前的单词开头,则匹配。

如果使用截断运算符指定了单词,即使它太短(由ft_min_word_len设置确定)或停用词,也不会从布尔查询中删除该单词。发生这种情况的原因是,该单词不是被视为太短或一个停用词,而是作为前缀出现在文档中,必须以以该前缀开头的单词的形式出现在文档中。假设ft_min_word_len = 4。然后,搜索“ + word + the *”将比搜索“ + word + the”返回更少的行:

前一个查询保持原样,并且要求单词和the *(以the开头的单词)都出现在文档中。

后一个查询被转换为+ word(仅需要出现一个单词)。既太短又是一个停用词,任何一个条件都足以使它被忽略。

基于此,通配符适用于令牌的背面,而不适用于正面。鉴于此,输出必须正确,因为3个点的启动令牌中有2个。与pankt的故事相同。这至少可以解释为什么三分之二的行以及为什么行数更少。


哇,非常感谢您的投资。这意味着全文搜索工作符合预期,或者至少按照文档中的说明进行。但是,这也指出,整个全文问题无助于找到100%包含给定单词部分的列,这对我而言毫无用处。为了获得准确的结果,我需要使用LIKE或LOCALE进行搜索,除了令人惊讶的是,两者似乎都更快。
32bitfloat 2012年

为什么找到“ Punkten”而没有找到@ 32bitfloat?相反,他找到了“ Treffpunkt”,但您没有找到。而且我不太明白为什么“ punkt”在COUNT(IF(MATCH查询中返回“ Pankten” 。
mgutt 2015年

我想知道InnoDB中会发生什么。
瑞克·詹姆斯

为什么COUNT(…)在PunktMatch和PanktMatch列上都有?COUNT(IF(MATCH (meldungstext ) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0))始终导致1,因为它是在计数10IF(…)
Quinn Comendant
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.