Как ускорить SELECT .. LIKE запросы в MySQL по нескольким столбцам? - PullRequest
40 голосов
/ 11 января 2010

У меня есть таблица MySQL, для которой я делаю очень частые SELECT x, y, z FROM table WHERE x LIKE '%text%' OR y LIKE '%text%' OR z LIKE '%text%' запросы.Поможет ли какой-нибудь индекс ускорить процесс?

В таблице несколько миллионов записей.Если есть что-то, что ускорит поиск, серьезно ли это повлияет на использование диска файлами базы данных и скорость операторов INSERT и DELETE?(UPDATE никогда не выполняется)

Обновление : вскоре после публикации я увидел много информации и дискуссий о том, как LIKE используется в запросе;Я хотел бы отметить, что решение должно использовать LIKE '%text%' (то есть, текст, который я ищу, добавляется с добавлением подстановочного знака%).База данных также должна быть локальной по многим причинам, включая безопасность.

Ответы [ 7 ]

60 голосов
/ 11 января 2010

Индекс не ускорит запрос, потому что для текстовых столбцов индексы работают, индексируя N символов, начиная слева. Когда вы делаете LIKE «% text%», он не может использовать индекс, потому что перед текстом может быть переменное количество символов.

То, что вы должны делать, это вообще не использовать такой запрос. Вместо этого вы должны использовать что-то вроде FTS (полнотекстовый поиск), который MySQL поддерживает для таблиц MyISAM. Также довольно легко создать такую ​​систему индексации для таблиц, не относящихся к MyISAM, вам просто нужна отдельная таблица индексов, в которой вы храните слова и соответствующие им идентификаторы в фактической таблице.

18 голосов
/ 11 января 2010

Индекс не поможет сопоставлению текста с лидирующим подстановочным знаком, индекс можно использовать для:

LIKE 'text%'

Но я предполагаю, что это не сработает. Для этого типа запроса вам действительно стоит обратиться к поставщику полнотекстового поиска, если вы хотите масштабировать количество записей, по которым вы можете искать. Мой предпочтительный провайдер - Sphinx , очень полнофункциональный / быстрый и т. Д. Lucene также может стоить посмотреть. Полнотекстовый индекс для таблицы MyISAM также будет работать, но, в конечном счете, использование MyISAM для любой базы данных, в которой имеется значительное количество записей, не является хорошей идеей.

12 голосов
/ 11 января 2010

Индекс может не использоваться для ускорения запросов, когда критерии поиска начинаются с подстановочного знака:

LIKE '%text%'

Индекс может (и может быть, в зависимости от селективности) использоваться для условий поиска в форме:

LIKE 'text%'

10 голосов
/ 26 ноября 2013

Я бы добавил, что в некоторых случаях вы можете ускорить запрос, используя индекс вместе с like / rlike, если поле, на которое вы просматриваете, часто пустое или содержит нечто постоянное.

В этом случае кажется, что вы можете ограничить число посещаемых строк с помощью индекса, добавив предложение "и" с фиксированным значением.

Я попробовал это для поиска «тегов» в огромной таблице, которая обычно не содержит много тегов.

SELECT * FROM objects WHERE tags RLIKE("((^|,)tag(,|$))" AND tags!=''

Если у вас есть индекс для тегов, вы увидите, что он используется для ограничения строк, в которых ведется поиск.

6 голосов
/ 06 июня 2017

Может быть, вы можете попытаться обновить mysql5.1 до mysql5.7.

У меня около 70000 записей. И запустить следующий SQL:

select * from comics where name like '%test%'; 

Требуется 2000мс в mysql5.1. И это занимает 200мс в MySQL5.7 или MySQL5.6.

0 голосов
/ 09 января 2019

Другой способ:

Вы можете вести расчетные столбцы с этими строками REVERSEd и использовать

SELECT x, y, z FROM table WHERE x LIKE 'text%' OR y LIKE 'text%' OR z LIKE 'text%' OR xRev LIKE 'txet%' OR yRev LIKE 'txet%' OR zRev LIKE 'txet%' 

Пример того, как ДОБАВИТЬ сохраненный постоянный столбец

ALTER TABLE table ADD COLUMN xRev VARCHAR(N) GENERATED ALWAYS AS REVERSE(x) stored;

, а затем создать индексы для xRev, yRev и т. Д.

0 голосов
/ 08 июня 2018

Другая альтернатива, позволяющая избежать полных сканирований таблицы, - это выбрать подстроки и проверить их в операторе using:

SELECT 
    al3.article_number,
    SUBSTR(al3.article_number, 2, 3) AS art_nr_substr,
    SUBSTR(al3.article_number, 1, 3) AS art_nr_substr2,
    al1.*
FROM
    t1 al1 
    INNER JOIN t2 al2 ON al2.t1_id = al1.id
    INNER JOIN t3 al3 ON al3.id = al2.t3_id
WHERE
    al1.created_at > '2018-05-29'
HAVING 
    (art_nr_substr = "FLA" OR art_nr_substr = 'VKV' OR art_nr_subst2 = 'PBR');
...