ทำไม full-text-search จึงส่งกลับแถวน้อยกว่า 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" เท่านั้นหรือคำที่ฉันคิดว่าเป็น "Punkt" เช่นเดียวกับใน "i-Punkt"

ฉันลองโหมดบูลีน:

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

ส่งคืน 44 แถว ฉันได้รับแถวที่มี "Zwei-Punkte-Vorsprung" หรือ "Treffpunkt" ในคอลัมน์ meldungstext แต่ไม่ใช่แถวที่มี "Punkten"

ทำไมสิ่งนี้ถึงเกิดขึ้นและฉันจะตั้งค่าการค้นหาข้อความทั้งหมดที่ "เต็ม" เพื่อป้องกันการใช้ LIKE '%%' ในตำแหน่งได้อย่างไร


1
สิ่งนี้สมควรได้รับ +1 ที่ยิ่งใหญ่เพราะปัญหานี้ไม่ได้รับการตรวจสอบจริง ๆ และการทำดัชนีแบบ FULLTEXT มักได้รับอนุญาต
RolandoMySQLDBA

คำตอบ:


13

ผมเอาสามสายในคำถามของคุณและเพิ่มไปยังตารางบวกสตริงสามมากขึ้นด้วยการแทนpanktpunkt

ต่อไปนี้ถูกดำเนินการโดยใช้ MySQL 5.5.12 สำหรับ 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 แบบ

  • 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>

ค่า 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>

ตกลงใช้ MATCH .. เทียบกับ 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>

ลองเรียกใช้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 และฉันได้ผลลัพธ์ที่แตกต่างกัน อะไรที่ 2 และไม่ใช่ 3 ???

ตามเอกสาร MySQLให้สังเกตสิ่งที่พูดเกี่ยวกับอักขระตัวแทน:

* * * *

เครื่องหมายดอกจันทำหน้าที่เป็นตัวดำเนินการที่ถูกตัดทอน (หรือไวด์การ์ด) ซึ่งแตกต่างจากผู้ประกอบการอื่น ๆ มันควรจะผนวกเข้ากับคำที่จะได้รับผลกระทบ คำที่ตรงกันหากพวกเขาเริ่มต้นด้วยคำก่อนหน้าผู้ประกอบการ *

หากมีการระบุคำด้วยตัวดำเนินการตัดคำนั้นจะไม่ถูกแยกออกจากคิวรีบูลีนแม้ว่าจะสั้นเกินไป (ตามที่กำหนดจากการตั้งค่า ft_min_word_len) หรือคำหยุด สิ่งนี้เกิดขึ้นเนื่องจากคำไม่ถูกมองว่าสั้นเกินไปหรือคำหยุด แต่เป็นคำนำหน้าที่ต้องมีอยู่ในเอกสารในรูปแบบของคำที่ขึ้นต้นด้วยคำนำหน้า สมมติว่า ft_min_word_len = 4 จากนั้นการค้นหา '+ คำ + the *' จะส่งกลับแถวน้อยกว่าการค้นหา '+ word + the':

แบบสอบถามแบบเดิมยังคงเป็นอยู่และต้องการทั้งคำและ * (คำที่ขึ้นต้นด้วย) เพื่อให้ปรากฏในเอกสาร

แบบสอบถามหลังถูกแปลงเป็นคำ + (ต้องการคำเท่านั้นที่จะมีอยู่) สั้นเกินไปและเป็นคำหยุดและเงื่อนไขอย่างใดอย่างหนึ่งก็เพียงพอที่จะทำให้มันถูกละเว้น

ขึ้นอยู่กับสิ่งนี้อักขระ wildcard นั้นใช้กับด้านหลังของโทเค็นและไม่ใช่สำหรับด้านหน้า ด้วยเหตุนี้ผลลัพธ์จะต้องถูกต้องเนื่องจากโทเค็นเริ่มต้นของพังก์ 2 ใน 3 ตัว เรื่องเดียวกันกับ pankt อย่างน้อยก็อธิบายว่าทำไม 2 จาก 3 และทำไมจำนวนแถวน้อยลง


ว้าวขอบคุณมากสำหรับการลงทุนของคุณ ซึ่งหมายถึงการค้นหาข้อความแบบเต็มตามที่ได้รับการพิสูจน์แล้วหรืออย่างน้อยดังที่ได้กล่าวไว้ในเอกสาร แต่สิ่งนี้ยังระบุว่าข้อความฉบับเต็มทั้งหมดจะไม่ช่วยในการค้นหาคอลัมน์ 100% ซึ่งรวมส่วนคำที่กำหนดซึ่งทำให้ไร้ประโยชน์สำหรับวัตถุประสงค์ของฉัน สำหรับผลลัพธ์ที่แน่นอนฉันต้องค้นหาด้วย LIKE หรือ LOCALE ซึ่งนอกเหนือจากที่น่าประหลาดใจทั้งสองดูเหมือนจะเร็วขึ้น
32bitfloat

ทำไมคุณถึงพบว่า "Punkten" และ @ 32bitfloat ไม่ได้! เขากลับพบ "Treffpunkt" แต่คุณไม่ได้ทำ และฉันไม่เข้าใจว่าทำไม "punkt" จึงส่งคืน "Pankten" ในการCOUNT(IF(MATCHสืบค้น
mgutt

ฉันสงสัยว่าเกิดอะไรขึ้นใน InnoDB
Rick James

ทำไมคุณถึงมีCOUNT(…)ในคอลัมน์ PunktMatch และ PanktMatch COUNT(IF(MATCH (meldungstext ) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0))จะเสมอส่งผลให้1เพราะมันเป็นนับ1หรือผลจากการที่0 IF(…)
Quinn Comendant
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.