Работа с Системой компоновки данных (СКД) в 1С часто преподносит сюрпризы, связанные с ее интеллектуальными механизмами оптимизации запросов. Одной из классических проблем является ситуация, когда при попытке отобрать записи с пустой датой (например, контрагентов без документов) из отчета внезапно исчезают вообще все данные. Рассмотрим подробно, почему это происходит и как правильно настроить отчет, чтобы он работал ожидаемо.
Когда мы создаем набор данных «Запрос» и оставляем включенной галочку «Автозаполнение», СКД берет на себя управление текстом запроса. Проанализируем типичный сценарий: нам нужно вывести список всех контрагентов и дату их последнего счета на оплату. Для этого мы используем временную таблицу для получения максимальной даты и левое соединение со справочником контрагентов.
Выясним, что делает компоновщик, когда пользователь устанавливает в настройках отчета фильтр «Дата последнего счета Не заполнена»:
Дата.Проанализируем результат этой оптимизации. В запрос временной таблицы добавляется условие ГДЕ Дата = ДАТАВРЕМЯ(1, 1, 1). Поскольку в реальных документах дата всегда заполнена, временная таблица становится абсолютно пустой. При последующем левом соединении справочника контрагентов с пустой таблицей мы получаем значения NULL для всех контрагентов. Но так как отбор по незаполненной дате уже «отработал» на этапе формирования временной таблицы и отсек все записи, итоговый результат может оказаться непредсказуемым или полностью пустым (вспомните особенность: в СКД нет данных — нет столбцов) в зависимости от того, как СКД интерпретирует NULL и пустую дату в конкретном контексте.
Самый быстрый и простой способ «обмануть» механизм автозаполнения, не отключая его — это разрыв связи между именами полей. Если компоновщик не сможет сопоставить поле итогового запроса с полем временной таблицы, он не станет накладывать отбор на промежуточном этапе.
Рассмотрим пример измененного запроса (проверить выполнение которого поможет Консоль запросов УФ):
// Временная таблица с уникальным именем поля
ВЫБРАТЬ
_ДемоСчетНаОплатуПокупателю.Контрагент КАК Контрагент,
МАКСИМУМ(_ДемоСчетНаОплатуПокупателю.Дата) КАК ДатаПоследнегоСчета_ВТ
ПОМЕСТИТЬ втДатаПоследнегоСчета
ИЗ
Документ._ДемоСчетНаОплатуПокупателю КАК _ДемоСчетНаОплатуПокупателю
СГРУППИРОВАТЬ ПО
_ДемоСчетНаОплатуПокупателю.Контрагент;
////////////////////////////////////////////////////////////////////////////////
// Основной запрос
ВЫБРАТЬ
_ДемоКонтрагенты.Ссылка КАК Ссылка,
втДатаПоследнегоСчета.ДатаПоследнегоСчета_ВТ КАК Дата
ИЗ
Справочник._ДемоКонтрагенты КАК _ДемоКонтрагенты
ЛЕВОЕ СОЕДИНЕНИЕ втДатаПоследнегоСчета КАК втДатаПоследнегоСчета
ПО втДатаПоследнегоСчета.Контрагент = _ДемоКонтрагенты.Ссылка
В данном случае отбор пользователя по полю Дата применится только к финальному результату соединения, и контрагенты, у которых нет счетов (значение NULL), корректно попадут в отчет.
Важно помнить, что в базе данных 1С пустая дата — это 01.01.0001, а результат отсутствия записи при левом соединении — это NULL. Для пользователя в отчете эти понятия часто идентичны, но для СУБД это разные значения, что критично, например, при сравнении строки с представлением ссылки в запросе. Посмотрим, как можно явно обработать эти значения в запросе для корректной работы отборов.
Рассмотрим вариант написания запроса с явным указанием секций компоновки:
ВЫБРАТЬ
Контрагенты.Ссылка КАК Контрагент,
ЕСТЬNULL(ВТ_Заказы.Дата, ДАТАВРЕМЯ(1, 1, 1)) КАК Дата
{ВЫБРАТЬ
Контрагент.*,
Дата}
ИЗ
Справочник.Контрагенты КАК Контрагенты
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Заказы КАК ВТ_Заказы
ПО Контрагенты.Ссылка = ВТ_Заказы.Контрагент
{ГДЕ
(ЕСТЬNULL(ВТ_Заказы.Дата, ДАТАВРЕМЯ(1, 1, 1))) КАК Дата}
Обратите внимание на конструкцию в фигурных скобках {ГДЕ ...}. Это расширение языка запросов СКД. Оно указывает системе: «Если пользователь установил отбор по полю Дата, примени именно это условие и именно здесь». Это гарантирует, что отбор не уйдет во временные таблицы.
Если отчет сложный и автоматические механизмы СКД продолжают мешать, лучшим решением будет отключение Автозаполнения. Однако программисты часто пугаются, когда после снятия этой галочки из списка доступных полей набора данных исчезают все поля. Выясним, как правильно настроить компоненты вручную.
Проанализируем пошаговый алгоритм настройки при отключенном автозаполнении:
ГДЕ.ЕСТЬNULL.После этих действий на вкладке «Набор данных» появятся только те поля, которые вы выбрали вручную. Это дает 100% контроль над тем, как будет исполняться запрос.
Рассмотрим важный технический момент. Проталкивание отборов во временные таблицы — это не ошибка, а оптимизация. Если в вашей базе миллионы контрагентов и миллионы счетов, то выполнение левого соединения Всех со Всеми перед фильтрацией может существенно замедлить отчет.
Поэтому, если вы отключаете автоматическое проталкивание отборов, проанализируйте объем данных. В некоторых случаях лучше оставить автозаполнение, но во временной таблице использовать не просто Дата, а МАКСИМУМ(Дата) и фильтровать уже результат группировки через секцию ИМЕЮЩИЕ.
Резюме: Чтобы отбор по пустой дате работал корректно при использовании левых соединений, необходимо либо изолировать имена полей во временных таблицах от имен в итоговой выборке, либо использовать ручное управление секцией {ГДЕ} в тексте запроса через вкладку «Компоновка данных» в конструкторе запроса.