Как найти проведенные документы без движений по регистру накопления

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

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

Анализ причин отсутствия движений

Прежде чем переходить к написанию запроса, проанализируем, по каким причинам документ может оказаться в таком «подвешенном» состоянии:

  1. Ошибки в программном коде: В модуле документа в процедуре ОбработкаПроведения может отсутствовать логика записи в набор записей регистра при определенных условиях.
  2. Особенности бизнес-логики: В некоторых конфигурациях (например, в розничной торговле) движения могут «переезжать» из одного документа в другой (например, из Чека ККМ в Отчет о розничных продажах), при этом сам документ-основание остается проведенным.
  3. Сбои при записи: Технические ошибки в момент фиксации транзакции базы данных или некорректное использование метода Записать(РежимЗаписиДокумента.Проведение) в коде.
  4. Ручное вмешательство: Использование внешних обработок для очистки движений без снятия документа с проведения.

Использование левого соединения для поиска пустых документов

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

Рассмотрим пример запроса для документа ПриходныйОрдерНаТовары и регистра СвободныеОстатки:


ВЫБРАТЬ
    ПриходныйОрдерНаТовары.Ссылка КАК Ссылка,
    ПриходныйОрдерНаТовары.Дата КАК Дата
ИЗ
    Документ.ПриходныйОрдерНаТовары КАК ПриходныйОрдерНаТовары
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СвободныеОстатки КАК СвободныеОстатки
        ПО ПриходныйОрдерНаТовары.Ссылка = СвободныеОстатки.Регистратор
ГДЕ
    ПриходныйОрдерНаТовары.Проведен = ИСТИНА
    И ПриходныйОрдерНаТовары.СкладскаяОперация = &СкладскаяОперация
    И СвободныеОстатки.Регистратор ЕСТЬ NULL
УПОРЯДОЧИТЬ ПО
    Дата

В данном примере мы используем проверку СвободныеОстатки.Регистратор ЕСТЬ NULL в блоке ГДЕ. Это позволяет отсечь все документы, которые смогли найти хотя бы одну строку в регистре. Для быстрого выполнения и отладки таких запросов в управляемом интерфейсе можно использовать консоль запросов с поддержкой пакетных запросов.

Важность правильного расположения фильтров

Обратите внимание на важный нюанс, который часто становится причиной ошибок при написании подобных запросов. Разберем ситуацию: если мы поместим условие СкладскаяОперация = &СкладскаяОперация внутрь условия соединения (после слова ПО), запрос может отработать некорректно.

Правило: Условия, ограничивающие выборку самих документов (дата, проведен, тип операции), должны находиться в секции ГДЕ. Условия в секции ПО определяют только правила сопоставления таблиц. Если наложить фильтр по складу в условии соединения, то документы с «другим» складом не соединятся с регистром и тоже попадут в результат как имеющие NULL, хотя движения у них могут быть.

Оптимизация запроса для больших баз данных

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

Посмотрим на шаги оптимизации:

  1. Выбираем уникальные идентификаторы регистраторов из регистра накопления за нужный период во временную таблицу.
  2. Соединяем таблицу документов с этой маленькой временной таблицей.

Пример оптимизированного кода:


// Шаг 1: Собираем все документы, которые СДЕЛАЛИ движения
ВЫБРАТЬ РАЗЛИЧНЫЕ
    СвободныеОстатки.Регистратор КАК Регистратор
ПОМЕСТИТЬ ВТ_ДокументыСДвижениями
ИЗ
    РегистрНакопления.СвободныеОстатки КАК СвободныеОстатки
ГДЕ
    СвободныеОстатки.Период МЕЖДУ &НачалоПериода И &КонецПериода
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Ищем документы, которых НЕТ в этой временной таблице
ВЫБРАТЬ
    Док.Ссылка КАК Ссылка
ИЗ
    Документ.ПриходныйОрдерНаТовары КАК Док
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДокументыСДвижениями КАК Движения
        ПО Док.Ссылка = Движения.Регистратор
ГДЕ
    Док.Проведен = ИСТИНА
    И Док.Дата МЕЖДУ &НачалоПериода И &КонецПериода
    И Движения.Регистратор ЕСТЬ NULL

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

Почему не стоит использовать "ГДЕ НЕ Ссылка В (...)"?

Проанализируем альтернативный вариант с использованием конструкции В (ВЫБРАТЬ...). Хотя язык запросов 1С позволяет использовать вложенные подзапросы с отрицанием, на уровне СУБД (особенно MS SQL Server) такие конструкции часто работают медленнее, чем ЛЕВОЕ СОЕДИНЕНИЕ. Это связано с тем, что оптимизатор запросов SQL лучше обрабатывает соединения (JOIN), чем операции исключения из множества. Поэтому использование ЕСТЬ NULL является промышленным стандартом разработки на платформе 1С.

Что делать с найденными документами?

После того как вы выявили список «проблемных» документов, необходимо принять решение о способе исправления данных. Рассмотрим основные варианты:

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

← На главную