Почему запрос проверки отрицательных остатков возвращает NULL и как это исправить?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При выполнении процедуры закрытия месяца в конфигурациях «1С:Управление торговлей 11» (а также в «1С:ERP» и «1С:Комплексная автоматизация») пользователи и разработчики нередко сталкиваются с ситуацией, когда регламентный этап «Исправление отрицательных остатков организации» остается в незавершенном статусе. При этом запуск стандартной обработки исправления сообщает, что отрицательных остатков не обнаружено, однако этап закрытия месяца все равно висит как невыполненный.

Разберем детально механизм возникновения этой проблемы, проанализируем поведение оптимизатора запросов платформы и СУБД, а также рассмотрим пошаговые способы устранения ошибки.

Анализ проблемы и поведения запроса

Посмотрим на типовой запрос, который используется платформой для определения статуса необходимости этапа исправления остатков:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    Остатки.ЕстьОтрицательныеОстатки
ИЗ (
    ВЫБРАТЬ
        ИСТИНА КАК ЕстьОтрицательныеОстатки,
        Остатки.Организация,
        Остатки.АналитикаУчетаНоменклатуры,
        Остатки.ВидЗапасов,
        Остатки.НомерГТД
    ИЗ
        РегистрНакопления.ТоварыОрганизаций.Остатки(&Граница, Организация В (&МассивОрганизаций)) КАК Остатки
    ГДЕ
        Остатки.КоличествоОстаток < 0
) КАК Остатки

При выполнении этого запроса в консоли, даже если реальных движений с отрицательными значениями в базе нет, запрос может возвращать ровно одну строку со значением NULL в поле ЕстьОтрицательныеОстатки. Если же переписать запрос, заменив выборку поля на ВЫБРАТЬ РАЗЛИЧНЫЕ *, результат становится полностью пустым (0 строк).

Выясним причины такого поведения:

  1. Особенности трансляции виртуальных таблиц в SQL: Виртуальная таблица остатков регистра накопления на уровне СУБД формируется через соединение таблиц движений (_AccumReg) и таблиц итогов (_AccumRegTotals). Когда во внешнем запросе выбирается конструкция ВЫБРАТЬ РАЗЛИЧНЫЕ только для поля-константы (ИСТИНА), оптимизатор платформы и SQL-сервера может свернуть выборку до скалярного агрегата над внешним соединением. В результате возвращается одна строка со значением NULL.
  2. Рассинхронизация таблиц итогов регистра: Если база создавалась переносом данных или в ней выполнялись масштабные загрузки остатков, таблицы рассчитанных итогов регистра ТоварыОрганизаций могут содержать фантомные записи или некорректные даты среза итогов.
  3. Влияние RLS (ограничений на уровне записей): При выполнении запроса в пользовательском сеансе без привилегированного режима шаблоны RLS динамически встраиваются во вложенные подзапросы, что также может искажать агрегацию и порождать строки со значениями NULL.

Способ 1. Восстановление итогов через тестирование и исправление (Рекомендуемый)

Если проблема вызвана повреждением или рассинхронизацией таблиц итогов после переноса данных, наиболее надежный способ — выполнить обслуживание базы средствами конфигуратора. Разберем порядок действий:

  1. Создадим резервную копию информационной базы.
  2. Откроем базу в режиме Конфигуратор.
  3. Перейдем в главное меню: Администрирование — Тестирование и исправление...
  4. Установим следующие флаги проверок:
    1. Реиндексация таблиц информационной базы;
    2. Реструктуризация таблиц информационной базы;
    3. Пересчет итогов.
  5. Нажмем кнопку Выполнить и дождемся завершения процесса.

После реиндексации и пересчета итогов виртуальные таблицы регистра ТоварыОрганизаций перестроятся на стороне СУБД, и ложное срабатывание запроса закрытия месяца будет устранено.

Способ 2. Программный пересчет итогов проблемного регистра

Если база данных имеет большой объем и полное тестирование займет слишком много времени, выполним пересчет итогов точечно только для регистра ТоварыОрганизаций во внешней обработке или через консоль кода:


// Устанавливаем признак использования итогов и принудительно пересчитываем их
РегистрыНакопления.ТоварыОрганизаций.УстановитьИспользованиеИтогов(Истина);
РегистрыНакопления.ТоварыОрганизаций.ПересчитатьИтоги();

Данный метод обновляет системные таблицы итогов конкретного регистра без блокировки всей информационной базы.

Способ 3. Оптимизация текста запроса (для разработчиков)

Если мы дорабатываем алгоритмы проверки остатков в собственных отчетах или процедурах закрытия, откажемся от вложенных запросов с константами в пользу прямой и однозначной конструкции выборки:


ВЫБРАТЬ ПЕРВЫЕ 1
    1 КАК ЕстьОтрицательныеОстатки
ИЗ
    РегистрНакопления.ТоварыОрганизаций.Остатки(
        &Граница,
        Организация В (&МассивОрганизаций)
    ) КАК Остатки
ГДЕ
    Остатки.КоличествоОстаток < 0

В коде встроенного языка 1С проверку факта наличия отрицательных остатков следует выполнять через проверку выборки, не завязываясь на значение поля:


УстановитьПривилегированныйРежим(Истина);

РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
    // Обнаружены отрицательные остатки
КонецЕсли;

Использование метода УстановитьПривилегированныйРежим(Истина) позволяет исключить влияние ограничений прав доступа (RLS), гарантируя корректность возвращаемого результата.

← На главную