Mengapa pencarian teks lengkap menghasilkan lebih sedikit baris daripada LIKE

10

Saya tidak mendapatkan pencarian teks lengkap yang saya inginkan, dan saya tidak mengerti perbedaan dalam daftar hasil.

Pernyataan contoh:

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

mengembalikan 92 baris. Saya menerima baris yang memiliki kecocokan, misalnya, seperti "Punkten", "Zwei-Punkte-Vorsprung" dan "Treffpunkt" di kolom meldungstext.

Saya menetapkan indeks teks lengkap pada kolom "meldungstext" dan mencoba ini:

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

ini hanya mengembalikan 8 baris. Saya hanya menerima baris yang cocok dengan "Punkt" itu sendiri atau kata-kata yang saya pikir diambil sebagai "Punkt" seperti dalam "i-Punkt".

Saya kemudian mencoba mode boolean:

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

mengembalikan 44 baris. Saya menerima baris yang memiliki "Zwei-Punkte-Vorsprung" atau "Treffpunkt" di kolom meldungstext, tetapi bukan yang dengan "Punkten".

Mengapa ini terjadi dan bagaimana saya bisa mengatur pencarian teks lengkap yang "sepenuhnya" berfungsi untuk mencegah penggunaan LIKE '%%' di mana-klausa?

32 bit mengapung
sumber
1
Ini layak mendapat +1 besar karena masalah ini tidak benar-benar diperiksa dan pengindeksan FULLTEXT sering dianggap biasa.
RolandoMySQLDBA

Jawaban:

13

Saya mengambil tiga string dalam pertanyaan Anda dan menambahkannya ke sebuah meja ditambah tiga string dengan panktalih - alih punkt.

Berikut ini dieksekusi menggunakan MySQL 5.5.12 untuk 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>

Saya menjalankan pertanyaan ini di atas meja menggunakan 3 pendekatan berbeda

  • MATCH ... AGAINST
  • LOCATEseperti pada fungsi LOCATE
  • LIKE

Harap perhatikan perbedaannya

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>

Semua nilai PunktMatch harus berupa lebah 1 1 dan 3 0.

Sekarang perhatikan saya meminta mereka seperti biasa

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 menggunakan MATCH .. MELAWAN dengan punkt tidak berfungsi. Bagaimana dengan pankt ???

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

mysql>

Mari kita jalankan GROUP BYpermintaan besar saya terhadap 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>

Ini salah juga karena saya akan melihat 3 0 dan 3 1 untuk PanktMatch.

Saya mencoba sesuatu yang lain

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>

Saya menambahkan tanda plus ke pankt dan saya mendapat hasil berbeda. Apa 2 dan bukan 3 ???

Menurut Dokumentasi MySQL , perhatikan apa yang dikatakannya tentang karakter wildcard:

*

Tanda bintang berfungsi sebagai operator pemotongan (atau wildcard). Berbeda dengan operator lain, itu harus ditambahkan ke kata yang akan terpengaruh. Kata-kata cocok jika mereka mulai dengan kata sebelum * operator.

Jika sebuah kata ditentukan dengan operator pemotongan, kata tersebut tidak dilucuti dari kueri boolean, meskipun terlalu pendek (seperti yang ditentukan dari pengaturan ft_min_word_len) atau kata kunci. Ini terjadi karena kata tersebut tidak dilihat sebagai kata yang terlalu pendek atau berhenti, tetapi sebagai awalan yang harus ada dalam dokumen dalam bentuk kata yang diawali dengan awalan. Misalkan ft_min_word_len = 4. Maka pencarian '+ kata + the *' kemungkinan akan menghasilkan lebih sedikit baris daripada pencarian untuk '+ kata + the':

Permintaan sebelumnya tetap apa adanya dan membutuhkan kata dan * (kata yang dimulai dengan) harus ada dalam dokumen.

Kueri yang terakhir ditransformasikan ke + kata (hanya membutuhkan kata untuk hadir). keduanya terlalu pendek dan berhenti, dan kedua kondisi ini cukup untuk membuatnya diabaikan.

Berdasarkan ini, karakter wildcard berlaku untuk bagian belakang token dan bukan untuk bagian depan. Sehubungan dengan ini, output harus benar karena 2 dari 3 token punkt. Kisah yang sama dengan pankt. Setidaknya ini menjelaskan mengapa 2 dari 3 dan mengapa lebih sedikit baris.

RolandoMySQLDBA
sumber
Wow, terima kasih banyak atas investasi Anda. Ini berarti pencarian teks lengkap berfungsi sebagai dicentang, atau setidaknya seperti yang dikatakan dalam dokumen. Tetapi ini juga menyatakan bahwa seluruh masalah teks lengkap tidak akan membantu menemukan 100% kolom yang menyertakan bagian kata tertentu, yang membuatnya tidak berguna untuk tujuan saya. Untuk hasil yang tepat, saya perlu mencari dengan LIKE atau LOCALE, yang selain mengejutkan keduanya tampaknya lebih cepat.
32bit naik
Mengapa Anda menemukan "Punkten" dan @ 32bitfloat tidak ?! Sebaliknya dia menemukan "Treffpunkt", tetapi kamu tidak. Dan saya tidak begitu mengerti mengapa "punkt" mengembalikan "Pankten" dalam COUNT(IF(MATCHpermintaan.
mgutt
Saya ingin tahu apa yang terjadi di InnoDB.
Rick James
Mengapa Anda memiliki COUNT(…)kolom PunktMatch dan PanktMatch? COUNT(IF(MATCH (meldungstext ) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0))akan selalu menghasilkan 1, karena ia menghitung 1atau 0, hasil dari IF(…).
Quinn Comendant