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