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