При построении отчетов в 1С:Предприятие на базе Системы Компоновки Данных (СКД) часто возникают ситуации, когда итоговый результат не соответствует ожиданиям. Одна из таких распространенных проблем — когда отчет вместо всех желаемых записей (например, "Запросов") выводит только часть из них, а именно те, по которым существуют связанные данные (например, "Взаимодействия"). Рассмотрим эту проблему и способы ее решения, чтобы ваш отчет отображал все необходимые данные.
Давайте проанализируем ситуацию, с которой столкнулся автор. Изначально задача состояла в том, чтобы вывести все "Запросы", поступившие с сайта, а также связанные с ними "Взаимодействия" (такие как телефонные звонки или ответы по почте). Проблема заключалась в том, что отчет показывал только те "Запросы", по которым были "Взаимодействия", полностью игнорируя "Запросы" без них.
Код запроса автора выглядел следующим образом:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ВложенныйЗапрос.Взаимодействие,
ВЫБОР КОГДА ВложенныйЗапрос.Сделка.СвязаннаяСделка = ЗНАЧЕНИЕ(Справочник.СделкиСКлиентами.ПустаяСсылка) ТОГДА ВложенныйЗапрос.Сделка ИНАЧЕ ВложенныйЗапрос.Сделка.СвязаннаяСделка КОНЕЦ КАК Сделка,
ВложенныйЗапрос.Выполнено,
ВложенныйЗапрос.ДатаВыполнения,
ВложенныйЗапрос.Дата
ПОМЕСТИТЬ ВТ_ВзаимодействияСделки
ИЗ
(ВЫБРАТЬ
Взаимодействия.Ссылка КАК Взаимодействие,
ЕСТЬNULL(ВЫРАЗИТЬ(ПредметыПапкиВзаимодействий.Предмет КАК Справочник.СделкиСКлиентами), ЗНАЧЕНИЕ(Справочник.СделкиСКлиентами.ПустаяСсылка)) КАК Сделка,
ЕСТЬNULL(ПредметыПапкиВзаимодействий.Рассмотрено, ЛОЖЬ) КАК Выполнено,
ЕСТЬNULL(ПредметыПапкиВзаимодействий.РассмотретьПосле, ДАТАВРЕМЯ(1, 1, 1)) КАК ДатаВыполнения,
Запросы.ДатаСоздания КАК Дата
ИЗ
ЖурналДокументов.Взаимодействия КАК Взаимодействия
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПредметыПапкиВзаимодействий КАК ПредметыПапкиВзаимодействий
ПО Взаимодействия.Ссылка = ПредметыПапкиВзаимодействий.Взаимодействие
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Запросы КАК Запросы
ПО Взаимодействия.ВзаимодействиеОснование = Запросы.Ссылка
ГДЕ
НЕ Взаимодействия.ПометкаУдаления
И Запросы.ДатаСоздания МЕЖДУ &ДатаНачала И &ДатаОкончания) КАК ВложенныйЗапрос
СГРУППИРОВАТЬ ПО
ВложенныйЗапрос.Взаимодействие,
ВложенныйЗапрос.Выполнено,
ВложенныйЗапрос.ДатаВыполнения,
ВЫБОР КОГДА ВложенныйЗапрос.Сделка.СвязаннаяСделка = ЗНАЧЕНИЕ(Справочник.СделкиСКлиентами.ПустаяСсылка) ТОГДА ВложенныйЗапрос.Сделка ИНАЧЕ ВложенныйЗапрос.Сделка.СвязаннаяСделка КОНЕЦ,
ВложенныйЗапрос.Дата
ИНДЕКСИРОВАТЬ ПО Сделка
;
////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
Запросы.Наименование,
Запросы.ДатаСоздания,
Запросы.Источник КАК ИсточникЗапроса,
Запросы.Статус,
Запросы.Ответственный КАК ОтветственныйМенеджер,
Запросы.Ссылка КАК НаименованиеЗапроса,
Запросы.Компания,
Запросы.РегионКлиента,
ТелефонныйЗвонок.Ссылка КАК СсылкаТелефон,
ТелефонныйЗвонок.Дата,
ТелефонныйЗвонок.Описание,
Запросы.Телефон КАК ТелефонКлиента,
СделкиСКлиентами.Партнер КАК Клиент,
СделкиСКлиентами.Менеджер,
СделкиСКлиентами.Организация,
СделкиСКлиентами.Наименование КАК СделкаСКлиентом,
ИсторияСтатусовСделокСКлиентамиСрезПоследних.Статус КАК СтатусСделкиСКлиентом,
СделкиПоКатегориям.Категория
ИЗ
ВТ_ВзаимодействияСделки КАК ВТ_ВзаимодействияСделки
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ТелефонныйЗвонок КАК ТелефонныйЗвонок
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Запросы КАК Запросы
ПО ТелефонныйЗвонок.ВзаимодействиеОснование = Запросы.Ссылка
ПО ВТ_ВзаимодействияСделки.Взаимодействие = ТелефонныйЗвонок.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СделкиСКлиентами КАК СделкиСКлиентами
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияСтатусовСделокСКлиентами.СрезПоследних КАК ИсторияСтатусовСделокСКлиентамиСрезПоследних
ПО СделкиСКлиентами.Ссылка = ИсторияСтатусовСделокСКлиентамиСрезПоследних.Сделка
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СделкиПоКатегориям КАК СделкиПоКатегориям
ПО СделкиСКлиентами.Ссылка = СделкиПоКатегориям.Сделка
ПО ВТ_ВзаимодействияСделки.Сделка = СделкиСКлиентами.Ссылка
Давайте выявим ключевые ошибки в этом запросе и последовательно их исправим.
ВТ_ВзаимодействияСделки (временной таблицы взаимодействий), что означает, что в выборку по умолчанию попадают только те записи, по которым есть взаимодействия. Если нам нужны все "Запросы", то именно "Запросы" должны быть главной таблицей.ГДЕ (WHERE) для ЛЕВОГО СОЕДИНЕНИЯ (LEFT JOIN): В запросе формирования временной таблицы условие Запросы.ДатаСоздания МЕЖДУ &ДатаНачала И &ДатаОкончания находится в секции ГДЕ. Когда мы используем ЛЕВОЕ СОЕДИНЕНИЕ, а затем фильтруем поля правой (присоединяемой) таблицы в ГДЕ, это фактически превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ, отсекая строки из левой таблицы, для которых не нашлось соответствия в правой (или поля которой оказались NULL из-за отсутствия соответствия).ПО) между таблицами: Некоторые условия соединения были неточными или не позволяли корректно получать данные из связанных таблиц, особенно при попытке связать "Запросы" с "Взаимодействиями" и "Телефонными звонками".Мы будем двигаться пошагово, исправляя выявленные ошибки. Ключевая идея — использовать Справочник.Запросы как основную таблицу и присоединять к ней все остальные данные с помощью ЛЕВОГО СОЕДИНЕНИЯ.
Прежде всего, давайте уточним, что должна содержать временная таблица ВТ_ВзаимодействияСделки. Ее задача — собрать информацию о взаимодействиях, связанных с предметами (в нашем случае, возможно, со сделками), и их статусах. Важно, чтобы эта временная таблица не отсекала данные о "Запросах" на ранних этапах, если только это не является явным требованием. Однако, исходя из логики решения, эта таблица также должна учитывать фильтр по датам запросов, чтобы не тянуть лишние данные, не соответствующие основному периоду отчета.
Рассмотрим исправление, предложенное в сообщении 5, которое касается размещения условия фильтрации. Если мы хотим, чтобы временная таблица содержала взаимодействия, связанные с запросами только в определенном периоде, то условие по дате создания запроса следует перенести из секции ГДЕ в секцию ПО для соединения ЖурналДокументов.Взаимодействия со Справочник.Запросы. Это гарантирует, что к взаимодействиям будут присоединяться только те запросы, которые удовлетворяют условию по дате, а сами взаимодействия без таких запросов или с запросами вне периода будут все равно учтены (если это требуется для дальнейшей логики) или их поля, относящиеся к запросам, будут NULL.
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ВложенныйЗапрос.Взаимодействие,
ВЫБОР КОГДА ВложенныйЗапрос.Сделка.СвязаннаяСделка = ЗНАЧЕНИЕ(Справочник.СделкиСКлиентами.ПустаяСсылка) ТОГДА ВложенныйЗапрос.Сделка ИНАЧЕ ВложенныйЗапрос.Сделка.СвязаннаяСделка КОНЕЦ КАК Сделка,
ВложенныйЗапрос.Выполнено,
ВложенныйЗапрос.ДатаВыполнения,
ВложенныйЗапрос.Дата
ПОМЕСТИТЬ ВТ_ВзаимодействияСделки
ИЗ
(ВЫБРАТЬ
Взаимодействия.Ссылка КАК Взаимодействие,
ЕСТЬNULL(ВЫРАЗИТЬ(ПредметыПапкиВзаимодействий.Предмет КАК Справочник.СделкиСКлиентами), ЗНАЧЕНИЕ(Справочник.СделкиСКлиентами.ПустаяСсылка)) КАК Сделка,
ЕСТЬNULL(ПредметыПапкиВзаимодействий.Рассмотрено, ЛОЖЬ) КАК Выполнено,
ЕСТЬNULL(ПредметыПапкиВзаимодействий.РассмотретьПосле, ДАТАВРЕМЯ(1, 1, 1)) КАК ДатаВыполнения,
Запросы.ДатаСоздания КАК Дата // Это поле 'Дата' относится к дате создания Запроса
ИЗ
ЖурналДокументов.Взаимодействия КАК Взаимодействия
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ПредметыПапкиВзаимодействий КАК ПредметыПапкиВзаимодействий
ПО Взаимодействия.Ссылка = ПредметыПапкиВзаимодействий.Взаимодействие
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Запросы КАК Запросы
ПО Взаимодействия.ВзаимодействиеОснование = Запросы.Ссылка
И Запросы.ДатаСоздания МЕЖДУ &ДатаНачала И &ДатаОкончания // <-- Исправление из сообщения 5
ГДЕ
НЕ Взаимодействия.ПометкаУдаления) КАК ВложенныйЗапрос
СГРУППИРОВАТЬ ПО
ВложенныйЗапрос.Взаимодействие,
ВложенныйЗапрос.Выполнено,
ВложенныйЗапрос.ДатаВыполнения,
ВЫБОР КОГДА ВложенныйЗапрос.Сделка.СвязаннаяСделка = ЗНАЧЕНИЕ(Справочник.СделкиСКлиентами.ПустаяСсылка) ТОГДА ВложенныйЗапрос.Сделка ИНАЧЕ ВложенныйЗапрос.Сделка.СвязаннаяСделка КОНЕЦ,
ВложенныйЗапрос.Дата
ИНДЕКСИРОВАТЬ ПО Сделка
;
В этом варианте временная таблица ВТ_ВзаимодействияСделки будет содержать только те взаимодействия, которые имеют связь с запросами, дата создания которых попадает в указанный период. Это важно, поскольку в дальнейшем мы будем строить отчет именно по запросам в этом же периоде.
Теперь переходим к основной части запроса, которая формирует итоговый результат. Здесь мы применим главные исправления:
Справочник.Запросы в качестве основной таблицы: Как было указано в сообщении 2, это ключевое изменение. Если мы хотим видеть все "Запросы", они должны быть отправной точкой.ЛЕВОЕ СОЕДИНЕНИЕ для всех дополнительных данных: Все остальные таблицы, такие как ВТ_ВзаимодействияСделки, Документ.ТелефонныйЗвонок, Справочник.СделкиСКлиентами и другие, будут присоединяться с помощью ЛЕВОГО СОЕДИНЕНИЯ. Это гарантирует, что все "Запросы" попадут в отчет, а связанные данные появятся, если они есть, или будут NULL в противном случае.ПО для соединений: Важно правильно связать таблицы, особенно при работе со ссылочными полями и их реквизитами.Давайте посмотрим на скорректированный основной запрос:
////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
Запросы.Наименование,
Запросы.ДатаСоздания,
Запросы.Источник КАК ИсточникЗапроса,
Запросы.Статус,
Запросы.Ответственный КАК ОтветственныйМенеджер,
Запросы.Ссылка КАК НаименованиеЗапроса,
Запросы.Компания,
Запросы.РегионКлиента,
ТелефонныйЗвонок.Ссылка КАК СсылкаТелефон,
ТелефонныйЗвонок.Дата,
ТелефонныйЗвонок.Описание,
Запросы.Телефон КАК ТелефонКлиента,
СделкиСКлиентами.Партнер КАК Клиент,
СделкиСКлиентами.Менеджер,
СделкиСКлиентами.Организация,
СделкиСКлиентами.Наименование КАК СделкаСКлиентом,
ИсторияСтатусовСделокСКлиентамиСрезПоследних.Статус КАК СтатусСделкиСКлиентом,
СделкиПоКатегориям.Категория
ИЗ
Справочник.Запросы КАК Запросы // <H3>Основная таблица - Справочник.Запросы</H3>
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВзаимодействияСделки КАК ВТ_ВзаимодействияСделки
ПО Запросы.Ссылка = ВТ_ВзаимодействияСделки.Взаимодействие.ВзаимодействиеОснование // <H3>Коррекция условия соединения (сообщения 3 и 22)</H3>
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ТелефонныйЗвонок КАК ТелефонныйЗвонок
ПО ВТ_ВзаимодействияСделки.Взаимодействие.ВзаимодействиеОснование = ТелефонныйЗвонок.ВзаимодействиеОснование // <H3>Коррекция условия соединения (сообщения 4 и 26)</H3>
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.СделкиСКлиентами КАК СделкиСКлиентами
ПО ВТ_ВзаимодействияСделки.Сделка = СделкиСКлиентами.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияСтатусовСделокСКлиентами.СрезПоследних КАК ИсторияСтатусовСделокСКлиентамиСрезПоследних
ПО СделкиСКлиентами.Ссылка = ИсторияСтатусовСделокСКлиентамиСрезПоследних.Сделка
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СделкиПоКатегориям КАК СделкиПоКатегориям
ПО СделкиСКлиентами.Ссылка = СделкиПоКатегориям.Сделка
ГДЕ
Запросы.ДатаСоздания МЕЖДУ &ДатаНачала И &ДатаОкончания // <H3>Фильтруем запросы по периоду</H3>
Давайте подробно разберем ключевые изменения в основном запросе:
ИЗ Справочник.Запросы КАК Запросы: Это фундаментальное изменение. Теперь отчет начинается именно с таблицы "Запросы", что гарантирует вывод всех запросов за указанный период, независимо от наличия связанных взаимодействий.ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ВзаимодействияСделки КАК ВТ_ВзаимодействияСделки ПО Запросы.Ссылка = ВТ_ВзаимодействияСделки.Взаимодействие.ВзаимодействиеОснование:
ЛЕВОЕ СОЕДИНЕНИЕ, чтобы присоединить данные из временной таблицы взаимодействий.ПО Запросы.Ссылка = ВТ_ВзаимодействияСделки.Взаимодействие.ВзаимодействиеОснование является крайне важным. Оно связывает текущий "Запрос" (Запросы.Ссылка) с "Основанием взаимодействия" (ВзаимодействиеОснование), которое, в свою очередь, получается из ссылки на само взаимодействие (ВТ_ВзаимодействияСделки.Взаимодействие). В 1С такая сквозная ссылка через реквизит ссылки является стандартной практикой для связывания объектов. Это позволяет корректно присоединить взаимодействия, которые являются основанием для данного запроса.ЛЕВОЕ СОЕДИНЕНИЕ Документ.ТелефонныйЗвонок КАК ТелефонныйЗвонок ПО ВТ_ВзаимодействияСделки.Взаимодействие.ВзаимодействиеОснование = ТелефонныйЗвонок.ВзаимодействиеОснование:
ПО ВТ_ВзаимодействияСделки.Взаимодействие.ВзаимодействиеОснование = ТелефонныйЗвонок.ВзаимодействиеОснование гарантирует, что телефонные звонки связываются с соответствующими записями из временной таблицы взаимодействий по их общему основанию (т.е. по тому запросу, на основании которого был сделан звонок).ВзаимодействиеОснование через ссылку на взаимодействие из временной таблицы, что было ключевым исправлением, позволившим связать данные о телефонном звонке.ГДЕ Запросы.ДатаСоздания МЕЖДУ &ДатаНачала И &ДатаОкончания: Это условие, применяемое непосредственно к основной таблице Справочник.Запросы, отфильтровывает все запросы, которые не попадают в заданный период, но при этом сохраняет все запросы из этого периода, даже если у них нет соответствующих взаимодействий.Благодаря этим изменениям, отчет теперь будет корректно выводить все "Запросы" за указанный период. Для тех "Запросов", по которым были созданы "Взаимодействия" (включая "Телефонные звонки"), соответствующие данные появятся в отчете. Для "Запросов" без взаимодействий или без соответствующих телефонных звонков, поля, относящиеся к этим таблицам, будут содержать значения NULL, как и требуется при использовании ЛЕВОГО СОЕДИНЕНИЯ.
ЛЕВОГО СОЕДИНЕНИЯ: Всегда помните, что ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) возвращает все записи из "левой" таблицы и только совпадающие записи из "правой". Если соответствия нет, поля из "правой" таблицы будут NULL. Это именно то поведение, которое нам нужно, когда мы хотим вывести все записи из одной сущности и, при наличии, добавить информацию из другой.ГДЕ и ПО: Еще раз подчеркнем: фильтрация по полям правой таблицы в секции ГДЕ после ЛЕВОГО СОЕДИНЕНИЯ фактически превратит его во ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Если вы хотите отфильтровать данные, но сохранить записи из левой таблицы, перенесите условие в секцию ПО (ON) для этого конкретного соединения. Если же вы хотите отфильтровать итоговый результат, то используйте ГДЕ, но убедитесь, что это не отсекает нужные строки из вашей основной таблицы.Применяя эти принципы, вы сможете избежать распространенных ошибок при построении отчетов в 1С и всегда получать точные и полные данные.