В системе нужно выбрать поставщиков, поставивших каждый товар из заданного набора: какой механизм реляционной алгебры выражает такое условие?
Такое условие выражает операция реляционного деления. Она возвращает те значения делимого отношения, для которых существует связанная строка с каждым значением из делителя.
Для поставщиков это означает: поставщик попадает в результат только тогда, когда для каждого товара из требуемого набора существует запись о его поставке.
Реляционная алгебра была создана как формальный аппарат для работы с отношениями без описания пошагового способа доступа к данным. Она позволила отделить логическое содержание запроса от физического хранения и навигации по структурам данных.
Большинство операций реляционной алгебры удобно описывает существование, выбор и сочетание строк. Однако запросы вида «найти объекты, связанные со всеми объектами другого множества» требуют выражения универсального условия. Для такого класса задач используется деление — непосредственно или через эквивалентные комбинации других операций.
Пусть есть отношение поставок с парами «поставщик — товар» и отдельное отношение требуемых товаров. Нужно найти поставщиков, которые поставили каждый товар из требуемого набора, а не хотя бы один из них.
Ошибочная проверка наличия хотя бы одной поставки даст слишком широкий результат. Другой риск — неучёт пустого требуемого множества: с точки зрения формальной логики условие «для всех товаров из пустого множества поставка существует» считается истинным, поэтому результатом могут стать все поставщики.
Пусть отношение поставок содержит атрибуты поставщик и товар, а отношение требований — только атрибут товар. Результат деления поставок на требования содержит поставщик, если для каждого товара из отношения требований существует соответствующая пара в отношении поставок.
Формально, для поставщика s условие можно записать так: не существует требуемого товара p, для которого отсутствует пара (s, p) в отношении поставок. Это эквивалентность между универсальным условием «для каждого» и двойным отрицанием «не существует нарушителя».
В SQL тот же механизм обычно выражают вложенными проверками отсутствия:
Внутренний запрос ищет поставку конкретного требуемого товара. Средний уровень ищет хотя бы один требуемый товар без такой поставки, а внешний NOT EXISTS исключает поставщика, у которого найден подобный пропуск.
В классической реляционной алгебре отношения являются множествами, поэтому дубликаты не меняют результат деления. SQL обычно работает с мультимножествами, но конструкция на EXISTS проверяет существование и потому не умножает строки из-за повторных поставок. Обычно идентификаторы товаров должны быть однозначными и не содержать NULL; иначе бизнес-смысл «каждый товар» нужно определить отдельно.
Интернет-магазин формирует список поставщиков, способных полностью закрыть заказ из нескольких товаров. В таблице поставок могут быть повторные строки из-за нескольких партий, поэтому проверка должна отвечать на вопрос о наличии, а не подсчитывать строки.
Рассматривались три варианта. Подсчёт поставленных товаров проще читать, но требует аккуратно устранять дубликаты и отдельно обрабатывать отсутствующие товары. Соединение с последующей группировкой может быть эффективным, однако легко получить неверный результат при повторных строках или несогласованном наборе требований. Двойной NOT EXISTS напрямую отражает условие «нет ни одного недостающего товара» и не зависит от количества повторных поставок.
Выбран вариант с NOT EXISTS. Для непустого набора требований он корректно возвращает только поставщиков, закрывающих весь набор. Дополнительно система явно запрещает пустой набор требований на уровне бизнес-логики, чтобы формально правильное свойство вакуумной истины не превратилось в неожиданный результат для пользователей.
Результат деления будет содержать всех поставщиков из делимого отношения. Условие «поставщик поставил каждый товар из пустого множества» не имеет контрпримера и поэтому считается истинным. В прикладной системе это часто нежелательно, поэтому пустой набор следует заранее запретить или обработать отдельно.
Проверка количества может быть эквивалентна делению только при дополнительных условиях: исключены дубликаты, задан точный набор требований, корректно обработаны отсутствующие строки и не нарушена связь между группировкой и поставщиком. Деление через NOT EXISTS проверяет отсутствие конкретного недостающего элемента и поэтому непосредственно соответствует смыслу универсального условия.
NULL в идентификаторе товара меняет рассуждение?NULL не является обычным значением и не сравнивается с другим NULL как равное значение. При сравнении в SQL может возникнуть UNKNOWN, поэтому логика поиска отсутствующей пары перестаёт совпадать с простой логикой множеств. Практически идентификаторы товаров и ключи отношений обычно объявляют NOT NULL; если NULL допускается, его семантику нужно определить явно, а не полагаться на реляционное деление как на чисто множественную операцию.