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

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

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

Суть проблемы: подмена условий в SQL-запросе

Представим типичную ситуацию: у нас есть регистр накопления «Отгрузка», в котором присутствуют три измерения типа Справочник.Контрагенты — это могут быть «ЮридическоеЛицо», «Контрагент» и «Комитент». При попытке построить универсальный отчет с динамическим формированием текста запроса мы сталкиваемся с тем, что условие, наложенное на реквизит одного измерения, внезапно применяется к другому, либо результат запроса вовсе оказывается пустым.

Проанализируем поведение системы: если измерение, по которому наложено условие, стоит в группировке первым — всё работает корректно. Если же оно перемещается в конец списка группировок, данные исчезают. Это происходит из-за того, как библиотека bkend.dll (ядро платформы 7.7 для работы с базами данных) транслирует внутренний язык запросов 1С в инструкции T-SQL.

Анализ через SQL Profiler: механизм ошибки

Чтобы понять, что происходит «под капотом», рассмотрим вывод SQL Profiler. Когда мы пишем условие в 1С:

Условие (КонтрагентКодСистемы > гУсл4);

Платформа должна выполнить операцию LEFT OUTER JOIN таблицы движений регистра с таблицей справочника контрагентов. Если в запросе участвуют несколько полей одного типа, 1С должна создать несколько соединений с разными псевдонимами (aliases), например, SC74, SC74_1, SC74_2. Однако из-за бага в трансляторе 7.7, система часто путает эти псевдонимы в блоке WHERE.

Рассмотрим пример кода запроса, вызывающего ошибку:


Период с ФормДатаНач по ФормДатаКон;
ЮридическоеЛицо = Регистр.Отгрузка.ЮридическоеЛицо;
Контрагент = Регистр.Отгрузка.Контрагент;
КонтрагентКодСистемы = Регистр.Отгрузка.Контрагент.КодСистемы;
Группировка ЮридическоеЛицо Без Групп;
Группировка Контрагент Без Групп;
Условие (ЮридическоеЛицо = гУсл2);
Условие (КонтрагентКодСистемы > гУсл4);

В этом случае в профайлере мы можем увидеть следующую картину: условие на SC74.SP103 (код системы) накладывается, используя связь от RA1159.SP1162 (Юридическое лицо), хотя в логике 1С мы обращались к измерению Контрагент (SP1274). Происходит «схлопывание» условий: 1С считает, что раз тип справочника один и тот же, можно использовать первый попавшийся JOIN. Это классический Aliasing Bug платформы 7.7.

Влияние встроенных функций на место выполнения запроса

Важным моментом является использование функций типа СокрЛП() (или TrimAll) внутри текста условия. Рассмотрим, как меняется поведение системы при их добавлении:

  1. С использованием функций: Если мы пишем Условие (СокрЛП(КонтрагентКодСистемы) >= СокрЛП(гУсл3)), условие вообще перестает передаваться на сторону SQL Server. Платформа 1С выкачивает все записи из базы во временные файлы на локальном компьютере (или в память) и выполняет фильтрацию средствами bkend.dll. Это решает проблему правильности данных (так как фильтрация идет программно), но катастрофически замедляет работу отчета на больших объемах данных.
  2. Без функций: Условие передается в SQL-запрос в блок WHERE. Это работает быстро, но здесь и проявляется баг с неверными псевдонимами таблиц.

Методы решения проблемы

Рассмотрим по шагам, как можно обойти данное ограничение штатного движка запросов.

Способ 1: Использование промежуточных переменных

Попробуем объявить реквизит справочника как отдельную переменную запроса и использовать именно её. Это не всегда дает 100% гарантию из-за того же бага, но часто помогает транслятору правильно построить связи.


|ПеременнаяКод = Регистр.Отгрузка.Контрагент.КодСистемы;
|Условие (ПеременнаяКод = НужныйКод);

Важно помнить, что порядок объявления переменных в тексте запроса имеет значение. Попробуйте переместить описание реквизита в самое начало списка переменных.

Способ 2: Манипуляция порядком группировок

Как мы выяснили, результат запроса корректен, если «проблемное» измерение идет в группировке первым. Если специфика отчета позволяет, измените иерархию группировок. Если же иерархия должна быть строго определенной, этот метод может не подойти.

Способ 3: Принудительный перенос фильтрации на сторону клиента

Если данных в выборке не слишком много (например, несколько тысяч строк), и скорость не является критическим фактором, можно намеренно использовать функции встроенного языка в условии. Это заставит 1С выполнить фильтрацию корректно, хоть и медленнее.


// Это условие выполнится локально после получения данных из SQL
|Условие (ПустоеЗначение(КонтрагентКодСистемы) = 0);
|Условие (СокрЛП(КонтрагентКодСистемы) = гУсл);

Способ 4: Самый надежный — Прямые запросы (1C++)

Для профессиональной разработки в среде 1С 7.7 + SQL использование штатных запросов («черных») при сложной логике считается плохой практикой именно из-за подобных непредсказуемых багов. Оптимальным решением будет переход на внешнюю компоненту 1C++.

Преимущества прямого запроса:

Проанализируем пример того, как бы выглядел такой запрос (упрощенно) через 1C++:


SELECT
    Reg.SP1274 as [Контрагент $Справочник.Контрагенты],
    ScCont.SP103 as [КодСистемы]
FROM
    RA1159 as Reg
INNER JOIN
    SC74 as ScCont ON ScCont.ID = Reg.SP1274
WHERE
    ScCont.SP103 > :УслКод

Здесь исключена любая двусмысленность, и SQL Server выполнит выборку именно по тому полю, которое вам необходимо.

Резюме

Подведем итоги. Столкнувшись с пустым результатом запроса в 1С 7.7 при использовании условий через две точки, выполните следующие действия:

  1. Проверьте, нет ли в регистре других измерений того же типа.
  2. Посмотрите в SQL Profiler, к какой именно таблице (через какой псевдоним) обращается блок WHERE.
  3. Попробуйте убрать функции (СокрЛП и др.) из условий для проверки корректности формирования JOIN.
  4. Если ошибка сохраняется, измените порядок группировок или переменных.
  5. При первой возможности рассмотрите переход на технологию прямых запросов, так как это единственный способ гарантировать стабильность работы системы на SQL-версии 7.7.

Работа с «черными запросами» требует осторожности и понимания того, что 1С 7.7 — это система, созданная задолго до современных стандартов ORM, и её транслятор запросов имеет свои исторические ограничения.

← На главную