Почему отчет СКД пуст при отборе по вычисляемому булевому полю, содержащему проверки на NULL?

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

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

Анализируем исходную проблему с отбором

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

Разбираем логику вычисления булевого поля с NULL

Давайте подробно рассмотрим фрагмент запроса, в котором вычисляется проблемное поле Сопоставлен. В данном случае выражение выглядит так:


НЕ СоответствиеЭлементовМО.ЭлементМО ЕСТЬ NULL И НЕ СоответствиеЭлементовМО.ЭлементМО = ЗНАЧЕНИЕ(Справочник.ПереченьПериодичностиМедосмотра.ПустаяСсылка) КАК Сопоставлен

На первый взгляд, это выражение кажется вполне логичным: поле Сопоставлен должно принимать значение Истина только в том случае, если СоответствиеЭлементовМО.ЭлементМО не является NULL И одновременно не является пустой ссылкой. В противном случае оно должно быть Ложь. Однако, здесь необходимо учитывать особенности обработки значения NULL в языке запросов 1С, поведение которого во многом схоже с поведением SQL.

Особенности обработки NULL в логических выражениях 1С

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

  1. ИСТИНА И НЕОПРЕДЕЛЕНО всегда дает НЕОПРЕДЕЛЕНО.
  2. ЛОЖЬ И НЕОПРЕДЕЛЕНО всегда дает ЛОЖЬ.
  3. НЕОПРЕДЕЛЕНО И НЕОПРЕДЕЛЕНО всегда дает НЕОПРЕДЕЛЕНО.

При применении отбора в СКД значение НЕОПРЕДЕЛЕНО, как правило, интерпретируется как ЛОЖЬ. Это означает, что записи, для которых вычисляемое поле принимает значение НЕОПРЕДЕЛЕНО, будут исключены из результата отчета, даже если мы ожидали, что они пройдут отбор, например, по условию Сопоставлен = Ложь.

Давайте еще раз внимательно проанализируем наше выражение НЕ СоответствиеЭлементовМО.ЭлементМО ЕСТЬ NULL И НЕ СоответствиеЭлементовМО.ЭлементМО = ЗНАЧЕНИЕ(Справочник.ПереченьПериодичностиМедосмотра.ПустаяСсылка):

  1. Если СоответствиеЭлементовМО.ЭлементМО равно NULL (например, из-за левого соединения, когда соответствие не найдено):

    • Первая часть выражения (НЕ СоответствиеЭлементовМО.ЭлементМО ЕСТЬ NULL) будет ЛОЖЬ.
    • Вторая часть выражения (НЕ СоответствиеЭлементовМО.ЭлементМО = ЗНАЧЕНИЕ(...)) будет НЕОПРЕДЕЛЕНО, так как сравнение NULL = ПустаяСсылка дает НЕОПРЕДЕЛЕНО, и оператор НЕ не меняет НЕОПРЕДЕЛЕНО на булево значение, оставляя его НЕОПРЕДЕЛЕНО.
    • Всё выражение в целом (ЛОЖЬ И НЕОПРЕДЕЛЕНО) в итоге даст ЛОЖЬ. В этом случае, на первый взгляд, поведение ожидаемое.
  2. Если СоответствиеЭлементовМО.ЭлементМО является пустой ссылкой (ЗНАЧЕНИЕ(Справочник.ПереченьПериодичностиМедосмотра.ПустаяСсылка)):

    • Первая часть выражения (НЕ СоответствиеЭлементовМО.ЭлементМО ЕСТЬ NULL) будет ИСТИНА.
    • Вторая часть выражения (НЕ СоответствиеЭлементовМО.ЭлементМО = ЗНАЧЕНИЕ(...)) будет ЛОЖЬ (поскольку ПустаяСсылка = ПустаяСсылка дает ИСТИНА, а НЕ ИСТИНА дает ЛОЖЬ).
    • Всё выражение в целом (ИСТИНА И ЛОЖЬ) в итоге даст ЛОЖЬ. Это тоже ожидаемое поведение.
  3. Если СоответствиеЭлементовМО.ЭлементМО содержит заполненную ссылку:

    • Первая часть выражения (НЕ СоответствиеЭлементовМО.ЭлементМО ЕСТЬ NULL) будет ИСТИНА.
    • Вторая часть выражения (НЕ СоответствиеЭлементовМО.ЭлементМО = ЗНАЧЕНИЕ(...)) будет ИСТИНА (поскольку заполненная ссылка не равна пустой ссылке).
    • Всё выражение в целом (ИСТИНА И ИСТИНА) в итоге даст ИСТИНА.

