При работе в среде 1С:Предприятие 7.7, особенно при использовании SQL-версии базы данных, программисты часто сталкиваются с загадочным поведением системы: сумма, рассчитанная функцией Сумма() в тексте запроса, не совпадает с результатом метода Итог() по табличной части того же самого документа. Разница может составлять как копейки, так и значительные суммы (сотни и тысячи рублей), при этом «рандомный» характер ошибки только усложняет диагностику.
В этой статье мы подробно разберем, почему возникают такие расхождения, проанализируем технические особенности взаимодействия 1С 7.7 с SQL-сервером и рассмотрим шаги, которые позволят получать гарантированно точные данные.
Одной из самых неочевидных проблем 1С 7.7 является специфика трансляции внутреннего языка запросов в T-SQL. Рассмотрим ситуацию: когда мы пишем функцию Сумма(СуммаД), SQL-сервер выполняет агрегацию числовых данных. В некоторых случаях, из-за особенностей типов данных (Numeric/Decimal) и их обработки в компонентах доступа к данным 1С, происходит неявное приведение типов или потеря точности при суммировании вещественных чисел.
Проанализируем пример «костыля», который, как ни странно, является стандартным отраслевым решением для этой платформы: умножение на 100. Суть метода заключается в том, чтобы перевести вычисления из области дробных чисел в область целых чисел (копеек).
Посмотрим на пример трансформации кода функции в запросе:
// Было (проблемный вариант):
|Функция СуммаСумма = Сумма(СуммаД);
// Стало (надежный вариант):
|Функция СуммаСумма = Сумма(СуммаД * 100);
После получения результата запроса нам остается лишь разделить итоговое значение на 100. Этот простой прием позволяет избежать накопленной ошибки округления при обработке больших выборок данных на стороне SQL-сервера.
Рассмотрим код автора темы. Одной из критических ошибок является избыточное использование обращения через ТекущийДокумент внутри описания переменных запроса. Выясним причину, почему это плохо.
Когда вы пишете СуммаД = Документ.ЗаявкаПокупателя.ТекущийДокумент.Сумма;, система вынуждена выполнять дополнительные соединения (JOIN) таблиц или даже обращаться к объектам метаданных неоптимальным способом. В 1С 7.7 для получения данных из табличной части документа в запросе следует обращаться к реквизиту напрямую через вид документа.
Проанализируем правильный способ описания переменных:
ТекстЗапроса = "
|Период с НачДата по КонДата;
|ОбрабатыватьДокументы Все;
|ТекДок = Документ.ЗаявкаПокупателя.ТекущийДокумент;
|Фирма = Документ.ЗаявкаПокупателя.Фирма;
|Контр = Документ.ЗаявкаПокупателя.Контрагент;
// Обращаемся напрямую к реквизиту Сумма, без ТекущийДокумент
|СуммаД = Документ.ЗаявкаПокупателя.Сумма;
|Функция ИтогСумма = Сумма(СуммаД);
|Группировка ТекДок;
|";
Использование ТекущийДокумент оправдано только для переменной группировки (чтобы получить ссылку на сам объект), но не для получения значений его реквизитов или данных табличной части.
Посмотрим на условие, которое использовал автор: Условие (ТекДок.ПометкаУдаления() = 0);. Это крайне неэффективный метод фильтрации в запросах 1С 7.7. Рассмотрим, почему так делать не стоит:
ПометкаУдаления() внутри запроса заставляет систему для каждой найденной записи выполнять проверку флага в журнале документов.Разберем, как правильно реализовать этот фильтр с помощью встроенных инструкций языка запросов:
ТекстЗапроса = "
|Период с НачДата по КонДата;
|ОбрабатыватьДокументы Все;
|Обрабатывать НеПомеченныеНаУдаление; // Вот штатный и быстрый фильтр
|...
|";
Если вам нужно обрабатывать только проведенные документы, используйте ОбрабатыватьДокументы Проведенные;. Если же нужны и проведенные, и не проведенные, но не удаленные — связка ОбрабатыватьДокументы Все; и Обрабатывать НеПомеченныеНаУдаление; является эталонной.
Иногда причина расхождений кроется не в коде запроса, а в самих данных. Выясним ситуацию на примере реквизитов табличной части. В 1С 7.7 существует особенность: если в строке документа не заполнено какое-либо ключевое поле (например, единица измерения или цена), метод Итог() объекта «Документ» может все равно учитывать эту строку, так как он работает с данными в памяти или через объектный интерфейс. Однако Запрос, работающий через SQL-выборку, может проигнорировать такую строку или выдать NULL, что исказит общую сумму.
Рекомендация: Если вы обнаружили проблемный документ, проверьте его табличную часть на наличие «пустых» или некорректно заполненных строк. Пересохранение документа (или программная перезапись) часто помогает восстановить целостность индексов и записей в таблицах SQL.
Для решения проблемы расхождения сумм в запросах 1С 7.7 мы рекомендуем придерживаться следующего алгоритма:
Сумма(Реквизит * 100), а результат делите на 100. Это нивелирует ошибки округления SQL.ТекущийДокумент.Реквизит на прямой доступ Документ.ВидДокумента.Реквизит.Условие(ПометкаУдаления()=0) на системную инструкцию Обрабатывать НеПомеченныеНаУдаление.Группировка Документ; работает стабильнее, чем использование произвольного алиаса.Соблюдение этих правил не только обеспечит точность ваших отчетов, но и значительно ускорит их работу за счет правильного построения SQL-запросов и снижения нагрузки на сервер.