WHERE: отличаем истинные строки от ложных
Сравнения, AND и OR, шаблоны LIKE — и странное правило: NULL не равен ничему, даже самому себе.
На прошлом уроке ты уже фильтровал строки через WHERE. Теперь разберёмся, что вообще может сказать условие, потому что именно на фильтрации рождается больше всего неверных ответов. У столовой есть вторая таблица, orders, по строке на заказ: что купили, на какую сумму и оплачен ли он. В одной строке стоит paid = NULL: столовая пока не знает.
orders
+----+--------+-------+----------+
| id | item | price | paid |
+----+--------+-------+----------+
| 1 | soup | 2.50 | yes |
| 2 | salad | 2.80 | no |
| 3 | juice | 1.90 | yes |
| 4 | juice | 1.90 | NULL |
| 5 | soup | 2.50 | yes |
+----+--------+-------+----------+Таблица orders: одна строка на заказ, и одно неизвестное значение в paid.Сравнение вроде price < 2.00 или paid = 'yes' ничего не описывает — это вопрос, который ты задаёшь каждой строке отдельно. Каждая строка отвечает да, нет или не могу сказать. WHERE оставляет ровно строки с ответом «да». Поэтому один и тот же запрос завтра может вернуть другое число строк: вопрос стабилен, данные — нет.
Прочитай оба запроса к таблице orders и предскажи строки, прежде чем смотреть ответ. AND сужает: одно условие про оплату, другое про цену, и верны должны быть оба. OR расширяет: строка выживает, если истинно хотя бы одно условие, так что одна лишь дешевизна спасает дешёвые строки.
WHERE paid = 'yes' AND price < 2.00
rows kept: juice (1.90, yes)
WHERE paid = 'yes' OR price < 2.00
rows kept: soup, juice, juice, soupДва условия над одной таблицей: строки, оставленные AND, потом OR.AND связывает крепче, чем OR — в точности как умножение раньше сложения в арифметике. Если ты имеешь в виду другое, поставь скобки. WHERE a = 1 OR b = 2 AND c = 3 означает a = 1 OR (b = 2 AND c = 3), и люди ошибаются в этом постоянно — скобки ничего не стоят.
Равенство требует совпадения значения целиком. LIKE вместо этого сравнивает с шаблоном: % означает любой текст любой длины, включая пустой. Поэтому dish LIKE 'veggie%' ловит всё, что начинается с veggie, а '%soup%' — всё, что содержит soup где угодно.
menu
+----+---------------+
| id | dish |
+----+---------------+
| 1 | veggie soup |
| 2 | chicken soup |
| 3 | veggie salad |
+----+---------------+
WHERE dish LIKE 'veggie%' matches: veggie soup, veggie salad
WHERE dish LIKE '%soup%' matches: veggie soup, chicken soupТаблица menu и два шаблона LIKE по столбцу dish.А теперь самое странное правило SQL. Строка с paid = NULL исчезает из всех сравнительных запросов. Не только из paid = 'no' и не только из paid = 'yes' — из обоих сразу. Чтобы понять почему, смотри, чем NULL является на самом деле.
WHERE paid = 'yes'
rows kept: soup (1), juice (3), soup (5)
row 4: paid is NULL, dropped
NULL = NULL is not true, it is unknownПочему paid = 'yes' выбрасывает строку с NULL, и почему NULL = NULL не есть истина.NULL это не значение, а его отсутствие: столовая ещё не знает, оплачен ли заказ. Спросить «равна ли неизвестная вещь значению yes» — всё равно что спросить «болит ли у меня завтра голова»: честный ответ не «нет», а «неизвестно». SQL называет это unknown, а WHERE оставляет только истинное — поэтому строка и пропадает.
Практика
Столовой нужен список товаров, которые были оплачены и проданы за день три раза и больше. Напиши запрос к таблице sales: в ней столбцы item, count и paid, где paid это текст 'yes' или 'no'.
Подсказки
- Два условия должны выполняться одновременно, и в SQL это одно слово.
- Текст сравнивай в одинарных кавычках, числа — без: paid = 'yes' AND count >= 3.
- Столбец, который измеряет, сколько раз товар продали, называется count.
Пять строк таблицы orders проверяются условием WHERE paid = 'yes'. В одной строке стоит paid = NULL. Сколько строк вернётся и что случится со строкой NULL?
Подсказки
- Про каждую из пяти строк спроси: это условие истинно, ложно или неизвестно?
- Строка остаётся, только когда условие WHERE в точности истинно; неизвестное выбрасывается так же, как ложное.
Соедини каждый шаблон с тем, что он находит в столбце dish.
Подсказки
- Знак % означает любой текст любой длины: до, после или с обеих сторон.
- Смотри, где прижат шаблон: 'A%' должно начинаться с A, '%A' — заканчиваться на A.