Почему пользовательские функции в запросах 1С 7.7 возвращают 0 на SQL-версии и как это исправить

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Выясним причину: почему функция возвращает 0

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

Рассмотрим механизм работы ВключитьSQL(1):

  1. Трансляция в T-SQL: Платформа пытается перевести запрос со встроенного языка 1С в один SQL-запрос (инструкцию SELECT), который отправляется на сервер Microsoft SQL Server.
  2. Ограничения сервера: SQL-сервер выполняет запрос на своей стороне. Он ничего не знает о функциях, определенных в вашем модуле 1С. Он понимает только стандартные функции языка SQL.
  3. Подстановка нуля: Поскольку оптимизатор 1С не может вставить код вашей функции КолвоПлюс(Колво, 1) в текст SQL-запроса, он просто подставляет значение по умолчанию для числового типа — 0.

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

Решение №1: Отключение SQL-оптимизации

Самый простой, но «болезненный» с точки зрения производительности способ — это принудительное отключение трансляции запроса в SQL. Для этого используется метод ВключитьSQL(0).

Рассмотрим пример кода:


Запрос = СоздатьОбъект("Запрос");
// Принудительно отключаем выполнение на стороне SQL-сервера
Запрос.ВключитьSQL(0); 

ТекстЗапроса = "
|Ном = Регистр.ПартииНаличие.Номенклатура;
|Колво = Регистр.ПартииНаличие.Количество;
|Функция КолвоПлюс = Сумма(КолвоПлюс(Колво, 1));
|Группировка Ном;
|";

Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    // Здесь функция КолвоПлюс будет возвращать корректные значения
КонецЕсли;

Анализ ситуации: При ВключитьSQL(0) платформа будет скачивать данные из SQL-таблиц во временные локальные файлы и обрабатывать их построчно силами собственного движка. Это заставит функцию работать, но скорость выполнения запроса может упасть в 10–20 раз на больших объемах данных. Это решение подходит только для редких отчетов или маленьких справочников.

Решение №2: Вынос логики в Таблицу Значений (Пост-обработка)

Если нам важна скорость, мы можем разделить процесс на два этапа: получение сырых данных быстрым SQL-запросом и последующий расчет в 1С. Проанализируем этот подход:

  1. Выполняем запрос без пользовательской функции, получая только базовые показатели (например, Колво).
  2. Выгружаем результат запроса в объект ТаблицаЗначений с помощью метода Выгрузить(ТЗ).
  3. Перебираем таблицу значений циклом и вычисляем нужный нам показатель программно.

Посмотрим на пример реализации:


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

Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    ТЗ = СоздатьОбъект("ТаблицаЗначений");
    Запрос.Выгрузить(ТЗ);
    
    // Добавляем колонку для нашего вычисляемого значения
    ТЗ.НоваяКолонка("КолвоПлюс");
    
    ТЗ.ВыбратьСтроки();
    Пока ТЗ.ПолучитьСтроку() = 1 Цикл
        // Вызываем функцию 1С для каждой строки таблицы
        ТЗ.КолвоПлюс = КолвоПлюс(ТЗ.КонОстКолво, 1);
    КонецЦикла;
КонецЕсли;

Этот метод сохраняет преимущества высокой скорости SQL-запроса и позволяет использовать любую сложную логику 1С.

Решение №3: Переход на Прямые запросы (1CPP)

Для профессиональной разработки в среде 1С 7.7 на SQL стандартом де-факто является использование внешних компонент, таких как 1CPP.dll. Прямые запросы позволяют писать код непосредственно на языке T-SQL, минуя ограничения штатного построителя 1С.

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

Пример использования прямой выборки через объект ODBCRecordset:


// Требуется наличие 1cpp.dll
ЗапросSQL = СоздатьОбъект("ODBCRecordset");
Текст = "
|SELECT 
|    Рег.ID [Ном $Справочник.Номенклатура],
|    SUM(Рег.Quantity) as [Колво],
|    SUM(Рег.Quantity + 1) as [КолвоПлюс] -- Логика функции на стороне SQL
|FROM 
|    $Регистр.ПартииНаличие as Рег
|GROUP BY 
|    Рег.ID
|";

ТЗ = ЗапросSQL.ВыполнитьИнструкцию(Текст);

Преимущества данного метода:

Подведем итоги

Если ваш запрос в 1С 7.7 на SQL выдает нули вместо результатов функций, помните: штатный запрос 1С не умеет передавать ваши функции серверу SQL. Для решения проблемы мы рекомендуем:

  1. Использовать пост-обработку в Таблице Значений для простых отчетов, где не хочется менять логику запроса.
  2. Освоить прямые запросы (1CPP) для высоконагруженных систем. Это позволит вам не только решить проблему с функциями, но и значительно ускорить работу всей базы данных.
  3. Отключать ВключитьSQL(1) только в крайнем случае, когда объем данных минимален и скорость не критична.
← На главную