Проблема поиска: почему LIKE в SQL не работает

Разбираемся, почему обычная база данных плохо ищет по тексту и зачем вообще понадобился отдельный поисковый движок.

Полнотекстовый поиск — это поиск документов по словам и их формам с учётом релевантности, а не по точному совпадению подстроки.

Зачем это нужно

Представьте каталог интернет-магазина на миллион товаров. Пользователь вводит в строку поиска «красные кросовки найк» — с опечаткой, в неправильном падеже, со словами в произвольном порядке. Он ждёт, что система поймёт намерение и покажет самые подходящие товары сверху. Обычная реляционная база данных с этим справляется плохо, и сейчас мы разберём почему.

Как ищет LIKE

В SQL для поиска по тексту обычно берут оператор LIKE. Он сравнивает значение поля с шаблоном, где % заменяет любое число символов.

SELECT * FROM products
WHERE name LIKE '%кросовки%';

Этот запрос найдёт строки, где подстрока «кросовки» встречается где угодно внутри поля name. На первый взгляд работает. Но проблемы начинаются сразу.

Четыре фундаментальные проблемы

1. Медленно

Шаблон вида '%слово%' начинается с %, поэтому обычный B-tree индекс по полю не используется — база вынуждена прочитать каждую строку таблицы и проверить её (это называется full table scan). На миллионе строк это сотни миллисекунд или секунды на каждый запрос.

2. Только точная подстрока

LIKE ищет ровно ту последовательность символов, что вы написали. Запрос по «кросовки» не найдёт «кроссовки» (правильное написание), «кроссовок» (другой падеж) и «кеды». Морфологию и опечатки он не понимает в принципе.

3. Нет релевантности

LIKE возвращает строки в произвольном порядке (или по тому, что вы укажете в ORDER BY). Он не умеет сказать «вот этот документ подходит лучше, потому что слово встречается в заголовке и трижды». Результат — плоский список без ранжирования.

4. Слова в одном поле, без анализа

Если искать сразу по нескольким словам в разном порядке («найк красные»), приходится писать громоздкие конструкции с AND и кучей LIKE, и всё равно без учёта близости слов и их важности.

Как работает под капотом

Реляционная БД хранит данные построчно и оптимизирована под точные выборки по ключам и диапазонам: «дай заказ с id=42», «все заказы за март». Её индексы — это отсортированные деревья по значениям полей. Для запроса «строки, содержащие слово где-то в середине текста» такая структура бесполезна: нужного нам ключа в дереве просто нет. Поисковый движок устроен наоборот — он заранее раскладывает текст на отдельные слова и строит индекс от слова к документам. Именно поэтому он ищет по словам мгновенно. Эту структуру — инвертированный индекс — мы разберём через два урока.

Частые ошибки

  • «Поставлю полнотекстовый индекс в Postgres и хватит». Встроенный full-text search в PostgreSQL действительно есть и для небольших проектов его достаточно. Но он уступает Elasticsearch по гибкости анализа, ранжированию, агрегациям и горизонтальному масштабированию.
  • Путать поиск и фильтрацию. «Товары дешевле 1000 рублей» — это фильтр, тут SQL отлично подходит. «Товары, похожие на запрос пользователя» — это поиск с релевантностью, тут нужен движок.

Итоги

  • LIKE медленный (full scan), не понимает морфологию и опечатки, не ранжирует результаты.
  • Реляционная БД оптимизирована под точные выборки, а не под поиск по словам внутри текста.
  • Полнотекстовый поиск — это поиск по словам с релевантностью; для него нужен специальный движок.
Проверьте себя
1. Почему запрос с LIKE '%слово%' обычно медленный на больших таблицах?
AПотому что LIKE всегда блокирует таблицу
BПотому что шаблон с ведущим % не позволяет использовать B-tree индекс и нужен full table scan
CПотому что LIKE работает только с числами
DПотому что база сортирует результат по релевантности
2. Какую задачу LIKE решает плохо?
AНайти строку по точному id
BНайти товары дешевле 1000 рублей
CНайти документы по словам с учётом морфологии и релевантности
DВыбрать все строки таблицы