В практике разработчика 1С нередко возникает ситуация, когда документ в информационной базе имеет статус Проведен, однако фактически не совершает никаких записей в соответствующие регистры накопления. Это может привести к расхождениям в отчетах, неверным остаткам на складах или ошибкам в расчете себестоимости. Рассмотрим подробнее, почему возникают такие ситуации и как правильно составить запрос для их выявления.
Прежде чем переходить к написанию запроса, проанализируем, по каким причинам документ может оказаться в таком «подвешенном» состоянии:
ОбработкаПроведения может отсутствовать логика записи в набор записей регистра при определенных условиях.Записать(РежимЗаписиДокумента.Проведение) в коде.Самый эффективный и распространенный способ найти документы без движений — использование механизма ЛЕВОЕ СОЕДИНЕНИЕ между таблицей документов и физической таблицей регистра накопления. Проанализируем логику: мы выбираем все документы, удовлетворяющие условиям, и пытаемся сопоставить их с записями в регистре по полю Регистратор. Если в регистре записей нет, поле из таблицы регистра вернет значение NULL.
Рассмотрим пример запроса для документа ПриходныйОрдерНаТовары и регистра СвободныеОстатки:
ВЫБРАТЬ
ПриходныйОрдерНаТовары.Ссылка КАК Ссылка,
ПриходныйОрдерНаТовары.Дата КАК Дата
ИЗ
Документ.ПриходныйОрдерНаТовары КАК ПриходныйОрдерНаТовары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.СвободныеОстатки КАК СвободныеОстатки
ПО ПриходныйОрдерНаТовары.Ссылка = СвободныеОстатки.Регистратор
ГДЕ
ПриходныйОрдерНаТовары.Проведен = ИСТИНА
И ПриходныйОрдерНаТовары.СкладскаяОперация = &СкладскаяОперация
И СвободныеОстатки.Регистратор ЕСТЬ NULL
УПОРЯДОЧИТЬ ПО
Дата
В данном примере мы используем проверку СвободныеОстатки.Регистратор ЕСТЬ NULL в блоке ГДЕ. Это позволяет отсечь все документы, которые смогли найти хотя бы одну строку в регистре. Для быстрого выполнения и отладки таких запросов в управляемом интерфейсе можно использовать консоль запросов с поддержкой пакетных запросов.
Обратите внимание на важный нюанс, который часто становится причиной ошибок при написании подобных запросов. Разберем ситуацию: если мы поместим условие СкладскаяОперация = &СкладскаяОперация внутрь условия соединения (после слова ПО), запрос может отработать некорректно.
Правило: Условия, ограничивающие выборку самих документов (дата, проведен, тип операции), должны находиться в секции ГДЕ. Условия в секции ПО определяют только правила сопоставления таблиц. Если наложить фильтр по складу в условии соединения, то документы с «другим» складом не соединятся с регистром и тоже попадут в результат как имеющие NULL, хотя движения у них могут быть.
Если в вашей базе данных миллионы записей в регистре накопления, прямое левое соединение с физической таблицей может выполняться очень долго. Выясним причину: серверу БД приходится сканировать огромную таблицу регистра для сопоставления с каждым документом. В этом случае мы рекомендуем использовать временные таблицы для предварительного отбора данных.
Посмотрим на шаги оптимизации:
Пример оптимизированного кода:
// Шаг 1: Собираем все документы, которые СДЕЛАЛИ движения
ВЫБРАТЬ РАЗЛИЧНЫЕ
СвободныеОстатки.Регистратор КАК Регистратор
ПОМЕСТИТЬ ВТ_ДокументыСДвижениями
ИЗ
РегистрНакопления.СвободныеОстатки КАК СвободныеОстатки
ГДЕ
СвободныеОстатки.Период МЕЖДУ &НачалоПериода И &КонецПериода
;
////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Ищем документы, которых НЕТ в этой временной таблице
ВЫБРАТЬ
Док.Ссылка КАК Ссылка
ИЗ
Документ.ПриходныйОрдерНаТовары КАК Док
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДокументыСДвижениями КАК Движения
ПО Док.Ссылка = Движения.Регистратор
ГДЕ
Док.Проведен = ИСТИНА
И Док.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И Движения.Регистратор ЕСТЬ NULL
Такой подход значительно снижает нагрузку на дисковую подсистему, так как временная таблица ВТ_ДокументыСДвижениями будет содержать только ссылки, а поиск по ним в оперативной памяти происходит гораздо быстрее. При написании более сложных пакетов запросов для автоматического управления временем жизни таких таблиц удобно применять обработку для оптимизации временных таблиц.
Проанализируем альтернативный вариант с использованием конструкции В (ВЫБРАТЬ...). Хотя язык запросов 1С позволяет использовать вложенные подзапросы с отрицанием, на уровне СУБД (особенно MS SQL Server) такие конструкции часто работают медленнее, чем ЛЕВОЕ СОЕДИНЕНИЕ. Это связано с тем, что оптимизатор запросов SQL лучше обрабатывает соединения (JOIN), чем операции исключения из множества. Поэтому использование ЕСТЬ NULL является промышленным стандартом разработки на платформе 1С.
После того как вы выявили список «проблемных» документов, необходимо принять решение о способе исправления данных. Рассмотрим основные варианты:
Записать(РежимЗаписиДокумента.Проведение)) восстановит записи в регистрах.Движения.ИмяРегистра.Записать = ИСТИНА.Использование вышеописанных методов позволит вам эффективно поддерживать чистоту и актуальность данных в любой конфигурации на платформе 1С:Предприятие.