SkillGridИзучай computer science на практике

WHERE: отличаем истинные строки от ложных

Сравнения, AND и OR, шаблоны LIKE — и странное правило: NULL не равен ничему, даже самому себе.

13 мин · 42 XP

На прошлом уроке ты уже фильтровал строки через 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 оставляет только истинное — поэтому строка и пропадает.

Практика

  1. Столовой нужен список товаров, которые были оплачены и проданы за день три раза и больше. Напиши запрос к таблице sales: в ней столбцы item, count и paid, где paid это текст 'yes' или 'no'.

    Подсказки
    • Два условия должны выполняться одновременно, и в SQL это одно слово.
    • Текст сравнивай в одинарных кавычках, числа — без: paid = 'yes' AND count >= 3.
    • Столбец, который измеряет, сколько раз товар продали, называется count.
  2. Пять строк таблицы orders проверяются условием WHERE paid = 'yes'. В одной строке стоит paid = NULL. Сколько строк вернётся и что случится со строкой NULL?

    Подсказки
    • Про каждую из пяти строк спроси: это условие истинно, ложно или неизвестно?
    • Строка остаётся, только когда условие WHERE в точности истинно; неизвестное выбрасывается так же, как ложное.
  3. Соедини каждый шаблон с тем, что он находит в столбце dish.

    Подсказки
    • Знак % означает любой текст любой длины: до, после или с обеих сторон.
    • Смотри, где прижат шаблон: 'A%' должно начинаться с A, '%A' — заканчиваться на A.