Tại sao tìm kiếm toàn văn bản trả về ít hàng hơn THÍCH


10

Tôi không làm cho tìm kiếm toàn văn làm việc như tôi muốn và tôi không hiểu sự khác biệt trong danh sách kết quả.

Ví dụ báo cáo:

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

trả về 92 hàng. Tôi nhận được các hàng có kết quả trùng khớp, ví dụ như "Punkten", "Zwei-Punkte-Vorsprung" và "Treffpunkt" trong cột meldungstext.

Tôi đặt chỉ mục fulltext trên cột "meldungstext" và thử điều này:

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

Điều này chỉ trả về 8 hàng. Tôi chỉ nhận được các hàng khớp với chính "Punkt" hoặc các từ mà tôi nghĩ được coi là "Punkt" như trong "i-Punkt".

Sau đó tôi đã thử chế độ boolean:

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

trả về 44 hàng. Tôi nhận được các hàng có "Zwei-Punkte-Vorsprung" hoặc "Treffpunkt" trong cột meldungstext, nhưng không nhận được các hàng có "Punkten".

Tại sao điều này xảy ra và làm cách nào tôi có thể thiết lập tìm kiếm toàn văn bản "hoạt động đầy đủ" để ngăn việc sử dụng THÍCH '%%' trong mệnh đề where?


1
Điều này xứng đáng được +1 lớn vì vấn đề này chưa thực sự được kiểm tra và việc lập chỉ mục FULLTEXT thường được coi là điều hiển nhiên.
RolandoMySQLDBA

Câu trả lời:


13

Tôi lấy ba chuỗi trong câu hỏi của bạn và thêm nó vào một bảng cộng với ba chuỗi nữa panktthay vì punkt.

Sau đây đã được thực hiện bằng MySQL 5.5.12 cho 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>

Tôi đã chạy các truy vấn này trên bảng bằng 3 cách tiếp cận khác nhau

  • MATCH ... AGAINST
  • LOCATEnhư trong hàm LOCATE
  • LIKE

Xin lưu ý sự khác biệt

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>

Tất cả các giá trị PunktMatch nên tạo ra 3 1 và 3 0.

Bây giờ hãy xem tôi truy vấn họ như bình thường

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 sử dụng MATCH .. LẠI với punkt không hoạt động. Thế còn pankt ???

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

mysql>

Hãy chạy GROUP BYtruy vấn lớn của tôi chống lại 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>

Điều này cũng sai vì tôi sẽ thấy 3 0 và 3 1 cho PanktMatch.

Tôi đã thử một cái gì đó khác

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>

Tôi đã thêm một dấu cộng cho pankt và tôi đã nhận được kết quả khác nhau. Cái gì 2 chứ không phải 3 ???

Theo Tài liệu MySQL , hãy chú ý những gì nó nói về ký tự đại diện:

*

Dấu hoa thị đóng vai trò là toán tử cắt (hoặc ký tự đại diện). Không giống như các toán tử khác, nó nên được thêm vào từ bị ảnh hưởng. Các từ khớp với nhau nếu chúng bắt đầu bằng từ đứng trước toán tử *.

Nếu một từ được chỉ định với toán tử cắt bớt, nó không bị tước khỏi truy vấn boolean, ngay cả khi nó quá ngắn (như được xác định từ cài đặt ft_min_word_len) hoặc từ khóa. Điều này xảy ra bởi vì từ này không được xem là quá ngắn hoặc một từ dừng, mà là một tiền tố phải có trong tài liệu dưới dạng một từ bắt đầu bằng tiền tố. Giả sử rằng ft_min_word_len = 4. Sau đó, tìm kiếm '+ word + the *' có thể sẽ trả về ít hàng hơn so với tìm kiếm '+ word + the':

Truy vấn cũ vẫn giữ nguyên và yêu cầu cả từ và * (một từ bắt đầu bằng) phải có trong tài liệu.

Truy vấn sau được chuyển thành + từ (chỉ yêu cầu phải có mặt). cả hai quá ngắn và một từ khóa, và một trong hai điều kiện là đủ để làm cho nó bị bỏ qua.

Dựa trên điều này, ký tự đại diện được áp dụng cho mặt sau của mã thông báo chứ không phải cho mặt trước. Theo cách này, đầu ra phải chính xác vì 2 trong số 3 mã thông báo bắt đầu của punkt. Câu chuyện tương tự với pankt. Điều này ít nhất giải thích tại sao 2 trên 3 và tại sao ít hàng hơn.


Wow, cảm ơn rất nhiều vì sự đầu tư của bạn. Điều này có nghĩa là tìm kiếm toàn văn bản hoạt động như gián điệp, hoặc ít nhất là như đã nói trong tài liệu. Nhưng điều này cũng nói rằng toàn bộ vấn đề toàn văn bản sẽ không giúp tìm ra 100% các cột bao gồm một phần từ nhất định, điều này làm cho nó vô dụng cho mục đích của tôi. Để có kết quả chính xác, tôi cần tìm kiếm bằng THÍCH hoặc ĐỊA PHƯƠNG, điều đáng ngạc nhiên là cả hai dường như nhanh hơn.
32 bitfloat

Tại sao bạn tìm thấy "Punkten" và @ 32bitfloat không?! Thay vào đó anh ta tìm thấy "Treffpunkt", nhưng bạn thì không. Và tôi không thực sự hiểu tại sao "punkt" trả lại "Pankten" trong COUNT(IF(MATCHtruy vấn.
mgutt

Tôi tự hỏi điều gì xảy ra trong InnoDB.
Rick James

Tại sao bạn có COUNT(…)trên cột PunktMatch và PanktMatch? COUNT(IF(MATCH (meldungstext ) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0))sẽ luôn luôn dẫn đến 1, bởi vì nó đang đếm 1hoặc 0, kết quả từ IF(…).
Quinn Comendant
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.