бесплатно без регистрации урок 3 из 18

WHERE: отбор строк

Уметь отобрать нужные строки условием, правильно соединять несколько условий скобками и узнавать три ловушки, из-за которых отбор молча возвращает не то.

17 мин чтения · 4 главы ·4 задания ·домашняя работа

Условие: сравнение со значением

До сих пор запрос возвращал всю таблицу целиком. Отбирает строки часть WHERE — она пишется после FROM и содержит условие.

SELECT name, city
FROM customers
WHERE city = 'Москва'

Четыре строки вместо двенадцати:

namecity
Анна СмирноваМосква
Глеб ОрловМосква
Егор СоколовМосква
Лев НикитинМосква
Столько покупателей из Москвы в базе; остальные восемь строк условие не пропустило.

Устроено это просто: база берёт строку, подставляет её значения в условие и смотрит, что получилось. Условие проверяется для каждой строки отдельно и ничего не знает о соседних. Подошла — строка в ответе, не подошла — её нет.

таблица customers12 строк5 колонокWHERE city = 'Москва'4 строки5 колонокстрока проходит проверку целиком: её не обрезают, а пропускают или нетколонок столько же: их число решает SELECT, а не WHEREпорядок оставшихся строк по-прежнему не определён
Условие проверяется для каждой строки отдельно: подошла — осталась, нет — исчезла.

Обратите внимание: строка проходит проверку целиком. Условие написано про город, но в ответе остаются все колонки строки — и имя, и возраст, и дата. WHERE не обрезает строку, он её пропускает или не пропускает.

Сравнений шесть, и все они привычные:

знакчто значитпример
=равноcity = 'Москва'
<>не равноstatus <> 'cancelled'
>большеprice > 30000
<меньшеage < 30
>=больше или равноstock >= 10
<=меньше или равноrating <= 3

Главная мелочь, на которой спотыкаются в первый день: текст пишется в одинарных кавычках, число — без них. city = 'Москва' и price > 30000. Число в кавычках база обычно поймёт, но это везение, а не правило; текст без кавычек она прочитает как имя колонки и честно скажет, что такой колонки нет.

Сравнивать можно не только с числом или текстом, но и с другой колонкой той же строки: WHERE price < stock * 100 — вполне законное условие. И выражение в условии тоже разрешено: WHERE price * 0.9 > 30000. Правила арифметики те же, что в списке колонок, включая целочисленное деление.

И маленькая, но важная деталь: WHERE не меняет данные. Строка, не прошедшая условие, никуда не девается — она просто не попала в этот ответ. Слово «отбор» иногда читается как «отсев», и у новичков возникает опаска: не испорчу ли я таблицу. Не испортите: SELECT только читает.

Полезно и обратное чтение условия. Запрос без WHERE — это запрос с условием «всегда истина»: подходят все строки. Поэтому WHERE 1 = 1 — законный запрос, возвращающий всё, и в сгенерированном коде такое встречается: программе проще дописывать AND … к заведомо истинному условию, чем решать, нужно ли слово WHERE вообще.

И самое частое условие на свете — отбор по ключу: WHERE id = 5. Такой запрос вернёт ровно одну строку или ни одной, потому что ключ уникален. С него начинается почти всякий разбор: «покажи мне этот заказ целиком», «что вообще записано в этой строке». Запомните эту форму — в следующих уроках она будет встречаться постоянно, уже как часть запросов побольше.

AND, OR и скобки

Условий редко бывает одно. Соединяют их двумя словами: AND — «и то, и другое», OR — «хотя бы одно».

SELECT name, age
FROM customers
WHERE city = 'Москва' AND age > 30

Из четырёх москвичей останутся двое: Глеб (41) и Лев (52). Каждое добавленное через AND условие сужает результат, каждое добавленное через ORрасширяет. Это полезно помнить, когда строк в ответе неожиданно много или неожиданно мало: посмотрите, каким словом соединены условия.

