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