Почему запрос в 1С выдает разные результаты в консоли и в обработке?

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

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

Суть проблемы и разбор исходного кода

Рассмотрим конкретную ситуацию. При заполнении данных из регистра бухгалтерии выполнялся следующий запрос к виртуальной таблице ОстаткиИОбороты:


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

Запрос.УстановитьПараметр("Организация", Организация);
Запрос.УстановитьПараметр("Счет", мНастройки.Счет);
Запрос.УстановитьПараметр("Дата", КонецДня(мНастройки.ПериодАвтоматизацииКон));
Запрос.УстановитьПараметр("Документы", мДокументы);

При тестировании запроса выяснилось следующее различие:

  1. В консоли запросов параметр &Документы был передан как значение Неопределено — результаты отчета сформировались верно.
  2. В обработке параметр &Документы передавался как пустой Массив (созданный через Новый Массив) — итоговые суммы в выборке оказались ровно в два раза больше правильных.

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

Проанализируем механику работы платформы 1С при обращении к виртуальным таблицам регистра бухгалтерии или накопления.

1. Метод дополнения ДвиженияИГраницыПериода
Когда в параметрах виртуальной таблицы ОстаткиИОбороты задана периодичность Регистратор и указан метод дополнения ДвиженияИГраницыПериода (или он используется по умолчанию), платформа создает служебные записи для отображения остатков на начало и конец периода. В этих служебных граничных записях поле Регистратор не имеет конкретной ссылки и принимает значение Неопределено (или NULL на уровне базы данных СУБД).

2. Поведение условия НЕ Регистратор В (&Параметр)

  1. Если параметр равен Неопределено, условие преобразуется в НЕ Регистратор В (Неопределено). СУБД отсекает все записи, где поле Регистратор имеет значение Неопределено или NULL. Таким образом, системные граничные записи виртуальной таблицы отфильтровываются, и в итоговый результат попадают только реальные движения.
  2. Если параметр равен пустому массиву, условие преобразуется в выражение НЕ Регистратор В (&ПустойМассив). В SQL-запросе это превращается в условие, которое истинно абсолютно для всех записей (включая записи, где поле Регистратор равно Неопределено). В результат попадают как реальные движения, так и служебные записи остатков на границы периода, что и приводит к суммированию и задвоению итогов.

Решения проблемы

Для предотвращения задвоения данных и некорректного поведения при передаче параметров существуют следующие варианты настройки запроса.

Способ 1. Использование метода дополнения Движения

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


РегистрБухгалтерии.Типовой.ОстаткиИОбороты(
    ,
    &Дата,
    Регистратор,
    Движения, // Явно указываем метод дополнения "Движения"
    Счет = &Счет,
    ,
    Субконто1.ЮрФизЛицо = ЗНАЧЕНИЕ(Перечисление.ЮрФизЛицо.Оператор)
        И НЕ Субконто1.ГоловнойКонтрагент.Ссылка ЕСТЬ NULL
        И Организация = &Организация) КАК ТиповойОстаткиИОбороты

Способ 2. Проверка на заполненность массива в коде 1С

Если условие фильтрации опционально, рекомендуется формировать текст запроса динамически и накладывать условие НЕ Регистратор В (&Документы) только тогда, когда массив действительно содержит элементы:


Если мДокументы.Количество() > 0 Тогда
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "//_УсловиеДокументы", "И НЕ ТиповойОстаткиИОбороты.Регистратор В (&Документы)");
    Запрос.УстановитьПараметр("Документы", мДокументы);
Иначе
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "//_УсловиеДокументы", "");
КонецЕсли;

Способ 3. Использование виртуальной таблицы «Обороты»

Если задача заключается исключительно в получении и исключении оборотов по документам за определенный период (без необходимости анализа остатков), предпочтительнее использовать таблицу РегистрБухгалтерии.Типовой.Обороты. Она не создает граничных записей остатков с пустыми регистраторами и работает значительно быстрее.

← На главную