А теперь главное место урока. AND связывает сильнее, чем OR — ровно так же, как умножение сильнее сложения.

city = 'Москва' OR city = 'Казань'AND age > 30без скобокМосква · или · (Казань и 30+)со скобками(Москва или Казань) · и · 30+слева шесть строк, справа две — а запрос отличается парой скобокпоэтому скобки ставят всегда, когда в условии есть и AND, и OR
Без скобок «А ИЛИ Б И В» читается как «А ИЛИ (Б И В)» — как сложение и умножение.
-- без скобок: Москва — или Казань и старше тридцати
WHERE city = 'Москва' OR city = 'Казань' AND age > 30

-- со скобками: (Москва или Казань) — и старше тридцати
WHERE (city = 'Москва' OR city = 'Казань') AND age > 30

Первый запрос вернёт шесть строк: всех четверых москвичей независимо от возраста плюс тех казанцев, кому за тридцать (таких нет вовсе). Второй — двоих. Разница в паре скобок, и никакой ошибки ни в одном из вариантов нет: база выполнит оба, а какой из них вы имели в виду, знаете только вы.

Третье слово — NOT, «не»: WHERE NOT city = 'Москва' оставит всех, кроме москвичей. Пишут его нечасто, потому что почти всегда есть способ короче: то же самое говорит city <> 'Москва'. Полезен NOT там, где отрицается не сравнение, а целое условие в скобках.

И ещё одно, что стоит знать про NOT: он сильнее и AND, и OR. NOT a AND b — это «(не а) и б», а не «не (а и б)». Список старшинства получается короткий: сначала NOT, потом AND, потом OR.

Скобок в условии может быть сколько угодно, и лишние базе не мешают. Единственное, что они делают, — определяют порядок; всё остальное решает читатель. Условие из двух строк со скобками читается, условие из одной строки без них — нет, и через полгода его открывать будете вы же.

Где стоит WHERE и что в нём можно

WHERE стоит в запросе на своём месте — после FROM и до всего остального:

SELECT name, city
FROM customers
WHERE age > 30

Порядок частей жёсткий: WHERE перед FROM база не примет и скажет об этом при разборе. А выполняется он, как мы помним из первого урока, вторым — сразу после FROM и раньше SELECT.

частькогда выполняетсячто из этого следует
FROMпервойесть все строки таблицы
WHEREвторойлишние строки отсеяны
SELECTпочти последнимколонки и псевдонимы появляются только тут
ORDER BYпосле SELECTпсевдонимом уже можно пользоваться

Отсюда два правила, которые выглядят произволом, пока не вспомнишь порядок.

Псевдоним из SELECT в WHERE использовать нельзя. На момент проверки условия его ещё не существует:

-- так нельзя: имени skidka ещё нет
SELECT price * 0.9 AS skidka FROM products WHERE skidka > 30000

-- так можно: выражение повторяется целиком
SELECT price * 0.9 AS skidka FROM products WHERE price * 0.9 > 30000

Итог по группе в WHERE использовать нельзя. «Где покупателей больше пяти», «где сумма заказов выше средней» — всё это про группы строк, а групп на момент WHERE ещё нет: он работает с одиночной строкой и ничего не знает о соседних. Для отбора итогов есть отдельная часть, HAVING, и ей посвящён отдельный урок.

Что в WHERE можно: сравнения, выражения с арифметикой, склейку текста, обращение к любой колонке таблицы — в том числе к той, которой нет в списке SELECT. Последнее удивляет: отбирать по колонке, которую не показываешь, совершенно нормально. SELECT name FROM customers WHERE age > 30 — законный и очень частый запрос.

Из порядка выполнения следует и практический совет: отбор дешевле всего остального. WHERE выполняется сразу после чтения строк, до группировки, до вычисления колонок и до сортировки, — и всё, что он отсёк, дальше не обрабатывается вовсе. Запрос, отбирающий тысячу строк из миллиона, сортирует тысячу, а не миллион.

