LIKE и ILIKE в SQL: поиск по строкам и паттернам
Как использовать LIKE и ILIKE в SQL для поиска по тексту, префиксам и шаблонам, учитывать регистр и не превратить запрос в медленный скан.
Содержание статьи
Поиск «найди все события checkout» быстро превращается в LIKE %checkout%, фильтрацию по регистру и неожиданные совпадения. Пользователю нужен релевантный список, а аналитик должен понимать, какие строки и почему попали в выборку. В этой статье отвечаем на вопрос: Как искать по тексту в SQL и понимать, когда шаблон перестаёт использовать индекс?
Коротко
Для точного события используй =. Для известного префикса — LIKE prefix. Для регистронезависимого исследовательского поиска в PostgreSQL — ILIKE. Для больших полей проектируй отдельный механизм, а не наращивай цепочку LIKE.
- Определи зерно результата до написания оператора.
- Разделяй фильтрацию строк, групп и рассчитанных показателей.
- Проверь NULL, дубли, границы периода и стабильность сортировки.
- Сверь запрос на маленьком примере перед публикацией метрики.
Какую задачу решает этот оператор
В LIKE знак % заменяет любое количество символов, _ — один символ. ILIKE в PostgreSQL игнорирует регистр. Шаблон с ведущим % обычно сложнее индексировать, чем префиксный. Для больших текстовых полей может понадобиться полнотекстовый поиск.
event_name LIKE 'checkout_%' найдёт checkout_start и checkout_error, но также checkout_old, если такое имя появится. Для аналитики событий лучше использовать точные значения или отдельный event_group, а LIKE оставить для исследовательского поиска.
Рабочий пример для аналитика
Сначала задай ожидаемый набор совпадений и негативные примеры. Проверь регистр, пробелы, локаль и спецсимволы. Посмотри EXPLAIN на реальном объёме. Если пользовательский поиск допускает произвольный текст, отдели поисковую задачу от аналитического фильтра.
Пример ниже намеренно оставляет явными период, ключи связи и уровень агрегации. Если заменить их на «как принято в базе», запрос может начать считать другую сущность. Сначала адаптируй названия колонок под свою схему, затем проверь результат на нескольких известных строках.
SELECT event_name, COUNT(*) AS events
FROM events
WHERE event_name ILIKE 'checkout%' AND occurred_at >= DATE '2026-07-01'
GROUP BY event_name ORDER BY events DESC;Как проверить результат
Первый тест — арифметический: совпадает ли число строк с ожидаемым зерном, а сумма подгрупп с общей суммой? Второй — поведенческий: что происходит на пустой группе, NULL, повторном событии и границе периода? Третий — сравнительный: можно ли получить тот же результат независимым способом на небольшом срезе?
Не ограничивайся тем, что запрос выполнился. Рабочий SQL должен быть объяснимым: другой аналитик понимает источник, фильтр, связь и причину выбранного оператора. Если результат меняется после добавления строки, зафиксируй, какое бизнес-правило это объясняет.
Для воспроизводимости сохрани рядом с запросом дату проверки, ожидаемый результат на контрольной выборке и версию определения метрики. Если SQL выполняется в BI, проверь, какие параметры подставляет интерфейс и не меняется ли часовая зона. Перед передачей запроса другому человеку убери неявные зависимости от сортировки, текущей даты и локальных настроек.
| Шаблон | Найдёт | Замечание |
|---|---|---|
| checkout | точное значение | самый узкий фильтр |
| checkout% | префикс checkout | проверить контракт |
| %checkout% | вхождение где угодно | дороже и шире |
| checkout_ | один символ после | специальное правило |
Зерно данных и границы расчёта
До запуска запроса выпиши, что означает одна строка в каждой таблице. В orders это может быть заказ, в order_items — позиция заказа, а в events — отдельное событие. Оператор работает поверх этого зерна: если соединить таблицы разных уровней без промежуточной агрегации, сумма и количество начнут отвечать уже на другой вопрос.
Зафиксируй период, часовую зону, статус записи и правило для повторов. Для событий важно отделить повторную отправку от двух реальных действий пользователя. Для денежных показателей нужно понять, входят ли скидки, возвраты и отмены. Эти условия должны быть видны в запросе или в названном CTE, а не жить только в голове автора.
Проверка на маленьком наборе
Сложный запрос стоит сначала проверить на пяти-десяти строках, где ответ можно посчитать вручную. Добавь один NULL, один дубль, одну строку ровно на границе периода и одну запись без связанной строки. Такой набор быстрее показывает ошибку в логике, чем просмотр тысячи строк в BI.
После ручной проверки сравни контрольные суммы и количество уникальных ключей до и после каждого важного шага. Если JOIN изменил число строк, это не обязательно ошибка, но причина должна быть понятна. Если оператор удаляет строки, запиши, какие именно условия это делают и почему они соответствуют бизнес-вопросу.
От запроса к рабочему решению
SQL-результат становится полезным только после интерпретации. Назови, какое изменение команда увидит, какой сегмент его объясняет и какое действие можно проверить дальше. Не выдавай техническую конструкцию за причинный вывод: оператор помогает посчитать срез, но сам по себе не доказывает, почему метрика изменилась.
Для дашборда оставь понятные подписи, период обновления, фильтры и ссылку на определение метрики. Для ревью сохрани запрос, допущения и известные ограничения. Если материал пойдёт в SQL-тренажёр, добавь ожидаемое зерно и несколько тестовых случаев, чтобы пользователь учился объяснять запрос, а не только получать правильный результат.
Как читать чужой SQL-запрос
Начинай не с отдельных операторов, а с внешнего результата. Найди SELECT, посмотри, какие поля группируются, затем поднимись к FROM и JOIN. У каждого источника подпиши примерное зерно и ожидаемую кардинальность связи. После этого проверь WHERE: он может убрать пользователей до расчёта, а может незаметно исключить строки, которые нужны для знаменателя.
Если в запросе есть CTE, читай их сверху вниз и фиксируй, что изменилось на каждом шаге: только названия, число строк, зерно или бизнес-смысл. Если есть окно, проверь PARTITION BY и ORDER BY. Если есть отрицательное условие, отдельно протестируй NULL. Такой порядок чтения помогает делать ревью по логике, а не спорить о стиле форматирования.
Для сравнения двух решений собери маленькую таблицу: результат, зерно, обработанные строки, поведение на NULL, границы периода и стоимость. Иногда два запроса дают одинаковые цифры на текущем срезе, но один будет ломаться после появления повторного события или новой категории. Хорошее решение выдерживает не только текущий пример, но и заранее названные крайние случаи.
Что чаще всего ломается
LIKE %term% может сканировать таблицу. Символы % и _ в пользовательском вводе имеют смысл шаблона и требуют экранирования. ILIKE не решает морфологию и опечатки. Не используй текстовый поиск вместо нормализованного справочника.
Отдельно проверь изменение зерна после JOIN или разворачивания массива. Большая часть странных цифр появляется не из-за сложного синтаксиса, а потому что одна строка стала описывать два разных факта. Полезный приём — временно вывести ключ и посчитать COUNT(*) по нему до и после спорного шага.
Когда выбрать другой подход
Для точного события используй =. Для известного префикса — LIKE prefix. Для регистронезависимого исследовательского поиска в PostgreSQL — ILIKE. Для больших полей проектируй отдельный механизм, а не наращивай цепочку LIKE.
Оператор не обязан быть единственным решением. Сравни альтернативу по трём критериям: совпадает ли смысл, видно ли зерно и можно ли проверить стоимость выполнения. Более короткий запрос не всегда лучше, если он прячет важное бизнес-правило или делает дубли незаметными.
Чем шире условие, тем важнее посмотреть список совпавших значений.
Мини-практика
Собери аудит event_name: список уникальных значений и частоты. Сравни точное равенство, prefix LIKE и contains LIKE. Найди значения с разным регистром и пробелами, затем реши, где нужен трекинг, а где достаточно фильтра.
После решения напиши короткое объяснение в формате: «одна строка результата — …; в числителе — …; знаменатель — …; период — …; потенциальное ограничение — …». Такой шаблон хорошо переносится в SQL-тренажёр, ревью и рабочий отчёт.
- Сначала реши, что является единицей результата.
- Проверь граничные значения и пустые наборы.
- Сравни результат с ручным примером.
- Сохрани запрос рядом с определением метрики.
- Назови, что запрос не умеет доказать.
Вывод
Для точного события используй =. Для известного префикса — LIKE prefix. Для регистронезависимого исследовательского поиска в PostgreSQL — ILIKE. Для больших полей проектируй отдельный механизм, а не наращивай цепочку LIKE.
Главный навык здесь — не выучить оператор, а увидеть уровень данных, на котором он работает. Когда вопрос, зерно и период зафиксированы, SQL становится воспроизводимым способом принять решение, а не набором случайных конструкций.
Материалы по теме
JSONB в PostgreSQL: операторы и запросы к вложенным данным
Как читать JSONB в PostgreSQL: ->, ->>, @>, jsonb_array_elements и индексы. Практика для свойств событий и гибких атрибутов.
Читать материалLATERAL JOIN в PostgreSQL: как получить последнюю связанную запись
Практический гайд по LATERAL JOIN: последняя покупка пользователя, top-N событий на сущность и сравнение с оконными функциями.
Читать материалОператоры SQL: шпаргалка для аналитика с примерами применения
Большая шпаргалка по SQL-операторам: фильтрация, сравнение, агрегаты, JOIN, окна, строки, даты и JSONB с маршрутами для практики.
Читать материал