Как в 1С 7.7 через ODBCRecordSet проверить реквизит неопределенного вида на вхождение в список объектов

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

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

Почему обычный запрос не находит данные

Проанализируем структуру хранения данных в SQL-версии 1С 7.7. Когда реквизит имеет строго определенный тип (например, Справочник.Контрагенты), система выделяет под него 9 символов — это уникальный идентификатор элемента (ID). Однако, если тип реквизита определен как просто Справочник, структура поля меняется.

В этом случае поле в таблице SQL занимает 13 символов:

  1. Первые 4 символа — это внутренний идентификатор вида справочника в 36-ричной системе исчисления (ID метаданных).
  2. Следующие 9 символов — это идентификатор самого элемента внутри этого справочника.

Когда мы используем стандартный метод УложитьСписокОбъектов, компонента помещает во временную таблицу только 9-символьные идентификаторы элементов. При сравнении в SQL-запросе через оператор IN происходит попытка сопоставить 13-символьную строку из таблицы базы данных с 9-символьной строкой из временной таблицы. Очевидно, что такое сравнение всегда будет ложным, даже если сами объекты совпадают.

Способ 1: Использование метода УложитьСписокОбъектов13

Рассмотрим специализированный метод компоненты 1CPP, предназначенный как раз для работы с реквизитами неопределенного вида. Метод УложитьСписокОбъектов13 автоматически формирует полные 13-символьные идентификаторы для объектов, попадающих во временную таблицу.

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

  1. Сначала подготовим «плоский» список всех элементов справочника на стороне 1С.
  2. Используем метод Выгрузить(СписокОбъектов, 1, 1), чтобы рекурсивно добавить все элементы из групп в новый список.
  3. Передадим полученный список в метод УложитьСписокОбъектов13.

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


СписокЭлементов = СоздатьОбъект("СписокЗначений");
// Предположим, СписокГруппИЭлементов заполнен пользователем
СписокГруппИЭлементов.Выгрузить(СписокЭлементов, 1, 1); 

RS = СоздатьОбъект("ODBCRecordSet");
RS.УложитьСписокОбъектов13(СписокЭлементов, "#ВремТаблица", "ВидСправочника");

ТекстЗапроса = "
|SELECT *
|FROM $Документ.ПриходнаяНакладная AS Док
|WHERE $Док.Субконто1 IN (SELECT Val FROM #ВремТаблица)";

Способ 2: Динамическое формирование префикса в SQL-запросе

Если по каким-то причинам использование метода «13» неудобно или мы работаем с обычным списком объектов (9 символов), мы можем программно сконструировать правильное условие сравнения прямо в тексте запроса. Выясним, как это сделать максимально эффективно с точки зрения производительности.

Для формирования 4-символьного префикса вида справочника в 1CPP существует специальная метаконструкция $ВидСправочника36.ИмяСправочника. Мы можем объединить этот префикс со значениями из обычной временной таблицы.

Разберем пример запроса:


// Укладываем обычный 9-символьный список
RS.УложитьСписокОбъектов(СписокЭлементов, "#ВремТаблица", "Контрагенты");

ТекстЗапроса = "
|SELECT 
|    Жур.IDDoc as [Док $Документ]
|FROM 
|    _1SJOURN as Жур
|INNER JOIN 
|    $Документ.Операция as ДокОперация ON Жур.IDDoc = ДокОперация.IDDoc
|WHERE 
|    ДокОперация.$ОбщийРеквизитНеопределенный IN 
|    (SELECT $ВидСправочника36.Контрагенты + Val FROM #ВремТаблица)";

Важное замечание по производительности: Мы используем конструкцию $ВидСправочника36.Контрагенты + Val в правой части сравнения. Это позволяет SQL Server эффективно использовать индексы по реквизиту неопределенного вида. Если же мы попытаемся обрезать сам реквизит в таблице (например, используя LEFT(Реквизит, 4)), это приведет к полному сканированию таблицы (Table Scan), что значительно замедлит работу на больших базах.

Способ 3: Работа с несколькими видами справочников

Проанализируем ситуацию, когда в одном реквизите могут находиться объекты разных типов (например, и «Контрагенты», и «Склады»), и нам нужно отфильтровать документы по списку, содержащему и те, и другие. В этом случае нам поможет объединение результатов (UNION ALL) внутри подзапроса.

Посмотрим на структуру такого решения:


// Предположим, у нас две временные таблицы для разных типов
ТекстЗапроса = "
|SELECT * 
|FROM $Документ.Расходная AS Док
|WHERE Док.$РеквизитНеопределенный IN (
|    SELECT $ВидСправочника36.Контрагенты + Val FROM #ВремКонтрагенты
|    UNION ALL
|    SELECT $ВидСправочника36.Склады + Val FROM #ВремСклады
|)";

Рекомендации по реализации

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

  1. Всегда используйте метаконструкции: Никогда не прописывайте 4-символьные идентификаторы видов справочников вручную (например, ' 1A'). Эти коды могут измениться при пересоздании базы или загрузке данных. Используйте $ВидСправочника36.Имя.
  2. Обработка пустых значений: Помните, что пустое значение в реквизите неопределенного вида — это не просто пустая строка, а специфическая комбинация пробелов и нулей. Если ваша логика предполагает выбор пустых значений, их также нужно правильно подготовить в списке.
  3. Типизация в запросе: Если вы точно знаете, что в данном поле в конкретном запросе вас интересует только один вид справочника, старайтесь явно указывать его префикс. Это помогает оптимизатору SQL построить наиболее эффективный план выполнения.

Выяснив структуру хранения данных и освоив методы работы с 13-символьными идентификаторами, мы можем строить быстрые и надежные отчеты на платформе 1С 7.7, корректно обрабатывая реквизиты с неопределенным типом данных.

← На главную