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