В практике разработки на платформе 1С:Предприятие часто возникает задача получения «наследуемого» значения. Типичный пример: у номенклатуры есть реквизит МестоХранения. Если он не заполнен у самого элемента, система должна проверить его у непосредственного родителя, затем у родителя родителя и так далее до самого верха иерархии. Разберем, как реализовать это максимально эффективно с помощью языка запросов.
Проанализируем ситуацию, когда мы заранее не знаем, на каком уровне иерархии заполнено значение, но хотим выбрать «ближайшее» к элементу. Для этого мы можем использовать механизм объединения (ОБЪЕДИНИТЬ ВСЕ) с назначением весовых коэффициентов (приоритетов) каждому уровню.
Рассмотрим по шагам логику построения такого запроса:
Посмотрим на пример реализации такого запроса:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
Номенклатура.МестоХранения КАК МестоХранения,
1 КАК Приоритет
ПОМЕСТИТЬ ВТ_ВсеУровни
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка В (&СписокНоменклатуры)
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Номенклатура.Ссылка,
Номенклатура.Родитель.МестоХранения,
2
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка В (&СписокНоменклатуры)
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Номенклатура.Ссылка,
Номенклатура.Родитель.Родитель.МестоХранения,
3
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка В (&СписокНоменклатуры)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Т2.Номенклатура,
Т3.МестоХранения
ИЗ
(ВЫБРАТЬ
Т1.Номенклатура КАК Номенклатура,
МИНИМУМ(Т1.Приоритет) КАК Приоритет
ИЗ
ВТ_ВсеУровни КАК Т1
ГДЕ
Т1.МестоХранения <> ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка)
И Т1.МестоХранения ЕСТЬ НЕ NULL
СГРУППИРОВАТЬ ПО
Т1.Номенклатура) КАК Т2
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ВсеУровни КАК Т3
ПО Т2.Номенклатура = Т3.Номенклатура
И Т2.Приоритет = Т3.Приоритет
Важный нюанс: при группировке по МИНИМУМ(Приоритет) обязательно нужно отсекать пустые значения (NULL или ПустаяСсылка) еще на этапе формирования выборки для группировки. В противном случае запрос найдет минимальный приоритет (например, 1 — сам элемент), но значение там будет пустым, и вы не перейдете к родителю.
Выясним причину, почему этот метод часто считается более предпочтительным. Использование ЛЕВОЕ СОЕДИНЕНИЕ позволяет получить результат в один проход без использования тяжелых объединений UNION, которые СУБД иногда сложно оптимизировать.
Проанализируем структуру запроса с использованием каскадного поиска:
ВЫБРАТЬ
Ном.Ссылка КАК Номенклатура,
ЕСТЬNULL(Ном.МестоХранения,
ЕСТЬNULL(Род1.МестоХранения,
ЕСТЬNULL(Род2.МестоХранения, ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка)))) КАК МестоХраненияИтого
ИЗ
Справочник.Номенклатура КАК Ном
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Род1
ПО Ном.Родитель = Род1.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Род2
ПО Род1.Родитель = Род2.Ссылка
ГДЕ
Ном.Ссылка В (&СписокНоменклатуры)
Однако здесь есть «подводный камень» платформы 1С. В отличие от стандартного SQL, в 1С реквизиты ссылочного типа почти никогда не бывают NULL, если запись существует. Они содержат Пустую ссылку. Поэтому конструкция ЕСТЬNULL сработает только в том случае, если у элемента вообще нет родителя (строка соединения будет NULL). Если же родитель есть, но у него не заполнено место хранения, ЕСТЬNULL вернет пустую ссылку этого родителя и прекратит поиск.
Чтобы обойти это ограничение, в запросе следует использовать конструкцию ВЫБОР:
ВЫБРАТЬ
Ном.Ссылка КАК Номенклатура,
ВЫБОР
КОГДА Ном.МестоХранения <> ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка) ТОГДА Ном.МестоХранения
КОГДА Род1.МестоХранения <> ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка) ТОГДА Род1.МестоХранения
КОГДА Род2.МестоХранения <> ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка) ТОГДА Род2.МестоХранения
ИНАЧЕ ЗНАЧЕНИЕ(Справочник.МестаХранения.ПустаяСсылка)
КОНЕЦ КАК МестоХраненияИтого
...
При работе с большими справочниками (более 100 000 элементов) необходимо учитывать следующие рекомендации:
Родитель в 1С индексируется по умолчанию, что обеспечивает высокую скорость соединений. Однако, если вы строите сложные условия по дополнительным реквизитам, убедитесь, что они также проиндексированы.Номенклатура сразу.Рассмотрим, какой метод выбрать в зависимости от условий задачи:
Метод с приоритетами (UNION):
ВТ_ВсеУровни операция группировки и последующего обратного соединения может быть ресурсоемкой.Метод с соединениями (JOIN):
ВЫБОР КОГДА.В завершение отметим, что если задача «наследования» свойств стоит очень остро и уровней иерархии много, стоит рассмотреть архитектурное решение: использование Регистров сведений или механизма Дополнительных реквизитов (БСП), в которых логика поиска по иерархии часто уже реализована на программном уровне или оптимизирована платформой.