Почему суммы в запросе отличаются от итога по документу в 1С 7.7 и как это исправить

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

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

В этой статье мы подробно разберем, почему возникают такие расхождения, проанализируем технические особенности взаимодействия 1С 7.7 с SQL-сервером и рассмотрим шаги, которые позволят получать гарантированно точные данные.

Разберем основную причину: ошибка округления в SQL-движке

Одной из самых неочевидных проблем 1С 7.7 является специфика трансляции внутреннего языка запросов в T-SQL. Рассмотрим ситуацию: когда мы пишем функцию Сумма(СуммаД), SQL-сервер выполняет агрегацию числовых данных. В некоторых случаях, из-за особенностей типов данных (Numeric/Decimal) и их обработки в компонентах доступа к данным 1С, происходит неявное приведение типов или потеря точности при суммировании вещественных чисел.

Проанализируем пример «костыля», который, как ни странно, является стандартным отраслевым решением для этой платформы: умножение на 100. Суть метода заключается в том, чтобы перевести вычисления из области дробных чисел в область целых чисел (копеек).

Посмотрим на пример трансформации кода функции в запросе:


// Было (проблемный вариант):
|Функция СуммаСумма = Сумма(СуммаД);

// Стало (надежный вариант):
|Функция СуммаСумма = Сумма(СуммаД * 100);

После получения результата запроса нам остается лишь разделить итоговое значение на 100. Этот простой прием позволяет избежать накопленной ошибки округления при обработке больших выборок данных на стороне SQL-сервера.

Оптимизируем структуру запроса и обращение к реквизитам

Рассмотрим код автора темы. Одной из критических ошибок является избыточное использование обращения через ТекущийДокумент внутри описания переменных запроса. Выясним причину, почему это плохо.

Когда вы пишете СуммаД = Документ.ЗаявкаПокупателя.ТекущийДокумент.Сумма;, система вынуждена выполнять дополнительные соединения (JOIN) таблиц или даже обращаться к объектам метаданных неоптимальным способом. В 1С 7.7 для получения данных из табличной части документа в запросе следует обращаться к реквизиту напрямую через вид документа.

Проанализируем правильный способ описания переменных:


ТекстЗапроса = "
|Период с НачДата по КонДата;
|ОбрабатыватьДокументы Все;
|ТекДок   = Документ.ЗаявкаПокупателя.ТекущийДокумент;
|Фирма    = Документ.ЗаявкаПокупателя.Фирма;
|Контр    = Документ.ЗаявкаПокупателя.Контрагент;
// Обращаемся напрямую к реквизиту Сумма, без ТекущийДокумент
|СуммаД   = Документ.ЗаявкаПокупателя.Сумма; 
|Функция ИтогСумма = Сумма(СуммаД);
|Группировка ТекДок;
|";

Использование ТекущийДокумент оправдано только для переменной группировки (чтобы получить ссылку на сам объект), но не для получения значений его реквизитов или данных табличной части.

Правильная фильтрация помеченных на удаление документов

Посмотрим на условие, которое использовал автор: Условие (ТекДок.ПометкаУдаления() = 0);. Это крайне неэффективный метод фильтрации в запросах 1С 7.7. Рассмотрим, почему так делать не стоит:

  1. Метод ПометкаУдаления() внутри запроса заставляет систему для каждой найденной записи выполнять проверку флага в журнале документов.
  2. Это препятствует выполнению запроса целиком на стороне SQL-сервера (происходит «прокачка» лишних данных на клиентскую часть для фильтрации).
  3. Это может приводить к расхождениям, если состояние документа изменилось прямо во время выполнения запроса.

Разберем, как правильно реализовать этот фильтр с помощью встроенных инструкций языка запросов:


ТекстЗапроса = "
|Период с НачДата по КонДата;
|ОбрабатыватьДокументы Все;
|Обрабатывать НеПомеченныеНаУдаление; // Вот штатный и быстрый фильтр
|...
|";

Если вам нужно обрабатывать только проведенные документы, используйте ОбрабатыватьДокументы Проведенные;. Если же нужны и проведенные, и не проведенные, но не удаленные — связка ОбрабатыватьДокументы Все; и Обрабатывать НеПомеченныеНаУдаление; является эталонной.

Влияние целостности данных в табличной части

Иногда причина расхождений кроется не в коде запроса, а в самих данных. Выясним ситуацию на примере реквизитов табличной части. В 1С 7.7 существует особенность: если в строке документа не заполнено какое-либо ключевое поле (например, единица измерения или цена), метод Итог() объекта «Документ» может все равно учитывать эту строку, так как он работает с данными в памяти или через объектный интерфейс. Однако Запрос, работающий через SQL-выборку, может проигнорировать такую строку или выдать NULL, что исказит общую сумму.

Рекомендация: Если вы обнаружили проблемный документ, проверьте его табличную часть на наличие «пустых» или некорректно заполненных строк. Пересохранение документа (или программная перезапись) часто помогает восстановить целостность индексов и записей в таблицах SQL.

Итоги и выводы

Для решения проблемы расхождения сумм в запросах 1С 7.7 мы рекомендуем придерживаться следующего алгоритма:

  1. Примените умножение на 100: В функции запроса используйте Сумма(Реквизит * 100), а результат делите на 100. Это нивелирует ошибки округления SQL.
  2. Уберите лишние обращения: Замените конструкции ТекущийДокумент.Реквизит на прямой доступ Документ.ВидДокумента.Реквизит.
  3. Используйте штатные фильтры: Замените Условие(ПометкаУдаления()=0) на системную инструкцию Обрабатывать НеПомеченныеНаУдаление.
  4. Проверьте группировки: Иногда использование стандартной группировки Группировка Документ; работает стабильнее, чем использование произвольного алиаса.

Соблюдение этих правил не только обеспечит точность ваших отчетов, но и значительно ускорит их работу за счет правильного построения SQL-запросов и снижения нагрузки на сервер.

← На главную