BETWEEN, IN y LIKE: filtros breves
Qué aprenderás
- a escribir filtros breves con
BETWEEN,INy patronesLIKEcon%y_ - a buscar texto sin distinguir mayúsculas con
ILIKEoLOWER(...)y a escapar%/_conESCAPE - a explicar por qué
LIKE 'абв%'se acelera con un índice, mientras que un%inicial obliga a recorrer la tabla entera
Operadores de condición cómodos
Los restauradores de la vieja Tierra reconstruían libros a partir de un fragmento del dibujo de la encuadernación. El archivero de datos hace algo parecido: en lugar de dar al archivo un valor exacto, le da un patrón —un rango, una lista, una plantilla— y deja que busque las coincidencias.
BETWEEN a AND b: el valor está en el rango de a a b, ambos incluidos;IN (...): el valor está en la lista;LIKE 'patrón': el texto encaja con el patrón, donde%es cualquier número de caracteres y_es un solo carácter.
Estos operadores ahorran espacio y dejan clara la intención. En vez de escribir una larga cadena de condiciones, formulas algo casi en lenguaje natural: artículos de las categorías Libros o Juguetes, con un precio de entre 1000 y 3000. Para el catálogo products era una tarea diaria en 2024, y ahora también lo es para ti.

BETWEEN, la lista IN y la plantilla LIKE; el archivo busca las coincidencias por sí solo.Три разных образца для поиска
У этих операторов разные роли.
BETWEEN отвечает на вопрос:
Значение лежит между двумя границами?
Например:
price BETWEEN 1000 AND 3000
Читается как:
цена от 1000 до 3000 включительно.
IN отвечает на вопрос:
Значение входит в этот набор вариантов?
Например:
category IN ('Книги', 'Игрушки')
Читается как:
категория — Книги или Игрушки.
LIKE отвечает на вопрос:
Текст похож на этот шаблон?
Например:
name LIKE 'Кофе%'
Читается как:
название начинается с «Кофе».
То есть:
| Оператор | Когда использовать | Пример |
|---|---|---|
BETWEEN | нужен диапазон | price BETWEEN 1000 AND 3000 |
IN | нужен список вариантов | category IN ('Книги', 'Игрушки') |
LIKE | нужен текстовый шаблон | name LIKE 'Кофе%' |
BETWEEN: значение между двумя границами
BETWEEN проверяет, что значение находится в диапазоне.
Например, нужно найти товары с ценой от 1000 до 3000:
SELECT name, price
FROM products
WHERE price BETWEEN 1000 AND 3000;
Это то же самое, что написать:
SELECT name, price
FROM products
WHERE price >= 1000
AND price <= 3000;
Главная деталь:
BETWEEN включает обе границы.
То есть условие:
price BETWEEN 1000 AND 3000
вернёт товары с ценой:
- 1000
- 1500
- 2999
- 3000
Цена 1000 входит.
Цена 3000 тоже входит.
Если нужно строго больше 1000 и строго меньше 3000, BETWEEN не подходит. Тогда нужно писать обычные сравнения:
WHERE price > 1000
AND price < 3000
BETWEEN 1000 AND 3000 включает обе границы: и 1000, и 3000 попадут в результат.BETWEEN читается почти как обычная фраза: цена между 1000 и 3000.| name | category | price |
|---|---|---|
| Мягкая игрушка кот | Игрушки | 1290.00 |
| SQL для профи | Книги | 1290.00 |
| Чистый код | Книги | 1490.00 |
| Алгоритмы на практике | Книги | 1690.00 |
| Плед флисовый | Дом | 1890.00 |
| Конструктор кубики | Игрушки | 1990.00 |
| Настольная лампа | Дом | 2190.00 |
| Веб-камера HD | Электроника | 2790.00 |
| Худи оверсайз | Одежда | 2990.00 |
Важные детали BETWEEN
1. Границы не переставляются автоматически
Такой фильтр нормальный:
WHERE price BETWEEN 1000 AND 3000
А такой почти всегда вернёт пустой результат:
WHERE price BETWEEN 3000 AND 1000
Потому что база читает это как:
WHERE price >= 3000
AND price <= 1000
Одновременно быть больше или равно 3000 и меньше или равно 1000 обычное число не может.
Поэтому порядок границ важен: сначала нижняя, потом верхняя.
2. Есть обратная форма NOT BETWEEN
Если нужно найти товары вне диапазона, можно написать:
SELECT name, price
FROM products
WHERE price NOT BETWEEN 1000 AND 3000;
Это похоже на:
WHERE price < 1000
OR price > 3000
То есть товар за 500 попадёт.
Товар за 4000 попадёт.
Товар за 1000 не попадёт.
Товар за 3000 не попадёт.
Потому что границы входят в сам диапазон BETWEEN.
3. С датами и временем нужно быть аккуратнее
С датами BETWEEN тоже работает:
WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31'
Но если created_at — это не просто дата, а дата со временем, например:
2024-01-31 18:45:00
то такой фильтр может случайно не захватить весь последний день.
Почему? Потому что '2024-01-31' для timestamp часто означает начало дня:
2024-01-31 00:00:00
Поэтому для периода по времени часто безопаснее писать полуоткрытый интервал:
WHERE created_at >= '2024-01-01'
AND created_at < '2024-02-01'
Так мы берём всё с 1 января включительно и всё до 1 февраля, но не включая 1 февраля.
Для новичка главное правило такое:
Для чисел
BETWEENудобен и понятен. Для дат со временем проверяй границы особенно внимательно.
IN: значение входит в список
IN используют, когда нужно проверить значение на несколько возможных вариантов.
Например, нужно найти товары только из категорий «Книги» и «Игрушки»:
SELECT name, category, price
FROM products
WHERE category IN ('Книги', 'Игрушки');
Это короче и понятнее, чем:
SELECT name, category, price
FROM products
WHERE category = 'Книги'
OR category = 'Игрушки';
IN читается так:
категория входит в список: Книги, Игрушки.
Особенно хорошо IN смотрится, когда вариантов много:
WHERE category IN ('Книги', 'Игрушки', 'Аксессуары', 'Корм', 'Домики')
Вместо длинной цепочки OR получается один компактный фильтр.
IN заменяет несколько условий через OR.| name | category | price |
|---|---|---|
| Конструктор кубики | Игрушки | 1990.00 |
| Мягкая игрушка кот | Игрушки | 1290.00 |
| Пазл 1000 деталей | Игрушки | 890.00 |
| Радиоуправляемая машина | Игрушки | 3290.00 |
| SQL для профи | Книги | 1290.00 |
| Алгоритмы на практике | Книги | 1690.00 |
| Чистый код | Книги | 1490.00 |
Важные детали IN
1. IN удобен для списка точных значений
IN не ищет похожие значения. Он проверяет точное совпадение с одним из вариантов.
WHERE category IN ('Книги', 'Игрушки')
Найдёт категорию 'Книги'.
Но не найдёт:
Книга
книги
КНИГИ
Электронные книги
Потому что это уже другие строки.
2. NOT IN исключает значения из списка
Если нужно убрать несколько категорий, можно написать:
SELECT name, category, price
FROM products
WHERE category NOT IN ('Книги', 'Игрушки');
Это читается так:
покажи товары, категория которых не входит в список «Книги», «Игрушки».
Но у NOT IN есть важная ловушка с NULL. Она будет ниже в отдельном блоке.
3. IN хорошо сочетается с BETWEEN
Например:
SELECT name, category, price
FROM products
WHERE category IN ('Книги', 'Игрушки')
AND price BETWEEN 1000 AND 3000
ORDER BY price;
Читается так:
покажи книги и игрушки с ценой от 1000 до 3000.
Это уже полноценный рабочий фильтр: список категорий плюс диапазон цены.
| name | category | price |
|---|---|---|
| SQL для профи | Книги | 1290.00 |
| Мягкая игрушка кот | Игрушки | 1290.00 |
| Чистый код | Книги | 1490.00 |
| Алгоритмы на практике | Книги | 1690.00 |
| Конструктор кубики | Игрушки | 1990.00 |
LIKE: поиск по текстовому шаблону
LIKE используют, когда нужно найти текст не по точному совпадению, а по шаблону.
Например, нужно найти товары, название которых начинается со слова «Кофе»:
SELECT name, price
FROM products
WHERE name LIKE 'Кофе%';
Знак % означает:
любое количество любых символов.
В том числе ноль символов.
Поэтому шаблон:
'Кофе%'
найдёт:
Кофе
Кофе молотый
Кофейная станция
Кофе для котонавтов
Но не найдёт:
Большой кофе
Набор: кофе и кружка
Потому что шаблон требует, чтобы текст начинался с Кофе.
LIKE: % se estira hasta cualquier número de caracteres (incluido cero), _ ocupa exactamente una posición.%: любое количество символов
% — самый частый символ в LIKE.
Он означает:
здесь может быть любое количество символов.
Посмотри на разные шаблоны.
Начинается с текста
WHERE name LIKE 'Кофе%'
Найдёт названия, которые начинаются с Кофе.
Примеры совпадений:
Кофе
Кофе молотый
Кофейный набор
Заканчивается текстом
WHERE name LIKE '%кофе'
Найдёт названия, которые заканчиваются на кофе.
Примеры совпадений:
Большой кофе
Капсульный кофе
Набор для кофе
Содержит текст где угодно
WHERE name LIKE '%кофе%'
Найдёт названия, где кофе встречается в любой позиции.
Примеры совпадений:
кофе
Большой кофе
Набор для кофе и чая
Кофейная станция
Если сказать образно:
'Кофе%'
— текст должен начаться с «Кофе».
'%кофе'
— текст должен закончиться на «кофе».
'%кофе%'
— текст должен где-то содержать «кофе».
_: ровно один символ
Кроме %, у LIKE есть второй специальный символ:
_
Он означает:
ровно один любой символ.
Например:
WHERE code LIKE 'A_1'
Подойдут:
AB1
AX1
A71
Но не подойдут:
A1
ABCD1
AA21
Почему?
Шаблон A_1 требует:
- сначала
A - потом ровно один любой символ
- потом
1
Ещё пример:
WHERE name LIKE 'Кот_'
Подойдут строки из четырёх символов:
Коты
Котя
Кот1
Но не подойдёт:
Кот
Котик
Потому что _ требует ровно один дополнительный символ.
Разница такая:
| Шаблон | Что означает |
|---|---|
'Кот%' | Кот, а потом сколько угодно символов |
'Кот_' | Кот, а потом ровно один символ |
% разрешает любое продолжение после указанного начала.| name | price |
|---|---|
| Кофеварка капельная | 4290.00 |
Регистр: LIKE, ILIKE и LOWER
В PostgreSQL обычный LIKE чувствителен к регистру.
Это значит, что шаблон:
WHERE name LIKE 'смарт%'
может не найти строку:
Смарт-часы
Потому что с и С — разные символы.
Чтобы искать без учёта регистра, в PostgreSQL есть ILIKE:
SELECT name, price
FROM products
WHERE name ILIKE 'смарт%';
ILIKE — это регистронезависимый вариант LIKE.
Он найдёт:
смарт-часы
Смарт-часы
СМАРТ-ЧАСЫ
Но важно помнить: ILIKE — это удобство PostgreSQL, а не универсальная часть стандартного SQL.
Более переносимый подход — привести текст к одному регистру:
SELECT name, price
FROM products
WHERE LOWER(name) LIKE 'смарт%';
Здесь база сначала превращает name в нижний регистр, а потом сравнивает с шаблоном в нижнем регистре.
Можно сделать и так:
WHERE LOWER(name) LIKE LOWER('Смарт%')
Но чаще шаблон просто сразу пишут в нужном регистре:
WHERE LOWER(name) LIKE 'смарт%'
Как искать сами символы % и _
У LIKE есть проблема: % и _ — специальные символы.
% означает любое количество символов.
_ означает ровно один символ.
Но иногда нужно найти именно знак процента или подчёркивание как обычный текст.
Например, есть товары:
Скидка 50%
Корм 50 кг
QA_набор
QA-набор
Если написать:
WHERE name LIKE '50%'
это не значит «найди текст 50%».
Это значит:
найди строки, которые начинаются с
50, а дальше что угодно.
Чтобы сказать базе: «этот % — обычный символ процента», используют ESCAPE.
Например:
SELECT name
FROM products
WHERE name LIKE '%50!%%' ESCAPE '!';
Разберём шаблон:
'%50!%%'
- первый
%— любое начало строки 50— обычные символы!%— обычный знак процента, потому что!назначен escape-символом- последний
%— любое продолжение строки
Фраза:
ESCAPE '!'
говорит SQL:
если перед
%или_стоит!, считай следующий символ обычным текстом.
Так же можно искать подчёркивание:
SELECT name
FROM products
WHERE name LIKE 'QA!_%' ESCAPE '!';
Этот шаблон найдёт строки, которые начинаются с обычного текста QA_.
Без экранирования шаблон:
'QA_%'
означал бы:
QA, потом любой один символ, потом любое продолжение.
И мог бы найти лишние строки вроде:
QA-набор
QA1набор
QA набор
КВЕРИ: В трафарете
LIKEсимволы%и_— не краска, а дырки. Если хочешь найти саму дырку на чертеже, сначала скажи архиву, что это обычный знак.
Цена ведущего %
На маленькой таблице разницы почти не видно. Но на миллионах строк разные шаблоны LIKE могут работать очень по-разному.
Сравни два условия:
WHERE name LIKE 'Кофе%'
и
WHERE name LIKE '%кофе'
Первое условие знает начало строки. База может рассуждать примерно как с бумажным словарём:
Открой раздел на «Кофе» и ищи рядом.
Такой поиск по префиксу иногда можно ускорить .
А второе условие начинается с %:
'%кофе'
Это значит:
перед словом «кофе» может быть что угодно.
Совпадение может находиться где угодно в строке. Обычный индекс по столбцу name уже не помогает так же хорошо, потому что база не знает, с какого начала искать.
Поэтому запросы вида:
WHERE name LIKE '%кофе%'
часто приводят к просмотру большого количества строк: базе приходится проверять каждое название.
Простая мысль:
| Условие | Что известно базе | Обычно быстрее? |
|---|---|---|
LIKE 'Кофе%' | известен префикс | да, может помочь индекс |
LIKE '%кофе' | начало неизвестно | часто медленно |
LIKE '%кофе%' | совпадение где угодно | часто медленно |
В PostgreSQL есть технические нюансы: для ускорения префиксного LIKE иногда нужен индекс с text_pattern_ops или подходящая локаль, а для поиска по середине строки используют pg_trgm.
Но на этом этапе важно понять саму идею:
Если шаблон начинается с обычных символов, базе легче искать. Если шаблон начинается с
%, базе часто приходится перечитывать таблицу шире.
Свойство условия «уметь использовать индекс» называют . Подробно оно будет в модуле про оптимизацию.
Правило для практики
Хорошо:
WHERE name LIKE 'Кофе%'
Осторожно:
WHERE name LIKE '%кофе%'
Первый запрос ищет по известному началу строки.
Второй ищет подстроку где угодно. На большой таблице он может быть дорогим.
Это не значит, что LIKE '%кофе%' запрещён. Иногда он нужен. Но если такой поиск используется часто и по большой таблице, это уже задача для специальных или отдельного поискового механизма.
UNKNOWN в связках и ловушка NOT IN
В прошлом уроке ты уже видел, что NULL даёт третий логический результат:
UNKNOWN
Это важно и для IN.
Пока список написан руками и в нём только обычные значения, всё просто:
WHERE category IN ('Книги', 'Игрушки')
Это похоже на:
WHERE category = 'Книги'
OR category = 'Игрушки'
А вот NOT IN работает как цепочка условий через AND.
Например:
WHERE category NOT IN ('Книги', 'Игрушки')
похоже на:
WHERE category <> 'Книги'
AND category <> 'Игрушки'
И здесь появляется ловушка.
Посмотри:
SELECT 'нашлось'
WHERE 1 NOT IN (2, 3);
Этот запрос вернёт строку, потому что:
1 <> 2 AND 1 <> 3
это:
TRUE AND TRUE
Итог — TRUE.
А теперь:
SELECT 'нашлось'
WHERE 1 NOT IN (2, NULL);
Этот запрос не вернёт строку.
Почему?
NOT IN (2, NULL) разворачивается примерно так:
1 <> 2 AND 1 <> NULL
Первая часть:
1 <> 2
даёт TRUE.
Вторая часть:
1 <> NULL
даёт UNKNOWN.
Итог:
TRUE AND UNKNOWN
даёт UNKNOWN.
А WHERE пропускает только TRUE.
Поэтому строка не попадает в результат.
Самая опасная ситуация возникает не тогда, когда список написан руками. Там NULL видно глазами.
Опасность появляется с :
WHERE product_id NOT IN (
SELECT product_id
FROM archived_products
)
Если подзапрос вернёт хотя бы один NULL, результат может стать неожиданно пустым.
Пока запомни сигнал тревоги:
NOT IN+ возможныйNULL= риск пустой выдачи.
В следующих модулях, когда появятся подзапросы, ты увидишь более безопасные варианты через NOT EXISTS.
Что будет, если значение NULL
BETWEEN, IN и LIKE тоже подчиняются логике NULL.
Если значение отсутствует, обычная проверка не становится TRUE.
Например:
price BETWEEN 1000 AND 3000
Если price равен NULL, результат будет UNKNOWN.
category IN ('Книги', 'Игрушки')
Если category равен NULL, результат будет UNKNOWN.
name LIKE 'Кофе%'
Если name равен NULL, результат будет UNKNOWN.
А WHERE пропускает только TRUE.
Поэтому строки с NULL не проходят такие фильтры сами по себе.
Если их нужно включить, добавляй явное условие:
WHERE price BETWEEN 1000 AND 3000
OR price IS NULL
или:
WHERE category IN ('Книги', 'Игрушки')
OR category IS NULL
Не нужно думать, что BETWEEN, IN или LIKE как-то отдельно «понимают» отсутствующие значения. Для NULL по-прежнему нужны IS NULL и IS NOT NULL.
Частые ошибки
Ошибка 1. Забыть, что BETWEEN включает границы
WHERE price BETWEEN 1000 AND 3000
Это включает и 1000, и 3000.
Если границы не должны входить, пиши:
WHERE price > 1000
AND price < 3000
Ошибка 2. Использовать BETWEEN с датами и случайно отрезать последний день
Осторожно:
WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31'
Если created_at хранит время, часть записей за 31 января может не попасть.
Часто безопаснее:
WHERE created_at >= '2024-01-01'
AND created_at < '2024-02-01'
Ошибка 3. Писать много OR вместо IN
Можно так:
WHERE category = 'Книги'
OR category = 'Игрушки'
OR category = 'Аксессуары'
Но читаемее так:
WHERE category IN ('Книги', 'Игрушки', 'Аксессуары')
Ошибка 4. Думать, что IN ищет похожий текст
WHERE category IN ('Книги')
Не найдёт:
Электронные книги
Потому что IN проверяет точное совпадение.
Для похожего текста нужен LIKE:
WHERE category LIKE '%книги%'
Или ILIKE, если нужен поиск без учёта регистра в PostgreSQL:
WHERE category ILIKE '%книги%'
Ошибка 5. Путать % и _
LIKE 'A%'
A, потом любое количество символов.
LIKE 'A_'
A, потом ровно один символ.
Ошибка 6. Забыть экранировать % и _
Если нужно найти обычный знак %, не пиши так:
WHERE name LIKE '%50%%'
Лучше явно экранировать:
WHERE name LIKE '%50!%%' ESCAPE '!'
Ошибка 7. Ставить % в начало шаблона и ждать быстрой работы
WHERE name LIKE '%кофе%'
Такой поиск может быть нормальным на маленькой таблице, но на большой часто становится дорогим.
Interview question
Pregunta de entrevista: en cuanto a rendimiento, ¿en qué se diferencia LIKE 'абв%' de LIKE '%абв'?
Respuesta sólida: con un prefijo conocido, el SGBD puede usar un índice: la búsqueda se reduce a un rango estrecho, como en un diccionario ordenado (en PostgreSQL, para esto el índice necesita text_pattern_ops o la configuración regional C). Un % inicial hace que la condición no sea sargable: la coincidencia puede empezar en cualquier posición, el índice no ayuda y la base recorre la tabla completa comprobando el patrón en cada fila. Si a menudo necesitas buscar «en mitad de la cadena», es señal de que hace falta un índice de trigramas (pg_trgm) o una búsqueda de texto completo, no más LIKE.
price BETWEEN 1000 AND 3000?category = 'Книги' OR category = 'Игрушки'?full_name LIKE 'А%'?code LIKE 'A_'?LIKE '%кофе%' может быть медленным на большой таблице?КВЕРИ: Диапазон, список и трафарет — три быстрых способа спросить архив. Главное — помнить, где образец помогает, а где заставляет архив перечитать всё с начала до конца.
- Kotomarket showcase: three categories with INEASY
- Pacientes con Maple Ave en la direcciónEASY
- Pasajeros con correo de HotmailEASY