Как в СКД включить в отбор пустую дату и не потерять строки при объединении таблиц?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов на схеме компоновки данных (СКД) программисты часто сталкиваются со следующей проблемой: отчет собирается из нескольких таблиц с помощью предложения ОБЪЕДИНИТЬ ВСЕ. В одной из таблиц (например, в документах) есть дата, а во второй таблице (например, контактные лица или должности сотрудников) даты по определению нет. Когда пользователь строит отчет без отборов, выводятся все данные. Однако стоит только установить отбор по дате, как строки из второй таблицы (где дата пустая) бесследно исчезают. Это происходит потому, что пустая дата не попадает в заданный пользователем интервал.

В этой статье мы подробно разберем, почему так происходит, проанализируем типичные ошибки и рассмотрим несколько эффективных способов решения этой задачи в 1С.

Анализ исходной ситуации и типичные ошибки

Посмотрим на пример запроса, который часто пишут начинающие разработчики для объединения данных:


ВЫБРАТЬ
    CRM_Обслуживание.Ссылка,
    CRM_Обслуживание.Номер,
    CRM_Обслуживание.Дата КАК Дата,
    CRM_Обслуживание.Контрагент КАК Контрагент,
    0 КАК Должность,
    КонтактнаяИнформация.Представление
ИЗ
    Документ.CRM_Обслуживание КАК CRM_Обслуживание
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
        ПО CRM_Обслуживание.Контрагент = КонтактнаяИнформация.Объект

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ВТКонтактыИДолжно.Поле1,
    ВТКонтактыИДолжно.Поле2,
    0, // Ошибка типизации!
    ВТКонтактыИДолжно.НаимКонтрагента,
    ВТКонтактыИДолжно.Должность,
    ВТКонтактыИДолжно.Представление
ИЗ
    ВТКонтактыИДолжно КАК ВТКонтактыИДолжно

В данном запросе присутствуют сразу две проблемы:

  1. Некорректное приведение типов (типизация полей): Во второй части объединения вместо даты указано числовое значение 0. Платформа 1С пытается сопоставить число и дату, что приводит к неявным преобразованиям типов. Это может вызвать непредсказуемое поведение СУБД при наложении индексов и фильтров. Правильным решением для обозначения пустой даты в запросе является использование конструкции ДАТАВРЕМЯ(1, 1, 1).
  2. Исчезновение строк при фильтрации: Если пользователь устанавливает стандартный отбор СКД, например, Дата Меньше или равно &КонецПериода, то пустая дата 01.01.0001 формально подходит под это условие. Но если задан интервал (например, Дата Больше или равно &НачалоПериода), то пустая дата отсекается, и все связанные с ней должности и контакты пропадают из отчета.

Рассмотрим по шагам методы, которые позволяют решить эту проблему красиво и профессионально.

Решение 1. Использование языка расширения запросов СКД (Фигурные скобки)

Это самый элегантный и архитектурно верный способ решения задачи в СКД. Мы можем указать системе компоновки данных, что отбор по дате должен применяться только к первой части запроса (где выбираются документы), а вторую часть (с должностями и контактами) этот отбор затрагивать не должен.

Для этого мы воспользуемся возможностями языка расширения запросов СКД и заключим условие отбора в фигурные скобки {}:


ВЫБРАТЬ
    CRM_Обслуживание.Ссылка,
    CRM_Обслуживание.Номер,
    CRM_Обслуживание.Дата КАК Дата,
    CRM_Обслуживание.Контрагент КАК Контрагент,
    0 КАК Должность,
    КонтактнаяИнформация.Представление
ИЗ
    Документ.CRM_Обслуживание КАК CRM_Обслуживание
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
        ПО CRM_Обслуживание.Контрагент = КонтактнаяИнформация.Объект
{ГДЕ
    (CRM_Обслуживание.Дата >= &НачалоПериода И CRM_Обслуживание.Дата <= &КонецПериода)}

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ВТКонтактыИДолжно.Поле1,
    ВТКонтактыИДолжно.Поле2,
    ДАТАВРЕМЯ(1, 1, 1), // Корректная пустая дата
    ВТКонтактыИДолжно.НаимКонтрагента,
    ВТКонтактыИДолжно.Должность,
    ВТКонтактыИДолжно.Представление
ИЗ
    ВТКонтактыИДолжно КАК ВТКонтактыИДолжно

