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