LIKE в SQL проверяет, соответствует ли строка шаблону. Символ % заменяет любую последовательность символов, включая пустую, а _ — ровно один символ. Для поиска в PostgreSQL без учёта регистра используют ILIKE. Например, name ILIKE '%sql%' найдёт названия SQL guide, sql workbook и PostgreSQL handbook.
Дальше все запросы выполняются в PostgreSQL 17.9. ILIKE — расширение PostgreSQL, поэтому переносить его в другую СУБД без проверки нельзя. Правила сопоставления регистра зависят от локали; примеры с ASCII-названиями позволяют проверить механику без особенностей конкретного алфавита. Определения операторов приведены в документации PostgreSQL.
Подготовить таблицу товаров
Выполни этот блок в учебном SQL-сеансе. Временная таблица существует только до его закрытия:
CREATE TEMP TABLE products (
id integer PRIMARY KEY,
name text
);
INSERT INTO products (id, name) VALUES
(1, 'SQL guide'),
(2, 'sql workbook'),
(3, 'PostgreSQL handbook'),
(4, 'T-shirt 100% cotton'),
(5, 'usb_c cable'),
(6, 'usb-c cable'),
(7, 'USB hub'),
(8, NULL),
(9, 'SQL');
Здесь есть разные регистры, буквальные % и _, а также неизвестное название NULL.
Знак % допускает и суффикс, и его отсутствие
LIKE 'SQL%' пропустит строки 1 и 9. Для строки с NULL результат неизвестен, поэтому она в выборку не попадёт.SQL LIKE: начало, конец и часть строки
Выберем товары, названия которых начинаются с SQL:
SELECT id, name
FROM products
WHERE name LIKE 'SQL%'
ORDER BY id;
Результат:
| id | name |
|---|---|
| 1 | SQL guide |
| 9 | SQL |
Товар SQL тоже подходит: % разрешает ноль символов. Без маски шаблон LIKE 'SQL' на нашей таблице совпадёт только с названием SQL; для обычного равенства понятнее написать name = 'SQL'.
Положение % меняет условие. Каждый шаблон из таблицы можно подставить в тот же запрос:
| Условие | Задача | Подходящие id |
|---|---|---|
name LIKE 'SQL%' | Начинается с SQL | 1, 9 |
name LIKE '%cotton' | Заканчивается на cotton | 4 |
name LIKE '%SQL%' | Содержит SQL в любой позиции | 1, 3, 9 |
name LIKE 'SQL' | Целиком совпадает с SQL | 9 |
LIKE сопоставляет всю строку. Без % по краям он не ищет фрагмент автоматически. Звёздочка * здесь тоже не заменяет %: это обычный символ, а не маска SQL LIKE.
Символ _ заменяет ровно один символ
Такой запрос найдёт два кабеля:
SELECT id, name
FROM products
WHERE name LIKE 'usb_c cable'
ORDER BY id;
| id | name |
|---|---|
| 5 | usb_c cable |
| 6 | usb-c cable |
В шаблоне _ стоит между usb и c. На его месте разрешён один символ: и подчёркивание, и дефис подходят. Поэтому это условие не проверяет буквальное имя кабеля.
Разницу между пустой последовательностью и одним символом видно в короткой проверке:
SELECT
'' LIKE '%' AS empty_matches_percent,
'' LIKE '_' AS empty_matches_underscore;
Ответ — true и false. Число _ задаёт точную длину участка; % такой длины не задаёт.
ILIKE: поиск без учёта регистра в PostgreSQL
Заменим оператор, оставив тот же префикс:
SELECT id, name
FROM products
WHERE name ILIKE 'sql%'
ORDER BY id;
| id | name |
|---|---|
| 1 | SQL guide |
| 2 | sql workbook |
| 9 | SQL |
PostgreSQL handbook не попал в ответ: слово начинается с Postgre, а шаблон требует sql в начале. Для поиска в любой позиции используй ILIKE '%sql%' — на наших данных это строки 1, 2, 3 и 9.
Для нелатинских названий проверяй поведение на локали своей базы. ILIKE не означает поиск похожих слов: он не исправляет опечатки и не делает транслитерацию.
ESCAPE: как найти буквальные % и _
Для поиска знака процента выберем ! как escape-символ. Он отменяет специальный смысл следующего % или _:
SELECT id, name
FROM products
WHERE name LIKE '%!%%' ESCAPE '!'
ORDER BY id;
Вернётся строка 4, T-shirt 100% cotton. Шаблон читается как три части: % допускает начало, !% требует настоящий знак процента, последний % допускает конец.
Буквальное подчёркивание ищется аналогично:
SELECT id, name
FROM products
WHERE name LIKE '%!_%' ESCAPE '!'
ORDER BY id;
Теперь подходит только строка 5, usb_c cable. Сам escape-символ тоже нужно экранировать: !! означает один !.
SELECT 'wow!' LIKE '%!!' ESCAPE '!' AS ends_with_exclamation;
Результат — true. Явный ESCAPE '!' помогает не путать экранирование шаблона с обратными слешами в строках SQL и языка приложения.
NOT LIKE и NULL: почему пропал товар без названия
Запрос «всё, что не начинается с SQL»:
SELECT id, name
FROM products
WHERE name NOT LIKE 'SQL%'
ORDER BY id;
Он вернёт id 2, 3, 4, 5, 6 и 7. Строки 8 нет: сравнение неизвестного названия с шаблоном даёт NULL, а отрицание неизвестности не превращает её в true.
Если товары без названия тоже должны входить в результат, назови это условие явно:
SELECT id, name
FROM products
WHERE name NOT ILIKE '%sql%' OR name IS NULL
ORDER BY id;
Ответ содержит id 4, 5, 6, 7 и 8. Обработка отсутствующих значений и оператор IS NULL описаны в документации PostgreSQL. Более подробно трёхзначная логика разобрана в статье про NULL.
Как передать поисковую строку из приложения
Не вставляй пользовательский ввод внутрь SQL через конкатенацию. Передавай целый шаблон параметром. Механизм можно проверить прямо в PostgreSQL:
PREPARE find_products(text) AS
SELECT id, name
FROM products
WHERE name ILIKE $1 ESCAPE '!'
ORDER BY id;
EXECUTE find_products('%sql%');
EXECUTE find_products('%!_%');
DEALLOCATE find_products;
Первое выполнение вернёт id 1, 2, 3 и 9, второе — id 5. $1 обозначает значение параметра, а не часть текста запроса. В драйвере приложения синтаксис заполнителя может быть другим; механизм PREPARE описан в справочнике PostgreSQL.
Параметризация защищает структуру SQL, но не отключает маски. Если пользователь ввёл %, параметр со значением %%% всё ещё найдёт любое известное название. Для буквального поиска сначала замени в пользовательском тексте ! на !!, затем % на !%, _ на !_ и только после этого добавь % с двух сторон. Для ввода usb_c получится %usb!_c%. Если интерфейс намеренно разрешает пользователю маски, такое экранирование, наоборот, меняет согласованное поведение поиска.
Почему индекс не всегда ускоряет LIKE
Для префикса LIKE 'SQL%' PostgreSQL может использовать B-tree; при локали, отличной от C, может понадобиться специальный класс операторов. У LIKE '%SQL%' нет фиксированного начала, поэтому такой поиск не получает то же преимущество обычного B-tree. Это ограничение описано в документации типов индексов.
По таблице из девяти товаров нельзя оценить ускорение: последовательное чтение здесь дёшево. Если поиск стал медленным на реальных данных, проверь план через EXPLAIN ANALYZE, затем выбирай индекс под фактическое условие. Варианты для текстового поиска разобраны в руководстве по индексам PostgreSQL.
Для самостоятельной проверки найди товары, названия которых содержат буквальное подчёркивание или знак процента. На этой таблице должны остаться только id 4 и 5; usb-c cable в ответ попадать не должен.