В конфигурациях на платформе 1С:Предприятие часто встречается структура, когда один справочник подчинен другому. Например, справочник Сертификаты подчинен справочнику Номенклатура. В этой схеме подчиненный справочник содержит стандартный реквизит Владелец, ссылающийся на родительскую позицию номенклатуры. Однако у самого справочника Номенклатура нет прямого реквизита, который хранил бы список или ссылку на сертификаты.
Рассмотрим, как правильно составить запрос на языке 1С, чтобы определить, у каких позиций номенклатуры отсутствуют подчиненные сертификаты.
Основным и наиболее эффективным способом решения данной задачи является использование ЛЕВОГО СОЕДИНЕНИЯ двух таблиц справочников с последующей фильтрацией по условию ЕСТЬ NULL.
При левом соединении в результат попадают абсолютно все записи из левой таблицы (в нашем случае — Справочник.Номенклатура). Если для какой-то номенклатуры в правой таблице (Справочник.Сертификаты) не находится подчиненной записи по условию Номенклатура.Ссылка = Сертификаты.Владелец, то поля правой таблицы для этой строки заполняются значением NULL.
Разберем пример запроса:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Сертификаты КАК Сертификаты
ПО Номенклатура.Ссылка = Сертификаты.Владелец
ГДЕ
Сертификаты.Ссылка ЕСТЬ NULL
В этом запросе мы выбираем все элементы номенклатуры, соединяем их с сертификатами по реквизиту Владелец и оставляем только те строки, где ссылка на сертификат равна NULL. Это и означает, что у номенклатуры нет ни одного подчиненного сертификата.
Проверить отсутствие подчиненных элементов можно и без соединения таблиц на верхнем уровне, используя условие НЕ В с подзапросом.
Посмотрим на пример реализации такого подхода:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
НЕ Номенклатура.Ссылка В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
Сертификаты.Владелец
ИЗ
Справочник.Сертификаты КАК Сертификаты)
Этот вариант удобен, если в подзапросе требуется наложить сложную фильтрацию на подчиненную таблицу (например, отсечь сертификаты со специальными статусами или дополнительными условиями).
При применении решения в реальных конфигурациях необходимо учитывать особенности хранения данных в 1С:
Номенклатура иерархический, в результат запроса попадут и папки (группы). Чтобы выбирать только элементы (товары и услуги), необходимо добавить фильтр Номенклатура.ЭтоГруппа = ЛОЖЬ.ПометкаУдаления = Истина), платформа все еще хранит его в базе. Чтобы не учитывать удаленные сертификаты, условие их отсутствия или недействительности накладывают непосредственно в секции соединения.Проанализируем усовершенствованный вариант запроса с учетом этих факторов:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Сертификаты КАК Сертификаты
ПО Номенклатура.Ссылка = Сертификаты.Владелец
И (Сертификаты.ПометкаУдаления = ЛОЖЬ)
ГДЕ
Номенклатура.ЭтоГруппа = ЛОЖЬ
И Номенклатура.ПометкаУдаления = ЛОЖЬ
И Сертификаты.Ссылка ЕСТЬ NULL
Важное правило: дополнительное условие Сертификаты.ПометкаУдаления = ЛОЖЬ следует размещать именно в секции ПО (в условиях соединения). Если написать его в секции ГДЕ, левое соединение фактически превратится во внутреннее, и элементы без сертификатов вообще не попадут в выборку.
Если у подчиненного справочника Сертификаты в конфигураторе задан составной тип реквизита Владелец (например, владельцем может быть как Номенклатура, так и ВидНоменклатуры), требуется подсказать СУБД точный тип связи. Это исключит лишние соединения с таблицами других объектов.
Для этого в условии соединения явно ограничивают тип с помощью функции ВЫРАЗИТЬ() или оператора ССЫЛКА:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Сертификаты КАК Сертификаты
ПО Номенклатура.Ссылка = ВЫРАЗИТЬ(Сертификаты.Владелец КАК Справочник.Номенклатура)
ГДЕ
Сертификаты.Ссылка ЕСТЬ NULL
Если задача заключается в том, чтобы быстро проверить наличие сертификата у конкретной номенклатуры при выполнении алгоритма в коде (например, перед проведением документа), делать выборку по всей базе не нужно. Достаточно запросить первую попавшуюся запись подчиненного справочника для данной ссылки и использовать метод Пустой():
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Сертификаты.Ссылка КАК Ссылка
|ИЗ
| Справочник.Сертификаты КАК Сертификаты
|ГДЕ
| Сертификаты.Владелец = &Номенклатура
| И НЕ Сертификаты.ПометкаУдаления";
Запрос.УстановитьПараметр("Номенклатура", СсылкаНаНоменклатуру);
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Сообщить("У выбранной номенклатуры отсутствуют действующие сертификаты!");
КонецЕсли;
Поскольку стандартный реквизит Владелец в платформах 1С индексируется автоматически, данный запрос выполняется мгновенно даже на очень больших объемах данных.