Отсюда же и частая ошибка новичка: посчитать всё, а потом отобрать нужное «глазами» в готовом ответе. Так работает выгрузка, но не запрос — и на большой таблице разница между «отобрать и посчитать» и «посчитать и отобрать» измеряется не процентами, а порядками.

И то, о чём стоит знать заранее, хотя до соответствующего урока ещё далеко. WHERE есть не только у SELECT. Команды, которые меняют данные, — UPDATE и DELETE — устроены так же: условие решает, какие строки тронуть. Разница в цене ошибки.

Когда отбор молча врёт

Отбор — самое простое место в языке и самое частое место ошибок. Не потому, что он сложный, а потому, что неверное условие не выглядит неверным: запрос выполняется, строки приходят, отчёт собирается. Три ловушки, в которые попадают все.

Первая: строка с пустой клеткой выпадает из любого сравнения. У одной покупательницы возраст не указан. Запрос «старше тридцати» вернёт шесть строк, запрос «тридцать и младше» — пять. Шесть плюс пять — одиннадцать, а покупателей двенадцать.

age > 306 строкage <= 305 строкage пусто1 строка6 + 5 = 11, а покупателей 12 — одного нет ни там, ни тампустота — не значение, и сравнивать её не с чемименно так в отчёте по двум частям теряется целоекак это лечится — в следующем уроке
Двенадцать покупателей, но «старше тридцати» и «тридцать и младше» вместе дают одиннадцать.

Пустота — не значение, и сравнить её не с чем: результат сравнения оказывается не «истина» и не «ложь», а тоже пустота, и строка не проходит. Хуже всего это в паре с отрицанием: «все, кто не из Москвы» тихо теряет тех, у кого город вообще не заполнен. Лечится это отдельной проверкой на пустоту, и ей посвящён следующий урок.

Вторая: текст сравнивается буквально. 'Москва' и 'москва' — в большинстве баз разные значения, а 'Москва ' с пробелом на конце не равно 'Москва'. Данные, собранные руками или пришедшие из разных систем, полны таких расхождений, и условие честно их различает.

Третья: сравнение не с тем типом. Число, взятое в кавычки, превращается в текст, а текст сравнивается не как число: '100' > '99' — ложь, потому что по алфавиту единица идёт раньше девятки. С датами то же самое: они хранятся текстом в формате «год-месяц-день» именно ради того, чтобы порядок текста совпадал с порядком времени.

Отдельная разновидность той же ошибки — сравнение с пустой строкой вместо пустоты. WHERE comment = '' и «комментарий не заполнен» — разные вещи: пустая строка это значение, а незаполненная клетка — отсутствие значения, и первое условие вторых не найдёт.

Общее у всех трёх ловушек одно — и это же делает их опасными: запрос выполнился без ошибки. Ни одна из них не даёт красной строчки, не останавливает работу и ничем себя не выдаёт — результат просто оказывается не тем, о котором вас спрашивали. Поэтому проверять отбор приходится не по факту «сработало», а по числу строк. Красная строчка об ошибке — это подарок: она означает, что запрос до данных не дошёл и ничего испортить не успел. Тихий неверный результат дороже любой ошибки разбора.

Задания урока

Каждое проверяется сразу: ответ сверяется с эталоном, и на неверный вариант приходит разбор.

  1. Выведите названия и цены товаров дороже 30 000. Колонки: title, price.средне
  2. Условие «city = 'Москва' OR city = 'Казань' AND age > 30» отберёт жителей Москвы и Казани старше тридцати.сложно
  3. Выведите имена и возраст покупателей из Москвы или Казани, которым больше тридцати. Колонки: name, age.сложно
  4. Что нельзя написать в WHERE?средне
Пройти урок с проверкой заданий Курс «SQL» открыт и бесплатен, прогресс сохраняется.
Открыть курс