전체 텍스트 검색이 LIKE보다 적은 행을 반환하는 이유


10

원하는대로 전체 텍스트 검색을 수행 할 수 없으며 결과 목록의 차이점을 이해하지 못합니다.

예문 :

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 개의 행만 반환합니다. "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

답변:


13

귀하의 질문에 3 개의 문자열을 가져 와서 테이블 pankt대신 3 개의 문자열을 추가했습니다 punkt.

다음은 Windows 용 MySQL 5.5.12를 사용하여 실행 된 것입니다.

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
  • LOCATELOCATE 함수 에서와 같이
  • 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 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 Documentation 에 따르면 와일드 카드 문자에 대한 내용을 확인하십시오.

*

별표는 잘림 (또는 와일드 카드) 연산자 역할을합니다. 다른 연산자와 달리 영향을받는 단어에 추가해야합니다. 단어가 * 연산자 앞에 나오는 단어로 시작하면 단어가 일치합니다.

자르기 연산자로 단어를 지정하면 단어가 너무 짧거나 (ft_min_word_len 설정에서 결정된 경우) 불용어라도 부울 쿼리에서 제거되지 않습니다. 이 단어는 단어가 너무 짧거나 스톱 워드가 아닌 접두사로 시작하는 단어의 형태로 문서에 있어야하는 접두어로 표시되기 때문에 발생합니다. ft_min_word_len = 4라고 가정하십시오. 그런 다음 '+ word + the *'를 검색하면 '+ word + the'를 검색하는 것보다 적은 행이 반환됩니다.

이전 쿼리는 그대로 유지되며 문서에 단어와 * (로 시작하는 단어)가 모두 있어야합니다.

후자의 쿼리는 + word로 변환됩니다 (단어가 있어야 함). 는 너무 짧고 스톱 워드이며 둘 중 하나의 조건만으로도 무시됩니다.

이를 기반으로 와일드 카드 문자는 토큰의 뒷면에 적용되며 앞면에는 적용되지 않습니다. 이것에 비추어, 3 개의 펑크 시작 토큰 중 2 개가 출력이기 때문에 출력이 정확해야합니다. pankt와 같은 이야기. 이것은 적어도 3 중 2가 왜 적은 행인지 설명합니다.


와, 많은 투자에 감사드립니다. 이것은 전체 텍스트 검색이 예상 한대로 또는 적어도 문서에서 말한대로 작동한다는 것을 의미합니다. 그러나 이것은 또한 전체 텍스트 문제가 주어진 단어 부분을 포함하는 열의 100 %를 찾는 데 도움이되지 않는다는 것을 나타내므로 내 목적에 쓸모가 없습니다. 정확한 결과를 얻으려면 LIKE 또는 LOCALE을 사용하여 검색해야합니다. 놀랍게도 둘 다 더 빠릅니다.
32bitfloat

왜 "펑크 텐"을 찾았고 @ 32bitfloat는 찾지 못했습니까?! 대신 그는 "Treffpunkt"를 찾았지만 그렇지 않았습니다. 그리고 왜 "punkt"가 COUNT(IF(MATCH쿼리 에서 "Pankten"을 반환했는지 이해하지 못합니다 .
mgutt

InnoDB에서 어떤 일이 일어나는지 궁금합니다.
Rick James

COUNT(…)PunktMatch 및 PanktMatch 열에 있습니까? COUNT(IF(MATCH (meldungstext ) AGAINST ('*pankt*' IN BOOLEAN MODE),1,0))는 또는 에서 결과를 계산하기 때문에 항상 결과가됩니다 . 110IF(…)
Quinn Comendant
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.