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

32b*_*oat 12 mysql myisam full-text-search

我没有按照我的意愿进行全文搜索,而且我不明白结果列表中的差异。

示例语句:

SELECT `meldungstext`
FROM `artikel`
WHERE `meldungstext` LIKE '%punkt%'
Run Code Online (Sandbox Code Playgroud)

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

我在“meldungstext”列上设置了全文索引并尝试了这个:

SELECT `meldungstext`
FROM `artikel`
WHERE MATCH (`meldungstext`)
AGAINST ('*punkt*')
Run Code Online (Sandbox Code Playgroud)

这仅返回 8 行。我只收到与“Punkt”本身或我认为在“i-Punkt”中被视为“Punkt”的单词匹配的行。

然后我尝试了布尔模式:

SELECT `meldungstext`
FROM `artikel`
WHERE MATCH (`meldungstext`)
AGAINST ('*punkt*' IN BOOLEAN MODE)
Run Code Online (Sandbox Code Playgroud)

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

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

Rol*_*DBA 14

我把你问题中的三个字符串添加到一个表中,再加上三个字符串,pankt而不是punkt.

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

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>
Run Code Online (Sandbox Code Playgroud)

我使用 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>
Run Code Online (Sandbox Code Playgroud)

所有 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>
Run Code Online (Sandbox Code Playgroud)

OK 使用 MATCH .. AGAINST 与 punkt 不起作用。pankt 呢???

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

mysql>
Run Code Online (Sandbox Code Playgroud)

让我们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>
Run Code Online (Sandbox Code Playgroud)

这也是错误的,因为我应该看到 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>
Run Code Online (Sandbox Code Playgroud)

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

根据MySQL 文档,请注意它对通配符的说明:

*

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

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

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

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

基于此,通配符适用于令牌的背面,而不适用于正面。鉴于此,输出必须正确,因为 3 个 punkt 的起始标记中有 2 个。与 pankt 相同的故事。这至少解释了为什么 3 个中有 2 个以及为什么行数更少。