При разработке отчетов в 1С часто возникает ситуация, когда необходимо получить данные из двух таблиц, сохранив при этом все записи из основной (левой) таблицы, даже если соответствующих им данных в правой таблице не нашлось. Казалось бы, ЛЕВОЕ СОЕДИНЕНИЕ должно идеально решать эту задачу, возвращая NULL для отсутствующих полей. Однако на практике программисты часто сталкиваются с тем, что записи, которые должны содержать NULL, бесследно исчезают из выборки. Давайте разберемся, почему это происходит и как правильно настраивать такие запросы.
Главная ошибка, с которой сталкиваются при написании запросов, заключается в неверном использовании секции ГДЕ (WHERE). Когда мы накладываем условия фильтрации на поля правой таблицы в секции ГДЕ, мы фактически принуждаем систему отсекать все записи, где связь не была установлена.
Рассмотрим логику работы системы: при выполнении ЛЕВОГО СОЕДИНЕНИЯ, если запись в правой таблице не найдена, все её поля принимают значение NULL. Если затем в секции ГДЕ мы указываем условие, например, ТаблицаПравая.Партнер = &Партнер, система проверяет: равно ли NULL значение нашего параметра? Конечно, нет. В результате строка, которая должна была остаться в отчете с пустыми полями справа, просто удаляется из результата запроса.
Таким образом, использование условий на поля правой таблицы в секции ГДЕ делает ЛЕВОЕ СОЕДИНЕНИЕ фактически ВНУТРЕННИМ СОЕДИНЕНИЕМ (INNER JOIN), что и приводит к потере данных.
Наиболее простым и эффективным способом решения проблемы является перенос условий фильтрации непосредственно в секцию ПО (ON) соединения. Когда мы задаем условия именно в момент связки таблиц, система учитывает их при формировании выборки, не отсекая записи с NULL.
Давайте посмотрим, как изменить запрос, чтобы фильтрация работала корректно:
ВЫБРАТЬ
ЛеваяТаблица.Номенклатура,
ПраваяТаблица.Количество
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец) КАК ЛеваяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец) КАК ПраваяТаблица
ПО
ЛеваяТаблица.Номенклатура = ПраваяТаблица.Номенклатура
И ПраваяТаблица.Партнер = &Партнер
В этом примере условие ПраваяТаблица.Партнер = &Партнер находится внутри секции ПО. Теперь, если для номенклатуры из левой таблицы не найдется записи с указанным партнером в правой, запрос все равно выведет эту номенклатуру, но поля из правой таблицы будут заполнены значениями NULL.
Если логика фильтрации сложная или вам нужно предварительно обработать правую таблицу перед соединением, используйте вложенные запросы. Это делает код более читаемым и помогает избежать ошибок.
Рассмотрим пример того, как это реализовать:
ВЫБРАТЬ
ЛеваяТаблица.Номенклатура,
ОтфильтрованнаяПравая.Количество
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец) КАК ЛеваяТаблица
ЛЕВОЕ СОЕДИНЕНИЕ
(ВЫБРАТЬ
Таблица.Номенклатура КАК Номенклатура,
Таблица.Количество КАК Количество
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец) КАК Таблица
ГДЕ
Таблица.Партнер = &Партнер) КАК ОтфильтрованнаяПравая
ПО
ЛеваяТаблица.Номенклатура = ОтфильтрованнаяПравая.Номенклатура
При таком подходе фильтрация происходит еще до того, как таблицы «встретятся» друг с другом. Это дает четкое понимание того, какие именно данные мы пытаемся присоединить.
Если вы все еще не получаете ожидаемого результата, воспользуйтесь проверенной методикой отладки:
Обороты, учитывают параметры периода. Если в параметрах указан узкий диапазон, данные могут отсутствовать в принципе, и никакое соединение не поможет их «найти».Работая последовательно и перенося фильтры из ГДЕ в секцию ПО, вы всегда сможете добиться корректного отображения NULL и построить отчет так, как задумано.