web-dev-qa-db-ja.com

全文検索でLIKEよりも少ない行が返される理由

全文検索が期待どおりに機能せず、結果リストの違いがわかりません。

ステートメントの例:

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行のみを返します。 「パンク」自体に一致する行、または「i-パンク」のように「パンク」と見なされると思われる単語のみを受け取ります。

次にブールモードを試しました:

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

44行を返します。 meldungstext列に「Zwei-Punkte-Vorsprung」または「Treffpunkt」を含む行を受け取りますが、「Punkten」を含む行は受け取りません。

なぜこれが発生するのか、および「完全に」機能する全文検索を設定して、where節でLIKE '%%'を使用しないようにするにはどうすればよいですか?

10
32bitfloat

私はあなたの質問の3つの文字列を取り、それをテーブルに追加し、さらにpanktではなくpunktを使用して3つの文字列を追加しました。

以下は、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>

3つの異なる方法を使用して、これらのクエリをテーブルに対して実行しました

違いに注意してください

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>

OK MATCH .. AGAINST with 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にプラス記号を追加すると、異なる結果が得られました。 3ではなく2とは???

MySQL Documentation によると、ワイルドカード文字についての説明に注意してください。

*

アスタリスクは、切り捨て(またはワイルドカード)演算子として機能します。他の演算子とは異なり、影響を受けるWordに追加する必要があります。 *演算子の前の単語で始まる場合、単語は一致します。

Wordが切り捨て演算子で指定されている場合、短すぎても(ft_min_Word_lenの設定から判断される)、ストップワードであっても、ブールクエリから削除されません。これは、Wordが短すぎたりストップワードとしてではなく、プレフィックスで始まるWordの形式でドキュメントに存在する必要があるプレフィックスとして表示されるために発生します。 ft_min_Word_len = 4と仮定します。次に、「+ Word + the *」を検索すると、「+ Word + the」を検索した場合よりも少ない行が返される可能性があります。

前のクエリはそのままで、Wordとthe *(theで始まるWord)の両方がドキュメントに存在する必要があります。

後者のクエリは+ Wordに変換されます(Wordのみが存在する必要があります)。これは短すぎてストップワードであり、どちらの条件でも無視できます。

これに基づいて、ワイルドカード文字はトークンの裏側に適用され、表側には適用されません。これに照らして、3つのpunktの開始トークンのうち2つが正しいため、出力は正しいはずです。パンクトと​​同じ話。これは、少なくとも3つのうち2つ、なぜ行が少ないのかを説明しています。

13
RolandoMySQLDBA