Как мы видим из анализа, при любом из трех состояний СоответствиеЭлементовМО.ЭлементМО, конечное булево значение поля Сопоставлен получается либо ИСТИНА, либо ЛОЖЬ. Однако, проблема может скрываться в неявном поведении СКД или оптимизатора запросов. Если мы в консоли запросов видим, что все сотрудники имеют Сопоставлен = Ложь, это означает, что для них СоответствиеЭлементовМО.ЭлементМО либо равно NULL, либо пустой ссылке. Если мы устанавливаем отбор Сопоставлен = Истина, отчет будет пуст, так как таких записей нет. Но если отчет пуст и при отборе Сопоставлен = Ложь, это указывает на то, что СКД каким-то образом не может корректно применить это условие к вычисленному полю. Часто это происходит, когда оптимизатор СКД пытается "протолкнуть" отбор на уровень данных, но сталкивается со сложной логикой вычисления, особенно с учетом потенциального НЕОПРЕДЕЛЕНО, даже если в итоге оно сводится к Ложь.

Явное приведение NULL к значению для надежного отбора

Для того чтобы исключить любые неоднозначности и сделать логику вычисления поля максимально предсказуемой для СКД и оптимизатора запросов, мы настоятельно рекомендуем явно преобразовывать NULL-значения. Это можно реализовать с помощью оператора ВЫБОР КОГДА или функции ЕСТЬNULL().

  1. Вариант 1: Явное преобразование через ВЫБОР КОГДА

    Наиболее наглядный и надежный способ – это явно определить все возможные состояния поля с помощью оператора ВЫБОР КОГДА:

    
    ВЫБОР
        КОГДА СоответствиеЭлементовМО.ЭлементМО ЕСТЬ NULL ТОГДА ЛОЖЬ
        КОГДА СоответствиеЭлементовМО.ЭлементМО = ЗНАЧЕНИЕ(Справочник.ПереченьПериодичностиМедосмотра.ПустаяСсылка) ТОГДА ЛОЖЬ
        ИНАЧЕ ИСТИНА
    КОНЕЦ КАК Сопоставлен
    

    Этот подход четко указывает, что в случаях, когда СоответствиеЭлементовМО.ЭлементМО равно NULL или является пустой ссылкой, поле Сопоставлен будет ЛОЖЬ. Во всех остальных случаях оно будет ИСТИНА. Таким образом, мы полностью исключаем возможность появления промежуточных значений НЕОПРЕДЕЛЕНО, что делает логику абсолютно прозрачной для СКД.

  2. Вариант 2: Использование функции ЕСТЬNULL() для компактности

    Более компактный способ достичь того же результата с помощью функции ЕСТЬNULL(), которая позволяет заменить NULL-значение на указанное:

    
    НЕ ЕСТЬNULL(СоответствиеЭлементовМО.ЭлементМО, ЗНАЧЕНИЕ(Справочник.ПереченьПериодичностиМедосмотра.ПустаяСсылка)) = ЗНАЧЕНИЕ(Справочник.ПереченьПериодичностиМедосмотра.ПустаяСсылка) КАК Сопоставлен
    

    В этом выражении мы сначала заменяем все NULL-значения поля СоответствиеЭлементовМО.ЭлементМО на пустую ссылку справочника ПереченьПериодичностиМедосмотра. Затем мы сравниваем результат с пустой ссылкой. Если он равен пустой ссылке (что означает, что исходное значение было либо NULL, либо пустой ссылкой), то сравнение будет ИСТИНА, а с оператором НЕ даст ЛОЖЬ. В противном случае (если исходное значение было заполненной ссылкой), сравнение с пустой ссылкой даст ЛОЖЬ, а с оператором НЕ – ИСТИНА. Этот метод также гарантирует, что поле Сопоставлен всегда будет иметь строго булево значение.