Как это работает: Когда пользователь устанавливает период в параметрах отчета, СКД динамически модифицирует только первый запрос в секции ОБЪЕДИНИТЬ ВСЕ, вставляя условие фильтрации туда, где расположены фигурные скобки. Второй запрос выполняется полностью, без наложения фильтра на пустую дату. В итоге документы фильтруются по датам, а контакты и должности выводятся всегда. Для отладки и проверки результирующих запросов схемы компоновки можно использовать консоль запросов с поддержкой отчетов СКД.

Решение 2. Подмена пустой даты в вычисляемых полях СКД

Если по какой-то причине нам нужно предоставить пользователю возможность фильтровать абсолютно все строки, но при этом "виртуально" приравнивать пустые даты к дате конца периода фильтрации, мы можем воспользоваться механизмом Вычисляемых полей в схеме компоновки.

Выполним следующие шаги:

  1. Перейдем на вкладку Вычисляемые поля в конструкторе СКД.
  2. Добавим новое поле с именем, например, ДатаДляФильтрации.
  3. В колонке "Выражение" пропишем следующее условие: Выбор Когда ЗначениеЗаполнено(Дата) Тогда Дата Иначе &КонецПериода Конец
  4. На вкладке Настройки в панели отборов заменим стандартное поле Дата на наше новое вычисляемое поле ДатаДляФильтрации.

При таком подходе, если у строки нет даты, СКД во время расчета отчета подставит вместо нее дату окончания периода (которая гарантированно удовлетворяет условиям фильтра "попадание в период"). Строка не пропадет из финального результата.

Решение 3. Группировка условий "ИЛИ" на уровне пользовательских настроек

Этот способ не требует изменения текста запроса и программирования. Настроить поведение можно напрямую в режиме 1С:Предприятие или на вкладке настроек по умолчанию в конфигураторе.

Рассмотрим, как настроить отбор:

  1. Откроем настройки отбора отчета.
  2. Добавим Группу условий ИЛИ.
  3. Внутри этой группы добавим два элемента отбора:
    • Элемент 1: Дата в интервале дат (например, между &НачалоПериода и &КонецПериода).
    • Элемент 2: Дата Не заполнено (или Дата Равно 01.01.0001 00:00:00).

В результате система выведет все записи, которые соответствуют выбранному периоду, а также все строки, где дата не указана вовсе. Это простое и надежное решение, понятное конечному пользователю.

Решение 4. Переход на ЛЕВОЕ СОЕДИНЕНИЕ и использование функции ЕСТЬNULL

Проанализируем структуру данных. Зачастую использование ОБЪЕДИНИТЬ ВСЕ является избыточным и приводит к усложнению логики отборов. Если нам нужно вывести контрагентов, их документы обслуживания и контактную информацию (включая должности), гораздо эффективнее использовать левое соединение таблиц.

Рассмотрим пример запроса с левым соединением:


ВЫБРАТЬ
    СправочникКонтрагенты.Ссылка КАК Контрагент,
    ЕСТЬNULL(CRM_Обслуживание.Дата, ДАТАВРЕМЯ(1, 1, 1)) КАК Дата,
    CRM_Обслуживание.Ссылка КАК ДокументОбслуживания,
    Контакты.Должность КАК Должность,
    Контакты.Представление КАК КонтактноеЛицо
ИЗ
    Справочник.Контрагенты КАК СправочникКонтрагенты
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.CRM_Обслуживание КАК CRM_Обслуживание
        ПО CRM_Обслуживание.Контрагент = СправочникКонтрагенты.Ссылка
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Контакты КАК Контакты
        ПО Контакты.Объект = СправочникКонтрагенты.Ссылка

Использование функции ЕСТЬNULL позволяет нам гарантировать, что даже при отсутствии документов обслуживания поле Дата не превратится в NULL (с которым СКД иногда работает не так, как ожидает разработчик), а вернет пустую дату платформы. После этого к запросу можно спокойно применять решение с фигурной скобкой или вычисляемыми полями.

Выбор конкретного метода зависит от архитектуры вашего отчета и требований к гибкости фильтрации, однако использование расширения языка запросов СКД в связке с правильной типизацией пустой даты через ДАТАВРЕМЯ(1, 1, 1) является стандартом разработки в среде 1С.

← На главную