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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок разработчики периодически сталкиваются с задачей: на форме документа отображается таблица с данными, заполненная оператором, и требуется сделать выборку документов, в которых эта таблица заполнена, а также извлечь сами строки. Однако попытка обратиться к такой таблице через язык запросов 1С приводит в тупик. Разберем архитектурные особенности платформы 1С, выясним причину такого поведения и рассмотрим, как правильно решить эту задачу.

Почему нельзя выбрать данные из элемента или реквизита формы через запрос

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

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

Как найти реальное место хранения данных в конфигураторе

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

  1. Откроем модуль формы документа в конфигураторе и выполним поиск по имени реквизита формы (имени таблицы).
  2. Проанализируем серверные процедуры чтения данных: ПриЧтенииНаСервере, ПриСозданииНаСервере (для управляемых форм) или ПриОткрытии (для обычных форм). В этих процедурах находится вызов алгоритма заполнения таблицы формы.
  3. Изучим серверные процедуры записи: ПередЗаписьюНаСервере, ПриЗаписиНаСервере в модуле формы, а также события ПередЗаписью и ПриЗаписи в модуле самого объекта документа. Здесь находится код сохранения содержимого таблицы значений в базу данных.

На практике разработчики 1С чаще всего используют три способа сохранения нестандартных таблиц, не являющихся штатными табличными частями документа. Рассмотрим каждый из них и способы выборки данных.

Вариант 1. Таблица хранится в реквизите документа с типом ХранилищеЗначений

Нередко таблица значений помещается в специальный реквизит документа с типом ХранилищеЗначений. В момент записи таблица сжимается и сохраняется в бинарном виде, а при чтении — извлекается методом Получить().

Ограничение платформы: реквизиты с типом ХранилищеЗначений нельзя фильтровать или анализировать непосредственно в теле запроса. Проверка на NULL или НЕ NULL в запросе не дает гарантии, так как внутри хранилища может быть записана пустая таблица значений (с нулевым количеством строк).

Для выборки таких документов используем комбинированный подход: отбираем документы запросом, а затем проверяем содержимое хранилища программно в цикле обхода выборки. Посмотрим на пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Док.Ссылка КАК Ссылка,
    |	Док.ХранилищеТаблицы КАК ХранилищеТаблицы
    |ИЗ
    |	Документ.РеализацияТоваровУслуг КАК Док
    |ГДЕ
    |	Док.Проведен = ИСТИНА";

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    // Извлекаем значение напрямую из выборки без получения объекта в память
    Таблица = Выборка.ХранилищеТаблицы.Получить();
    
    Если ТипЗнч(Таблица) = Тип("ТаблицаЗначений") И Таблица.Количество() > 0 Тогда
        // Документ содержит заполненную таблицу, обрабатываем строки
        Для Каждого Стр Из Таблица Цикл
            // Обработка данных строки: Стр.ИмяКолонки
        КонецЦикла;
    КонецЕсли;
КонецЦикла;

Обратим внимание: обращение к реквизиту через поле выборки запроса позволяет избежать тяжелой операции Ссылка.ПолучитьОбъект(), что существенно экономит оперативную память и ускоряет обработку большого массива документов.

Вариант 2. Данные хранятся в подчиненном регистре сведений

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

Это наиболее производительный вариант, поскольку он позволяет получить и документы, и строки таблицы за один запрос с помощью соединения таблиц. Рассмотрим пример запроса, если документ связан с регистром по измерению Документ:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Док.Ссылка КАК ДокументСсылка,
    |	Док.Номер КАК Номер,
    |	Док.Дата КАК Дата,
    |	Рег.Номенклатура КАК Номенклатура,
    |	Рег.Количество КАК Количество,
    |	Рег.Цена КАК Цена
    |ИЗ
    |	Документ.РеализацияТоваровУслуг КАК Док
    |	ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеДанныеДокументов КАК Рег
    |		ПО Док.Ссылка = Рег.Документ";

РезультатЗапроса = Запрос.Выполнить();

Использование конструкции ВНУТРЕННЕЕ СОЕДИНЕНИЕ автоматически исключает из результата запроса те документы, по которым в регистре сведений нет ни одной записи.

Вариант 3. Хранение в виде сериализованной строки (XML или JSON)

Иногда таблицу сериализуют в строку неограниченной длины методами ЗначениеВXML(), ЗначениеВСтрокуВнутр() или формируют JSON-структуру и записывают в текстовый реквизит документа. Строки неограниченной длины также не подлежат детальному разбору средствами языка запросов, поэтому их извлечение выполняется аналогично варианту с хранилищем: мы выбираем строковое поле запросом и программно восстанавливаем таблицу значений (например, через ЗначениеИзXML()) для последующей проверки на заполненность.

← На главную