Сначала определите смысл одной строки
Пусть в таблице клиентов есть А, Б и В. У клиента А два заказа на 50 и 150 условных единиц, у Б заказов нет, у В один заказ на 200. У каждого заказа есть непустой уникальный идентификатор. Левое соединение клиентов с заказами по клиенту даст две строки для А, одну для В и строку Б с NULL в полях заказа. Поэтому число строк результата не равно автоматически числу клиентов. Перед подсчётом нужно понять, что представляет строка: клиента, заказ или сочетание данных из двух таблиц.
ON выбирает совпадения, WHERE фильтрует результат
Если условие сумма заказа больше ста добавить в ON вместе со связью по клиенту, справа будут подобраны только подходящие заказы. При этом Б останется в результате без совпадения. Получатся А с заказом на 150, Б с пустыми полями заказа и В с заказом на 200. Если сначала соединить по клиенту, а затем потребовать сумму больше ста в WHERE, строка Б не пройдёт фильтр. Сравнение с NULL не становится истинным; в обычной трёхзначной логике оно даёт UNKNOWN. Поэтому наличие LEFT JOIN в тексте запроса не гарантирует сохранение всех клиентов после остальных условий.
Подсчёт строк и заказов отвечает на разные вопросы
После группировки по клиенту COUNT со звёздочкой считает строки группы, включая строку без совпавшего заказа. Для Б такой подсчёт даст один. COUNT по идентификатору заказа учитывает непустые значения и в этом примере даст ноль. Важно выбрать столбец, который действительно обозначает существующий заказ и не содержит NULL в настоящих заказах. Иначе подсчёт может потерять реальные записи. Также не стоит использовать DISTINCT только для маскировки неожиданного размножения строк. Сначала проверьте связи и определение результата, а затем выбирайте нужную агрегацию.
Попробуйте на практике
Разберите учебные таблицы клиентов и заказов на бумаге. Исполнять запросы в реальной базе не требуется.
- Запишите клиентов А, Б и В и три заказа с отдельными идентификаторами: два у А на 50 и 150, один у В на 200. У Б отметьте отсутствие заказа.
- Постройте результат левого соединения по клиенту и явно обозначьте NULL в полях заказа для Б. Посчитайте строки и объясните, почему их четыре.
- Сравните два варианта условия больше ста: в ON и в WHERE. Запишите, в каком варианте сохраняется Б и почему сравнение с NULL не проходит фильтр.
- Для варианта, сохраняющего всех клиентов, сравните подсчёт строк и непустых идентификаторов заказов. Проверьте отдельно ожидаемый ноль у Б, не заменяя отсутствие заказа вымышленной записью.
Как проверить результат. Исходное соединение содержит четыре строки. При фильтре в ON сохраняются три клиента, при фильтре в WHERE Б исчезает. Подсчёт непустых идентификаторов показывает у Б ноль заказов.
Частые вопросы
NULL означает нулевую сумму?
Нет. Это отсутствие известного значения в данном поле, а не число ноль. Подмена меняет смысл данных и сравнений.
Любое условие можно свободно перенести между ON и WHERE?
Для внешних соединений перенос может изменить результат. Нужно проверять, ограничиваются совпадения или отбираются уже полученные строки.
Почему COUNT со звёздочкой даёт один клиенту без заказов?
Левое соединение сохранило для него строку с пустыми полями справа. Подсчёт строк учитывает её, а подсчёт непустого идентификатора заказа нет.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.