При разработке отчетов на схеме компоновки данных (СКД) программисты часто сталкиваются со следующей проблемой: отчет собирается из нескольких таблиц с помощью предложения ОБЪЕДИНИТЬ ВСЕ. В одной из таблиц (например, в документах) есть дата, а во второй таблице (например, контактные лица или должности сотрудников) даты по определению нет. Когда пользователь строит отчет без отборов, выводятся все данные. Однако стоит только установить отбор по дате, как строки из второй таблицы (где дата пустая) бесследно исчезают. Это происходит потому, что пустая дата не попадает в заданный пользователем интервал.
В этой статье мы подробно разберем, почему так происходит, проанализируем типичные ошибки и рассмотрим несколько эффективных способов решения этой задачи в 1С.
Посмотрим на пример запроса, который часто пишут начинающие разработчики для объединения данных:
ВЫБРАТЬ
CRM_Обслуживание.Ссылка,
CRM_Обслуживание.Номер,
CRM_Обслуживание.Дата КАК Дата,
CRM_Обслуживание.Контрагент КАК Контрагент,
0 КАК Должность,
КонтактнаяИнформация.Представление
ИЗ
Документ.CRM_Обслуживание КАК CRM_Обслуживание
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
ПО CRM_Обслуживание.Контрагент = КонтактнаяИнформация.Объект
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ВТКонтактыИДолжно.Поле1,
ВТКонтактыИДолжно.Поле2,
0, // Ошибка типизации!
ВТКонтактыИДолжно.НаимКонтрагента,
ВТКонтактыИДолжно.Должность,
ВТКонтактыИДолжно.Представление
ИЗ
ВТКонтактыИДолжно КАК ВТКонтактыИДолжно
В данном запросе присутствуют сразу две проблемы:
0. Платформа 1С пытается сопоставить число и дату, что приводит к неявным преобразованиям типов. Это может вызвать непредсказуемое поведение СУБД при наложении индексов и фильтров. Правильным решением для обозначения пустой даты в запросе является использование конструкции ДАТАВРЕМЯ(1, 1, 1).Дата Меньше или равно &КонецПериода, то пустая дата 01.01.0001 формально подходит под это условие. Но если задан интервал (например, Дата Больше или равно &НачалоПериода), то пустая дата отсекается, и все связанные с ней должности и контакты пропадают из отчета.Рассмотрим по шагам методы, которые позволяют решить эту проблему красиво и профессионально.
Это самый элегантный и архитектурно верный способ решения задачи в СКД. Мы можем указать системе компоновки данных, что отбор по дате должен применяться только к первой части запроса (где выбираются документы), а вторую часть (с должностями и контактами) этот отбор затрагивать не должен.
Для этого мы воспользуемся возможностями языка расширения запросов СКД и заключим условие отбора в фигурные скобки {}:
ВЫБРАТЬ
CRM_Обслуживание.Ссылка,
CRM_Обслуживание.Номер,
CRM_Обслуживание.Дата КАК Дата,
CRM_Обслуживание.Контрагент КАК Контрагент,
0 КАК Должность,
КонтактнаяИнформация.Представление
ИЗ
Документ.CRM_Обслуживание КАК CRM_Обслуживание
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
ПО CRM_Обслуживание.Контрагент = КонтактнаяИнформация.Объект
{ГДЕ
(CRM_Обслуживание.Дата >= &НачалоПериода И CRM_Обслуживание.Дата <= &КонецПериода)}
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ВТКонтактыИДолжно.Поле1,
ВТКонтактыИДолжно.Поле2,
ДАТАВРЕМЯ(1, 1, 1), // Корректная пустая дата
ВТКонтактыИДолжно.НаимКонтрагента,
ВТКонтактыИДолжно.Должность,
ВТКонтактыИДолжно.Представление
ИЗ
ВТКонтактыИДолжно КАК ВТКонтактыИДолжно
Как это работает: Когда пользователь устанавливает период в параметрах отчета, СКД динамически модифицирует только первый запрос в секции ОБЪЕДИНИТЬ ВСЕ, вставляя условие фильтрации туда, где расположены фигурные скобки. Второй запрос выполняется полностью, без наложения фильтра на пустую дату. В итоге документы фильтруются по датам, а контакты и должности выводятся всегда. Для отладки и проверки результирующих запросов схемы компоновки можно использовать консоль запросов с поддержкой отчетов СКД.
Если по какой-то причине нам нужно предоставить пользователю возможность фильтровать абсолютно все строки, но при этом "виртуально" приравнивать пустые даты к дате конца периода фильтрации, мы можем воспользоваться механизмом Вычисляемых полей в схеме компоновки.
Выполним следующие шаги:
ДатаДляФильтрации.Выбор Когда ЗначениеЗаполнено(Дата) Тогда Дата Иначе &КонецПериода Конец
Дата на наше новое вычисляемое поле ДатаДляФильтрации.При таком подходе, если у строки нет даты, СКД во время расчета отчета подставит вместо нее дату окончания периода (которая гарантированно удовлетворяет условиям фильтра "попадание в период"). Строка не пропадет из финального результата.
Этот способ не требует изменения текста запроса и программирования. Настроить поведение можно напрямую в режиме 1С:Предприятие или на вкладке настроек по умолчанию в конфигураторе.
Рассмотрим, как настроить отбор:
Дата в интервале дат (например, между &НачалоПериода и &КонецПериода).Дата Не заполнено (или Дата Равно 01.01.0001 00:00:00).В результате система выведет все записи, которые соответствуют выбранному периоду, а также все строки, где дата не указана вовсе. Это простое и надежное решение, понятное конечному пользователю.
Проанализируем структуру данных. Зачастую использование ОБЪЕДИНИТЬ ВСЕ является избыточным и приводит к усложнению логики отборов. Если нам нужно вывести контрагентов, их документы обслуживания и контактную информацию (включая должности), гораздо эффективнее использовать левое соединение таблиц.
Рассмотрим пример запроса с левым соединением:
ВЫБРАТЬ
СправочникКонтрагенты.Ссылка КАК Контрагент,
ЕСТЬNULL(CRM_Обслуживание.Дата, ДАТАВРЕМЯ(1, 1, 1)) КАК Дата,
CRM_Обслуживание.Ссылка КАК ДокументОбслуживания,
Контакты.Должность КАК Должность,
Контакты.Представление КАК КонтактноеЛицо
ИЗ
Справочник.Контрагенты КАК СправочникКонтрагенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.CRM_Обслуживание КАК CRM_Обслуживание
ПО CRM_Обслуживание.Контрагент = СправочникКонтрагенты.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Контакты КАК Контакты
ПО Контакты.Объект = СправочникКонтрагенты.Ссылка
Использование функции ЕСТЬNULL позволяет нам гарантировать, что даже при отсутствии документов обслуживания поле Дата не превратится в NULL (с которым СКД иногда работает не так, как ожидает разработчик), а вернет пустую дату платформы. После этого к запросу можно спокойно применять решение с фигурной скобкой или вычисляемыми полями.
Выбор конкретного метода зависит от архитектуры вашего отчета и требований к гибкости фильтрации, однако использование расширения языка запросов СКД в связке с правильной типизацией пустой даты через ДАТАВРЕМЯ(1, 1, 1) является стандартом разработки в среде 1С.