LIKE и шаблоны (% и _)
LIKE и шаблоны (% и _)
В третьем модуле вы освоили все базовые инструменты фильтрации: WHERE, логические операторы, сортировку, ограничение результата. Оператор = отлично работает, когда вы знаете точное значение — WHERE city = 'Москва'. Но что если нужно найти всех клиентов, чьё имя начинается с «Ан»? Или все email-адреса на домене @gmail.com? Или продукты, в названии которых есть слово «ноутбук» независимо от регистра? Для поиска по шаблону в SQL существует оператор LIKE.
Что такое LIKE
LIKE — это оператор сравнения строк, который поддерживает шаблоны (wildcards). В отличие от =, который требует точного совпадения, LIKE позволяет искать строки, содержащие определённые подстроки, начинающиеся или заканчивающиеся определённым образом.
Базовый синтаксис:
SELECT колонки
FROM таблица
WHERE колонка LIKE 'шаблон';
Метасимвол «%»: любое количество символов
Символ % в шаблоне означает «любое количество любых символов (включая ноль)». Это самый используемый метасимвол в LIKE.
Примеры:
-- Все строки, начинающиеся с 'Ан'
WHERE name LIKE 'Ан%' -- найдёт: Анна, Антон, Андрей, Анастасия
-- Все строки, заканчивающиеся на 'ов'
WHERE name LIKE '%ов' -- найдёт: Иванов, Петров, Кузнецов
-- Все строки, содержащие 'sql' в любом месте
WHERE description LIKE '%sql%' -- найдёт: "Введение в SQL", "MySQL tutorial", "PostgreSQL docs"
Таблица products:
id | name
---+---------------------------
1 | Ноутбук HP ProBook
2 | Ноутбук Lenovo IdeaPad
3 | Мышь Logitech
4 | Монитор Samsung
5 | Клавиатура механическая
Запрос:
SELECT name FROM products WHERE name LIKE 'Ноутбук%';
Результат: «Ноутбук HP ProBook» и «Ноутбук Lenovo IdeaPad». Остальные строки не начинаются с «Ноутбук».
Проверь себя: сколько строк из таблицы выше вернёт WHERE name LIKE '%а%'?
Метасимвол «_»: ровно один символ
Символ _ (подчёркивание) означает ровно один любой символ. Это полезно, когда вы знаете длину строки или позицию нужного символа.
-- Артикулы вида A-001, B-002, C-003 (одна буква, дефис, три цифры)
WHERE article LIKE '_-___'
-- Телефоны в формате +7-XXX-XXX-XX-XX (подсчитайте символы)
WHERE phone LIKE '+7-___-___-__-__'
Практический пример — найти все коды продуктов, которые начинаются с «P» и имеют ровно 4 символа:
SELECT code, name FROM products WHERE code LIKE 'P___';
-- Вернёт: P001, P123, PABC, но не P01 или P00123
Сочетание % и _ даёт гибкий поиск:
-- Строки, где вторая буква — 'а'
WHERE name LIKE '_а%' -- Мария, Налог, Санкт-Петербург, Дана
LIKE чувствителен к регистру
По умолчанию LIKE в PostgreSQL чувствителен к регистру:
WHERE name LIKE 'ноутбук%' -- не найдёт 'Ноутбук HP ProBook'
WHERE name LIKE 'Ноутбук%' -- найдёт
Для поиска без учёта регистра используется оператор ILIKE (регистронезависимый LIKE, специфичный для PostgreSQL):
WHERE name ILIKE 'ноутбук%' -- найдёт и 'Ноутбук', и 'ноутбук', и 'НОУТБУК'
Альтернатива, работающая в любой СУБД — LOWER():
WHERE LOWER(name) LIKE 'ноутбук%' -- приводим колонку к нижнему регистру
В SQLite LIKE по умолчанию нечувствителен к регистру для ASCII-символов, но чувствителен для символов вне ASCII (кирилличные буквы). На практике лучше явно использовать LOWER().
NOT LIKE: исключение по шаблону
NOT LIKE работает как инверсия — возвращает строки, которые не соответствуют шаблону:
-- Все товары, название которых не содержит 'Ноутбук'
SELECT name FROM products WHERE name NOT LIKE '%Ноутбук%';
Результат: Мышь Logitech, Монитор Samsung, Клавиатура механическая.
Экранирование метасимволов
Что если нужно найти строки, которые буквально содержат % или _? Например, скидку «10%» или колонку со значением «кол_во»? Эти символы нужно экранировать.
В PostgreSQL для экранирования используется обратный слеш \ или явное задание escape-символа через ESCAPE:
-- Найти строки, содержащие буквальный знак процента
WHERE discount_text LIKE '%10\%%' ESCAPE '\'
-- Или задать другой escape-символ
WHERE code LIKE '%!_%' ESCAPE '!' -- ищем буквальное подчёркивание
На практике это редкая ситуация — когда в данных есть символы % или _, которые нужно искать буквально. Но знать о такой возможности полезно.
Производительность LIKE
LIKE с шаблоном в начале ('%keyword%') не может использовать обычный индекс — СУБД вынуждена сканировать всю таблицу. Это медленно на больших данных.
LIKE 'keyword%' (шаблон только в конце) может использовать B-tree индекс и работает быстро.
Для полнотекстового поиска PostgreSQL предлагает специальные механизмы (tsvector, tsquery, индексы GIN), но это уже за пределами базового курса.
Практический пример: поиск по базе клиентов
Задача: найти всех клиентов с email на корпоративном домене и именем, начинающимся с «А»:
SELECT name, email
FROM customers
WHERE email LIKE '%@company.ru'
AND name LIKE 'А%';
Запрос комбинирует два LIKE-условия через AND. Строки, удовлетворяющие обоим условиям, попадают в результат.
Другой пример — поиск товаров по части артикула:
SELECT article, name, price
FROM products
WHERE article LIKE 'NB-%' -- все ноутбуки (артикул начинается с NB-)
ORDER BY price ASC;
Типичные ошибки
1. Забытый % вокруг шаблона:
WHERE name LIKE 'Ноутбук' -- то же самое, что = 'Ноутбук'
WHERE name LIKE '%Ноутбук%' -- правильно для поиска подстроки
LIKE без метасимволов эквивалентен =.
2. Путаница между % и _:
WHERE code LIKE 'P%' -- все коды, начинающиеся с P (любой длины)
WHERE code LIKE 'P_' -- коды ровно из 2 символов, первый P
3. Регистр в PostgreSQL:
WHERE name LIKE '%ноутбук%' -- в PostgreSQL не найдёт 'Ноутбук HP'
WHERE name ILIKE '%ноутбук%' -- найдёт независимо от регистра
LIKE vs ILIKE vs LOWER(): что выбрать
В PostgreSQL есть три подхода к регистронезависимому поиску:
ILIKE — самое простое решение, специфичное для PostgreSQL:
WHERE name ILIKE '%ноутбук%'
LOWER() — работает в любой СУБД, включая MySQL и SQLite:
WHERE LOWER(name) LIKE '%ноутбук%'
UPPER() — то же самое, но приводит к верхнему регистру:
WHERE UPPER(name) LIKE '%НОУТБУК%'
Если ваш код должен быть переносимым между СУБД — используйте LOWER(). Если пишете только для PostgreSQL — ILIKE удобнее и нагляднее.
LIKE в реальных задачах
Вот несколько типичных сценариев, где LIKE незаменим:
Поиск по части имени в базе клиентов:
SELECT id, first_name, last_name, email
FROM customers
WHERE last_name ILIKE '%иванов%' -- найдёт Иванов, Иванова, Иванович...
ORDER BY last_name;
Фильтрация URL по домену:
SELECT page_url, visits
FROM analytics
WHERE page_url LIKE 'https://shop.example.com/%'
ORDER BY visits DESC;
Поиск по коду SKU (Stock Keeping Unit):
SELECT sku, product_name, stock
FROM inventory
WHERE sku LIKE 'EL-%' -- все SKU из категории «Электроника»
AND stock > 0;
Поиск дубликатов email-провайдеров:
SELECT DISTINCT
CASE
WHEN email LIKE '%@gmail.com' THEN 'Gmail'
WHEN email LIKE '%@yandex.ru' THEN 'Яндекс'
WHEN email LIKE '%@mail.ru' THEN 'Mail.ru'
ELSE 'Другое'
END AS provider
FROM customers;
Этот пример использует CASE WHEN — мощный инструмент условных выражений, который мы подробно разберём в уроке 4-5. Сейчас воспринимайте его как «если email заканчивается на X, то верни строку Y».
Краткий итог
LIKE— поиск по шаблону;%— любое количество символов;_— ровно один символLIKE 'abc%'— начинается с «abc»;LIKE '%abc'— заканчивается на «abc»;LIKE '%abc%'— содержит «abc»NOT LIKE— строки, не соответствующие шаблону- В PostgreSQL
LIKEчувствителен к регистру;ILIKE— нечувствителен;LOWER()— переносимый вариант LIKE '%keyword%'не использует обычный индекс — медленно на больших данных- Для экранирования
%и_используйте\илиESCAPE
Что дальше
Теперь вы умеете искать по шаблону. Следующий оператор — IN, который позволяет проверить, входит ли значение в заданный список. Это удобная альтернатива нескольким условиям через OR.