Применив одно из этих выражений, мы обеспечиваем, что поле Сопоставлен всегда будет принимать строго булевое значение (ИСТИНА или ЛОЖЬ) без возможности появления НЕОПРЕДЕЛЕНО. Это позволит СКД корректно применять отбор, и ваш отчет перестанет быть пустым.

Дополнительные рекомендации по работе с отборами в СКД

Помимо корректного вычисления булевых полей, есть и другие аспекты, которые следует учитывать при работе с отборами в СКД:

  1. Размещайте вычисления в основном запросе. Если поле, по которому вы планируете устанавливать отбор, является вычисляемым, СКД стремится "протолкнуть" это условие отбора как можно ближе к источнику данных, то есть на уровень запроса. Если вычисление поля происходит в промежуточном или итоговом запросе (как это было в изначальном примере с полем Сопоставлен), это может затруднить оптимизацию отбора. По возможности, включайте вычисление таких полей непосредственно в самый первый запрос, который формирует первичные данные. Это позволяет СКД более эффективно применять отборы.

  2. Используйте пользовательские поля в СКД. Если переместить вычисление поля в основной запрос по каким-либо причинам невозможно или нецелесообразно, вы можете определить его как "Вычисляемое поле" непосредственно в наборе данных СКД. В этом случае вы задаете выражение для вычисления поля в настройках СКД, а затем уже по этому вычисляемому полю настраиваете отбор. Важно убедиться, что выражение в вычисляемом поле СКД также учитывает все аспекты обработки NULL, как мы рассмотрели выше.

  3. Проверяйте тип данных вычисляемого поля. Хотя мы явно стремимся к булевому типу, иногда из-за особенностей сложных вычислений и взаимодействия с NULL тип может быть Неопределено. Функция ТИПЗНАЧЕНИЯ(), используемая в тестовых запросах, является отличным инструментом для отладки и проверки того, какой тип данных в действительности принимает ваше вычисляемое поле на разных этапах.

Работа с настройками отчета и кэшем

Иногда даже после исправления логики вычисления поля могут возникать проблемы с сохранением настроек отчета или применением старых значений отбора. Это может быть связано с несколькими факторами:

  1. Создайте новый вариант отчета. В некоторых версиях платформы или типовых конфигурациях могут возникать особенности в механизмах сохранения и загрузки настроек вариантов отчетов. Существующие варианты, особенно если они были созданы давно или модифицированы, могут иметь жестко заданные или "закешированные" настройки. Попробуйте создать совершенно новый вариант отчета "с нуля", чтобы исключить влияние старых или поврежденных настроек.

  2. Очистите кэш. Помимо общего кэша конфигурации, в 1С также существует кэш пользовательских настроек и временных данных, который может влиять на поведение отчетов. Попытайтесь очистить его. Это можно сделать различными способами: через специальную обработку (если она предусмотрена в вашей конфигурации), через интерфейс программы (например, "Сервис и настройки" -> "Настройки" -> "Параметры" -> "Очистить кэш" или подобные пункты), либо вручную удалив файлы кэша в каталоге пользователя.

  3. Проверьте типовую конфигурацию. Если отчет является штатным из типовой конфигурации (например, EHSMS), возможно, что отбор или параметры отчета жестко заданы в коде формирования варианта отчета или непосредственно в схеме компоновки данных. В таких случаях пользовательские изменения могут не иметь приоритета или вовсе не применяться. Для решения этой проблемы может потребоваться доработка конфигурации (желательно через механизмы расширений) или создание собственного, независимого отчета.

Мы выяснили, что основной причиной некорректной работы отбора по вычисляемому булевому полю в СКД часто является неявная обработка NULL в логических выражениях запроса. Используя явное приведение NULL к конкретному значению (например, ЛОЖЬ или пустая ссылка), мы можем добиться предсказуемого поведения отчета и корректной работы отбора. Надеемся, что эти рекомендации помогут вам в отладке и создании стабильных отчетов на СКД.

← На главную