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


10

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

ステートメントの例:

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

92行を返します。たとえば、 "Punkten"、 "Zwei-Punkte-Vorsprung"、 "Treffpunkt"などの一致する行がmeldungstext列に表示されます。

「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」の行は表示されません。

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


1
この問題は実際には調査されておらず、FULLTEXTインデックス付けは当然のことと考えられているため、これは大きな+1に値します。
RolandoMySQLDBA 2012

回答:


13

私はあなたの質問の3つの文字列を取り、それを表に追加し、のpankt代わりにでさらに3つの文字列を追加しました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>

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 BYpanktに対して大きなクエリを実行してみましょう

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のドキュメントによれば、ワイルドカード文字についての説明に注意してください。

*

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

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

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

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

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


うわー、あなたの投資に感謝します。これは、全文検索が想定どおり、または少なくとも文書で述べられているように機能することを意味します。しかし、これは、全文の問題全体が、特定の単語部分を含む100%の列を見つけるのに役立つわけではないことも示しています。正確な結果を得るには、LIKEまたはLOCALEで検索する必要があります。これは驚くべきことに、どちらも高速であるようです。
32ビットフロート2007

なぜ「パンクテン」を見つけ、@ 32bitfloatは見つけなかったのですか?代わりに彼は "Treffpunkt"を見つけましたが、あなたは見つけませんでした。また、COUNT(IF(MATCHクエリで「punkt」が「Pankten」を返した理由がよくわかりません。
mgutt 2015

InnoDBで何が起こるのだろうか。
リックジェームズ

なぜCOUNT(…)PunktMatch列とPanktMatch列にあるのですか?COUNT(IF(MATCH (meldungstextが) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0))します常にになり1、それはカウントしているため、1または0、からの結果IF(…)
クインコメンダント
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.