При работе с прямыми запросами к базе данных 1С 7.7 через компоненту 1CPP и класс ODBCRecordSet программисты часто сталкиваются с проблемой фильтрации реквизитов, имеющих обобщенный тип Справочник (без указания конкретного вида). Стандартные методы укладывания списка объектов во временную таблицу в таких случаях перестают работать «из коробки». Разберем подробно причины этой проблемы и изучим эффективные способы ее решения.
Проанализируем структуру хранения данных в SQL-версии 1С 7.7. Когда реквизит имеет строго определенный тип (например, Справочник.Контрагенты), система выделяет под него 9 символов — это уникальный идентификатор элемента (ID). Однако, если тип реквизита определен как просто Справочник, структура поля меняется.
В этом случае поле в таблице SQL занимает 13 символов:
Когда мы используем стандартный метод УложитьСписокОбъектов, компонента помещает во временную таблицу только 9-символьные идентификаторы элементов. При сравнении в SQL-запросе через оператор IN происходит попытка сопоставить 13-символьную строку из таблицы базы данных с 9-символьной строкой из временной таблицы. Очевидно, что такое сравнение всегда будет ложным, даже если сами объекты совпадают.
Рассмотрим специализированный метод компоненты 1CPP, предназначенный как раз для работы с реквизитами неопределенного вида. Метод УложитьСписокОбъектов13 автоматически формирует полные 13-символьные идентификаторы для объектов, попадающих во временную таблицу.
Однако здесь есть нюанс, который мы должны учитывать: данный метод не поддерживает автоматическую «развертку» групп справочника. Если в исходном списке 1С содержатся группы, SQL-запрос не найдет элементы внутри них. Чтобы обойти это ограничение, проанализируем следующий алгоритм действий:
Выгрузить(СписокОбъектов, 1, 1), чтобы рекурсивно добавить все элементы из групп в новый список.УложитьСписокОбъектов13.Посмотрим на пример кода:
СписокЭлементов = СоздатьОбъект("СписокЗначений");
// Предположим, СписокГруппИЭлементов заполнен пользователем
СписокГруппИЭлементов.Выгрузить(СписокЭлементов, 1, 1);
RS = СоздатьОбъект("ODBCRecordSet");
RS.УложитьСписокОбъектов13(СписокЭлементов, "#ВремТаблица", "ВидСправочника");
ТекстЗапроса = "
|SELECT *
|FROM $Документ.ПриходнаяНакладная AS Док
|WHERE $Док.Субконто1 IN (SELECT Val FROM #ВремТаблица)";
Если по каким-то причинам использование метода «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), что значительно замедлит работу на больших базах.
Проанализируем ситуацию, когда в одном реквизите могут находиться объекты разных типов (например, и «Контрагенты», и «Склады»), и нам нужно отфильтровать документы по списку, содержащему и те, и другие. В этом случае нам поможет объединение результатов (UNION ALL) внутри подзапроса.
Посмотрим на структуру такого решения:
// Предположим, у нас две временные таблицы для разных типов
ТекстЗапроса = "
|SELECT *
|FROM $Документ.Расходная AS Док
|WHERE Док.$РеквизитНеопределенный IN (
| SELECT $ВидСправочника36.Контрагенты + Val FROM #ВремКонтрагенты
| UNION ALL
| SELECT $ВидСправочника36.Склады + Val FROM #ВремСклады
|)";
Рассмотрим несколько практических советов, которые помогут избежать ошибок при реализации данных методов:
$ВидСправочника36.Имя.Выяснив структуру хранения данных и освоив методы работы с 13-символьными идентификаторами, мы можем строить быстрые и надежные отчеты на платформе 1С 7.7, корректно обрабатывая реквизиты с неопределенным типом данных.