Почему в 1С 7.7 условие запроса по списку документов не работает на SQL-версии и как это исправить

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

В практике разработки на платформе 1С:Предприятие 7.7 при использовании MS SQL Server программисты часто сталкиваются со странным поведением стандартных запросов. Ситуация выглядит следующим образом: когда мы передаем в условие запроса СписокЗначений, содержащий один документ, запрос отрабатывает корректно. Однако стоит добавить в этот список второй документ, как выборка становится пустой или возвращает неверные данные. Проанализируем, почему это происходит, и рассмотрим эффективные способы решения этой проблемы.

Разберем причину возникновения ошибки: конфликт типов IDDOC

Чтобы понять суть проблемы, нам необходимо заглянуть «под капот» взаимодействия 1С 7.7 и SQL Server. В базе данных SQL идентификатор документа хранится в поле IDDOC, которое имеет тип Char(9). Это уникальный 9-символьный код документа внутри таблицы.

Однако в самой платформе 1С 7.7 существует понятие «длинного идентификатора», который включает в себя 13 символов: 4 символа идентификатора типа документа и 9 символов самого IDDOC. Когда в СписокЗначений передается несколько элементов, стандартный движок запросов 1С пытается оптимизировать работу через создание временной таблицы в SQL (обычно с префиксом #RGTEMP).

Проблема заключается в том, что при заполнении этой временной таблицы платформа записывает туда полные 13-значные типизированные идентификаторы (например, ' 745 2CYME '), а затем пытается выполнить JOIN этой таблицы с таблицей регистра по полю IDDOC, длина которого всего 9 символов (например, ' 2CYME '). Из-за несовпадения строк (лишние 4 символа типа в начале) условие соединения никогда не выполняется, и запрос возвращает пустой результат. Выясним, как мы можем обойти это ограничение платформенного движка.

Способ 1. Принудительное отключение SQL-оптимизации

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


// Создание объекта типа Запрос
Запрос = СоздатьОбъект("Запрос");
Запрос.ВключитьSQL(0); // Отключаем выполнение на сервере SQL
ТекстЗапроса = 
"//{{ЗАПРОС(Сформировать)
|Период с ВыбНачПериода по ВыбКонПериода;
|ТекущийДокумент = Регистр.Продажи.ТекущийДокумент;
|Номенклатура = Регистр.Продажи.Номенклатура;
|Функция СуммаПрод = Сумма(Регистр.Продажи.Сумма);
|Группировка Номенклатура;
|Условие(ТекущийДокумент в СписокФильтр);
|"//}}ЗАПРОС
;

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

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

Проанализируем более элегантный способ, который позволяет сохранить высокую скорость работы и избежать ошибок типизации. Вместо использования стандартного оператора в, мы можем использовать встроенный метод объекта СписокЗначений непосредственно в условии запроса. Посмотрим, как изменить текст запроса:


// Вместо Условие(ТекущийДокумент в СписокФильтр);
// Используем следующий синтаксис:
ТекстЗапроса = "
|...
|Условие(СписокФильтр.Принадлежит(ТекущийДокумент) = 1);
|...
|";

Когда движок 1С видит в условии метод Принадлежит(), он часто меняет стратегию трансляции. Вместо создания временной таблицы и JOIN, он может перечислить идентификаторы документов через оператор IN ('ID1', 'ID2'...) непосредственно в тексте WHERE. Это позволяет SQL-серверу корректно сравнивать 9-символьные значения и использовать индексы для быстрого поиска.

Способ 3. Поиск значения через НайтиЗначение()

Аналогично предыдущему методу, мы можем использовать функцию поиска по списку. Это также заставляет 1С обрабатывать условие иначе, чем при стандартном операторе в. Рассмотрим пример:


// Использование метода НайтиЗначение для фильтрации
ТекстЗапроса = "
|...
|Условие(СписокФильтр.НайтиЗначение(ТекущийДокумент) <> 0);
|...
|";

Как показывает практика, этот метод работает стабильно и быстро. Он избавляет нас от необходимости типизировать документы в списке вручную, так как механизм НайтиЗначение внутри запроса корректно сопоставляет идентификаторы.

Дополнительные рекомендации по оптимизации

Разберем еще несколько моментов, которые помогут избежать подобных проблем в будущем:

  1. Использование прямых запросов. Если ваш отчет требует сложной фильтрации по документам и работает медленно, рекомендуется использовать внешние компоненты, такие как 1C++. Прямые запросы позволяют писать на чистом T-SQL, где вы полностью контролируете передачу параметров и типизацию полей IDDOC.
  2. Предварительная фильтрация. Всегда старайтесь ограничивать период запроса. Чем меньше данных попадет в предварительную выборку SQL, тем быстрее 1С справится с фильтрацией на уровне условий.
  3. Проверка на пустой список. Перед выполнением запроса обязательно проверьте, не пуст ли ваш СписокФильтр. Если список пустой, 1С может проигнорировать условие и выдать данные по всем документам, что приведет к зависанию системы.

Таким образом, мы выяснили, что основной причиной сбоя в работе запроса 1С 7.7 на SQL является некорректная трансляция списка значений во временную таблицу. Используя методы Принадлежит() или НайтиЗначение() вместо оператора в, мы «обманываем» транслятор и получаем корректный результат с сохранением производительности.

← На главную