SELECT
и WHERE
CREATE
, DROP
, ALTER
Buy this Reference Manual in softcover from Barnes & Noble!
Что касается MySQL 3.23.23, то эта версия MySQL поддерживает полнотекстовый поиск и индексацию. Полнотекстовые индексы в MySQL обозначаются как индексы типа FULLTEXT
. Эти индексы могут быть созданы в столбцах VARCHAR
и TEXT
во время создания таблицы командой CREATE TABLE
или добавлены позже с помощью команд ALTER TABLE
или CREATE INDEX
. Загрузка больших массивов данных в таблицу будет происходить намного быстрее, если таблица не содержит индекс FULLTEXT
, который затем создается командой ALTER TABLE
(или CREATE INDEX
). Загрузка данных в таблицу, уже имеющую индекс FULLTEXT
, будет более медленной.
Полнотекстовый поиск выполняется с помощью функции MATCH()
.
mysql> CREATE TABLE articles ( id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, title VARCHAR(200), body TEXT, FULLTEXT (title,body) ); Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO articles VALUES (0,'MySQL Tutorial', 'DBMS stands for DataBase ...'), (0,'How To Use MySQL Efficiently', 'After you went through a ...'), (0,'Optimising MySQL','In this tutorial we will show ...'), (0,'1001 MySQL Trick','1. Never run mysqld as root. 2. ...'), (0,'MySQL vs. YourSQL', 'In the following database comparison ...'), (0,'MySQL Security', 'When configured properly, MySQL ...'); Query OK, 6 rows affected (0.00 sec) Records: 6 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('database'); +----+-------------------+------------------------------------------+ | id | title | body | +----+-------------------+------------------------------------------+ | 5 | MySQL vs. YourSQL | In the following database comparison ... | | 1 | MySQL Tutorial | DBMS stands for DataBase ... | +----+-------------------+------------------------------------------+ 2 rows in set (0.00 sec)
Функция MATCH()
выполняет поиск в естественном языке, сравнивая строку с содержимым текста (совокупность одного или более столбцов, включенных в индекс FULLTEXT
). Строка поиска задается как аргумент в выражении AGAINST()
. Поиск выполняется без учета регистра символов. Для каждой строки столбца в заданной таблице команда MATCH()
возвращает величину релевантности, т.е. степень сходства между строкой поиска и текстом, содержащимся в данной строке указанного в списке оператора MATCH()
столбца.
Когда команда MATCH()
используется в выражении WHERE
(см. пример выше), возвращенные строки столбцов автоматически сортируются, начиная с наиболее релевантных. Величина релевантности представляет собой неотрицательное число с плавающей точкой. Релевантность вычисляется на основе количества слов в данной строке столбца, количества уникальных слов в этой строке, общего количества слов в тексте и числа документов (строк), содержащих отдельное слово.
Поиск возможен также в логическом режиме, это объясняется ниже в данном разделе.
Предыдущий пример представляет собой общую иллюстрацию использования функции MATCH()
. Строки возвращаются в порядке уменьшения релевантности.
В следующем примере показано, как извлекать величины релевантности в явном виде. В случае отсутствия выражений WHERE
и ORDER BY
возвращаемые строки не упорядочиваются.
mysql> SELECT id,MATCH (title,body) AGAINST ('Tutorial') FROM articles; +----+-----------------------------------------+ | id | MATCH (title,body) AGAINST ('Tutorial') | +----+-----------------------------------------+ | 1 | 0.64840710366884 | | 2 | 0 | | 3 | 0.66266459031789 | | 4 | 0 | | 5 | 0 | | 6 | 0 | +----+-----------------------------------------+ 6 rows in set (0.00 sec)
Следующий пример - более сложный. Запрос возвращает значение релевантности и, кроме того, сортирует строки в порядке убывания релевантности. Чтобы получить такой результат, необходимо указать MATCH()
дважды. Это не приведет к дополнительным издержкам, так как оптимизатор MySQL учтет, что эти два вызова MATCH()
идентичны, и запустит код полнотекстового поиска только однажды.
mysql> SELECT id, body, MATCH (title,body) AGAINST -> ('Security implications of running MySQL as root') AS score -> FROM articles WHERE MATCH (title,body) AGAINST -> ('Security implications of running MySQL as root'); +----+-------------------------------------+-----------------+ | id | body | score | +----+-------------------------------------+-----------------+ | 4 | 1. Never run mysqld as root. 2. ... | 1.5055546709332 | | 6 | When configured properly, MySQL ... | 1.31140957288 | +----+-------------------------------------+-----------------+ 2 rows in set (0.00 sec)
Для разбивки текста на слова MySQL использует очень простой синтаксический анализатор. ``Словом'' является любая последовательность символов, состоящая из букв, чисел, знаков `'' и `_'. Любое ``слово'', присутствующее в стоп-списке (stopword) или просто слишком короткое (3 символа или меньше), игнорируется.
Каждое правильное слово в наборе проверяемых текстов и в данном запросе оценивается в соответствии с его важностью в этом запросе или наборе текстов. Таким образом, слово, присутствующее во многих документах, будет иметь меньший вес (и даже, возможно, нулевой), как имеющее более низкое смысловое значение в данном конкретном наборе текстов. С другой стороны, редко встречающееся слово получит более высокий вес. Затем полученные значения весов слов объединяются для вычисления релевантности данной строки столбца.
Описанная техника подсчета лучше всего работает для больших наборов текстов (фактически она именно для этого тщательно настраивалась). Для очень малых таблиц распределение слов не отражает адекватно их смысловое значение, и данная модель иногда может выдавать некорректные результаты.
mysql> SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('MySQL'); Empty set (0.00 sec)
Поиск по слову ``MySQL'' в предыдущем примере не приводит к каким-либо результатам, так как это слово присутствует более чем в половине строк. По существу, данное слово целесообразно трактовать как стоп-слово (т.е. слово с нулевой смысловой ценностью). Это наиболее приемлемое решение - запрос на естественном языке не должен возвращать каждую вторую строку из таблицы размером 1Гб.
Маловероятно, что слово, встречающееся в половине строк таблицы, определяет местонахождение релевантных документов. На самом деле, наиболее вероятно, что будет найдено много не относящихся к делу документов. Общеизвестно, что такое случается слишком часто при попытке найти что-либо в Интернет с помощью поисковых машин. Именно на этом основании подобным строкам должно быть назначено низкое смысловое значение в данном конкретном наборе данных.
В MySQL 4.0.1 возможен полнотекстовый поиск также и в логическом режиме с использованием модификатора IN BOOLEAN MODE
.
mysql> SELECT * FROM articles WHERE MATCH (title,body) -> AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE); +----+------------------------------+-------------------------------------+ | id | title | body | +----+------------------------------+-------------------------------------+ | 1 | MySQL Tutorial | DBMS stands for DataBase ... | | 2 | How To Use MySQL Efficiently | After you went through a ... | | 3 | Optimising MySQL | In this tutorial we will show ... | | 4 | 1001 MySQL Trick | 1. Never run mysqld as root. 2. ... | | 6 | MySQL Security | When configured properly, MySQL ... | +----+------------------------------+-------------------------------------+
Данный запрос вывел все строки, содержащие слово ``MySQL'' (заметьте, 50-процентная пороговая величина здесь не используется), но эти строки не содержат слова ``YourSQL''. Следует отметить, что логический режим поиска не сортирует автоматически строки в порядке уменьшения релевантности. Это видно по результату предыдущего запроса, где строка с наиболее высокой релевантностью (содержащая слово ``MySQL'' дважды) помещена последней, а не первой. Логический режим полнотекстового поиска может работать даже без индекса FULLTEXT
, хотя и очень медленно.
В логическом режиме полнотекстового поиска поддерживаются следующие операторы:
+
-
MATCH() ... AGAINST()
без модификатора IN BOOLEAN MODE
.< >
<
уменьшает этот вклад, а оператор >
- увеличивает его. См. пример ниже.( )
~
*
"
Ниже приведен ряд примеров:
apple banana
+apple +juice
+apple macintosh
+apple -macintosh
+apple +(>pie
apple*
"some words"
Posted by Tim Gustafson on Friday May 17 2002, @6:24am | [Delete] [Edit] |
Note: you should add at least 3 rows to the table
before you try to match anything, and what you're
searching for should only be contained in one of
the three rows. This is because of the 50%
thereshold. If you insert only one row, then now
matter what you search for, it is in 50% or more
of the rows in the table, and therefore
disregarded.
Posted by Martin Glancy on Thursday August 29 2002, @12:06pm | [Delete] [Edit] |
A clarification for those very new to mySQL:
MATCH (body,title) will only work if you have an
index on both fields together - something created
like this:
ALTER TABLE some_tbl ADD FULLTEXT (body,title);
It will not work if you have an index on each
of 'body' and 'title' separately.
Posted by on Saturday October 12 2002, @6:50pm | [Delete] [Edit] |
I just discovered that MATCH (name,description)
AGAINST ('$search') doesn't return any results if
$search < 6 chars
Posted by Dyfed Lloyd Evans on Monday October 21 2002, @4:39am | [Delete] [Edit] |
Hyphen '-' characters break literals at the moment.
A search for something like "GATA-D22S690" finds
all entries containing GATA and not the full
hyphenated text. The '-' character is treated as a
word stop even within literals. The same is true if
any of the special text search modifiers are used (eg
+, -, ~) so that hyphenated literals are not correctly
found with full text searches.
Posted by Julien Hofstede on Sunday October 27 2002, @11:49pm | [Delete] [Edit] |
There is no way to check if the zero results are
caused by the 50%-thing or if there really are no
results.
There is no way to find out what the maximum of
relevance is.
Posted by on Friday November 15 2002, @4:54am | [Delete] [Edit] |
Note: When trying to search for an exact phrase in
boolean mode you have to use a construct like
MATCH(...) AGAINST('"my phrase"' IN BOOLEAN
MODE). Note the outer single-quotes!! The
documentation is not very clear in this point and it
took my a few hours to figure this out. If you don't
enclose it in single quotes the query becomes
a 'normal' search.
Posted by [name withheld] on Tuesday November 19 2002, @2:46pm | [Delete] [Edit] |
There seems to be a problem with using a Fulltext search
against a joined table where one of the Fulltext column
names is present in both tables....there's no way to fully
qualify the name for the match. For example: table 1
LISTING has id, title, category_id and table2 CATEGORY has
id and title. Since the MATCH has to be exact, you cannot
specify MATCH(table.column), and you cannot create the
table with FULLTEXT(table.column). Any work-around
besides renaming my columns? :)
Posted by on Monday December 9 2002, @8:51am | [Delete] [Edit] |
It should be noted in the documentation that IN
BOOLEAN MODE will almost always return a
relevance of 1.0. In order to get a relevance that is
meaningful, you'll need to:
SELECT MATCH('Content') AGAINST ('keyword1
keyword2') as Relevance FROM table WHERE MATCH
('Content') AGAINST('+keyword1 +keyword2' IN
BOOLEAN MODE) HAVING Relevance > 0.2 ORDER
BY Relevance DESC
Notice that you are doing a regular relevance query
to obtain relevance factors combined with a WHERE
clause that uses BOOLEAN MODE. The BOOLEAN
MODE gives you the subset that fulfills the
requirements of the BOOLEAN search, the relevance
query fulfills the relevance factor, and the HAVING
clause (in this case) ensures that the document is
relevant to the search (i.e. documents that score
less than 0.2 are considered irrelevant). This also
allows you to order by relevance.
This may or may not be a bug in the way that IN
BOOLEAN MODE operates, although the comments
I've read on the mailing list suggest that IN
BOOLEAN MODE's relevance ranking is not very
complicated, thus lending itself poorly for actually
providing relevant documents. BTW - I didn't notice
a performance loss for doing this, since it appears
MySQL only performs the FULLTEXT search once,
even though the two MATCH clauses are different.
Use EXPLAIN to prove this.
Posted by Pau Freixes Alio on Tuesday February 11 2003, @8:35am | [Delete] [Edit] |
I have I throw some tests with FULLTEXT, these are the results
1) Processing one file (size=800MB )width 250.000 articles with awk (raw format to insert sql format)
time awk -f raw2sql articles.raw > artiles.mysql
Posted by tony spencer on Wednesday March 5 2003, @10:47am | [Delete] [Edit] |
I hate the 3 character limit. I have scrapped my plans to use fulltext search because of it. I'm sure the 3 character limit is for performance improvement but I wish I had the option to lower it to 2.
If someone searches my site for '50' they get nothing. This is not acceptable for the site.
Posted by [name withheld] on Thursday March 6 2003, @2:08am | [Delete] [Edit] |
Has anyone else had any problems with using MATCH causes MySQL to crash?
I implemented the fulltext search on my website on three different tables and once every couple of days MySQL crashes. Could this be some kind of memory leak?
Posted by on Thursday March 6 2003, @2:40pm | [Delete] [Edit] |
TO tony spencer
regarding search limit of 3 characters
Why not implement 2 searches
1)simple 1/2 characters search
2)a fulltext search for queries greater than 2 charcters
and simply check the size of the query using whatever language you use to interact with MySQL. PHP being my Weapon of choice
Posted by on Friday March 7 2003, @1:19am | [Delete] [Edit] |
I am trying to find expressions like emd/c or kt/vt - it seems to me that the slash makes problems. I get all entries in my result set - no matter if the expression matches or not. Any ideas? Thanks a lot - Ka!
Add your own comment.