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