BETWEEN, IN und LIKE: kompakte Filter
What you'll learn
- kompakte Filter mit
BETWEEN,INundLIKE-Mustern mit%und_schreiben - Text unabhängig von Groß- und Kleinschreibung über
ILIKEoderLOWER(...)suchen und%/_überESCAPEmaskieren - erklären, warum
LIKE 'abc%'von einem Index profitieren kann, ein führendes%dagegen zu einem vollständigen Tabellendurchlauf führt
Praktische Bedingungsoperatoren
Die Restauratoren der alten Erde stellten Bücher anhand eines Musterfragments auf dem Einband wieder her. Ein Datenarchivar arbeitet ähnlich: Statt eines exakten Wertes zeigt er dem Archiv eine Vorlage — einen Bereich, eine Liste, ein Muster — und lässt das Archiv die Treffer finden.
BETWEEN a AND b— der Wert liegt im Bereich von a bis b einschließlich;IN (...)— der Wert ist in der Liste enthalten;LIKE 'Muster'— der Text passt zum Muster, wobei%für beliebig viele Zeichen steht und_für ein einzelnes Zeichen.
Diese Operatoren sparen Platz und machen die Absicht sichtbar. Statt einer langen Kette von Bedingungen schreibst du fast in Alltagssprache: Artikel aus den Kategorien Bücher oder Spielzeug, mit einem Preis von 1000 bis 3000. Für den Katalog products war das 2024 eine tägliche Arbeitsaufgabe — und jetzt ist es auch deine.

BETWEEN, die Liste IN und die Schablone LIKE — das Archiv findet die Treffer selbst.Три разных образца для поиска
У этих операторов разные роли.
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: % steht für beliebig viele Zeichen (auch null), _ belegt genau eine Position.%: любое количество символов
% — самый частый символ в 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
Frage aus dem Vorstellungsgespräch: Wodurch unterscheidet sich LIKE 'abc%' aus Performance-Sicht von LIKE '%abc'?
Starke Antwort: Bei bekanntem Präfix kann das einen Index nutzen — die Suche reduziert sich auf einen engen Bereich, wie in einem sortierten Wörterbuch (in PostgreSQL braucht der Index dafür text_pattern_ops oder die Locale C). Ein führendes % macht die Bedingung nicht sargable: Die Übereinstimmung kann an beliebiger Position beginnen, der Index ist nutzlos, die Datenbank führt einen vollständigen Durchlauf aus und prüft das Muster bei jeder Zeile. Wird die Suche „in der Stringmitte“ häufig gebraucht, ist das ein Fall für einen Trigramm-Index (pg_trgm) oder eine Volltextsuche, nicht für LIKE.
price BETWEEN 1000 AND 3000?category = 'Книги' OR category = 'Игрушки'?full_name LIKE 'А%'?code LIKE 'A_'?LIKE '%кофе%' может быть медленным на большой таблице?КВЕРИ: Диапазон, список и трафарет — три быстрых способа спросить архив. Главное — помнить, где образец помогает, а где заставляет архив перечитать всё с начала до конца.
- Kotomarket showcase: three categories with INEASY
- Patienten mit Maple Ave in der AdresseEASY
- Passagiere mit Hotmail-E-Mail-AdressenEASY