Как правильно задать множественное условие в запросе 1С 7.7 и избежать ошибок фильтрации?

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

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

Посмотрим на исходную попытку написания условия:

ТексктУсловия = "Условие ((Номенклатура.ВидАлкоголя.Код < 500) и (Номенклатура.ВидАлкоголя.Код <> 263));";

На первый взгляд конструкция выглядит логично, однако на практике она либо завершается аварийной остановкой запроса, либо пропускает некорректные записи. Выясним причину такого поведения и разберем по шагам, как построить логику правильно и без потери быстродействия.

Причины возникновения ошибок в запросе

  1. Строковый тип кодов в метаданных. По умолчанию в большинстве конфигураций 1С 7.7 реквизит Код справочников имеет строковый (текстовый) тип. При попытке сравнить строку с числом встроенный механизм запросов выполняет посимвольное (лексикографическое) сравнение. В результате строка "49" оказывается больше, чем "263" или "500", а строка "1000" — меньше, чем "500". Если коды не дополнены ведущими нулями до полной длины, математическое условие диапазона перестает работать.
  2. Ошибки оформления многострочных литералов. В языке 1С 7.7 символ вертикальной черты | является признаком продолжения строкового литерала на новой строке и должен стоять строго в самом начале строки исходного кода модуля. Если поместить его внутрь кавычек или забыть перенос, платформа выдаст синтаксическую ошибку: Неизвестная ошибка '|'.
  3. Множественное разыменование полей через точку. Конструкции вида Номенклатура.ВидАлкоголя.Код создают цепочки неявных соединений таблиц. Стандартный механизм запросов платформы обрабатывает такие конструкции крайне неэффективно, провоцируя избыточные дисковые операции чтения.
  4. Срыв серверной оптимизации в SQL-версии. Любые функции встроенного языка, такие как Число(), Строка() или СокрЛП(), помещенные внутрь секции Условие(...), не могут быть транслированы в нативное предложение WHERE языка SQL. В этом случае платформа считывает всю исходную таблицу с сервера в локальные временные файлы формата DBF на клиентскую машину и выполняет фильтрацию локально, что на больших базах приводит к критическому падению скорости.

Решение 1. Разбиение условий на отдельные строки и приведение типов

Рассмотрим самый быстрый способ исправления синтаксиса в тексте запроса. Платформа 1С 7.7 последовательно объединяет несколько строк Условие(...) логической связкой «И». Кроме того, чтобы корректно сравнить строковый код с числом, воспользуемся функцией явного преобразования Число().

Разберем пример правильного синтаксиса многострочного запроса:


ТекстЗапроса = "
|Период с ВыбНачПериода по ВыбКонПериода;
|Номенклатура = Регистр.ОстаткиТоваров.Номенклатура;
|Количество = Регистр.ОстаткиТоваров.Количество;
|Функция КонОст = КонОст(Количество);
|Группировка Номенклатура без групп;
|Условие (Число(Номенклатура.ВидАлкоголя.Код) < 500);
|Условие (Число(Номенклатура.ВидАлкоголя.Код) <> 263);
|";

Обратите внимание: каждая новая строка тела запроса начинается с символа |. Такой вариант решает проблему неверного сравнения, однако из-за функции Число() фильтрация будет производиться программным интерпретатором на клиенте.

Решение 2. Предварительная выборка в Список Значений (рекомендуемый способ)

Проанализируем архитектуру данных: справочник ВидыАлкоголя, как правило, содержит относительно небольшое количество элементов (от нескольких десятков до сотен), тогда как справочник Номенклатура и регистры могут содержать сотни тысяч записей. Выполнять вызов функций и разыменование через точку для каждой записи регистра или номенклатуры крайне нерационально.

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


СписокНужныхВидов = СоздатьОбъект("СписокЗначений");

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

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

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Предупреждение("Ошибка выполнения запроса!");
    Возврат;
КонецЕсли;

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

Решение 3. Изменение структуры метаданных

Если бизнес-логика учета предполагает, что коды видов продукции всегда являются целыми числами фиксированного диапазона, рассмотрим настройку конфигурации:

  1. Откроем дерево метаданных конфигурации в конфигураторе.
  2. Найдем справочник ВидыАлкоголя, откроем его свойства и изменим Тип кода со «Строковый» на «Числовой».
  3. Если изменение основного кода недопустимо (например, для сохранения ведущих нулей при обмене), добавим в справочник дополнительный числовой реквизит (например, ЧисловойКод) и включим для него свойство Индексировать.

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

Решение 4. Прямые SQL-запросы (для баз на MS SQL Server)

Если система работает под управлением Microsoft SQL Server, а объем номенклатуры исчисляется сотнями тысяч позиций, классический запрос 1С 7.7 часто уступает по скорости прямым запросам к базе данных. В промышленной разработке для решения подобных задач применяют внешнюю компоненту 1С++ и класс ODBCRecordset.

Такой подход позволяет передать явное преобразование типов СУБД (через конструкции CAST или CONVERT), задействовать серверные индексы и полностью исключить генерацию временных локальных DBF-файлов на клиентской станции.

← На главную