При решении задач свертки базы, универсального поиска, переноса данных или массовой перепроверки документов часто возникает необходимость получить список всех существующих в системе документов с фильтром по дате, пометке удаления или признаку проведения. В базе данных каждый вид документа хранится в отдельной таблице, поэтому простого обращения к единой таблице всех документов «из коробки» не предусмотрено.
Разберем основные подходы к решению этой задачи: от динамического построения пакетных запросов до перебора через менеджеры объектов, а также рассмотрим важные технические нюансы оптимизации производительности.
Писать вручную объединение десятков или сотен видов документов неэффективно и трудоемко. Лучший способ — программно обойти коллекцию Метаданные.Документы и сформировать текст запроса с конструкциями ОБЪЕДИНИТЬ ВСЕ.
Проанализируем ключевую особенность: не у всех документов в конфигурации разрешено проведение. Если попытаться обратиться к полю Проведен у документа, для которого свойство Проведение установлено в значение Запретить, система выдаст синтаксическую ошибку. Поэтому перед добавлением условия в запрос необходимо программно проверять метаданные каждого объекта.
Посмотрим на практический пример формирования запроса, помещающего отобранные данные во временную таблицу:
ТекстЗапроса = "";
Первый = Истина;
Для Каждого МДДокумент Из Метаданные.Документы Цикл
// Пропускаем документы, не сохраняемые в базе данных
Если НЕ МДДокумент.Таблица Тогда
Продолжить;
КонецЕсли;
Если Не Первый Тогда
ТекстЗапроса = ТекстЗапроса + Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС;
КонецЕсли;
ТекстЗапроса = ТекстЗапроса +
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.Дата КАК Дата,
| Док.ПометкаУдаления КАК ПометкаУдаления
|ИЗ
| Документ." + МДДокумент.Имя + " КАК Док
|ГДЕ
| Док.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| И Док.ПометкаУдаления = &ПометкаУдаления";
// Проверяем возможность проведения документа
Если МДДокумент.Проведение = Метаданные.СвойстваОбъектов.Проведение.Разрешить Тогда
ТекстЗапроса = ТекстЗапроса + "
| И Док.Проведен = &Проведен";
КонецЕсли;
Первый = Ложь;
КонецЦикла;
// Оборачиваем во временную таблицу
ТекстЗапроса = "ВЫБРАТЬ
| ВТ.Ссылка КАК Ссылка,
| ВТ.Дата КАК Дата,
| ВТ.ПометкаУдаления КАК ПометкаУдаления
|ПОМЕСТИТЬ ВТ_ВсеДокументы
|ИЗ
| (" + ТекстЗапроса + ") КАК ВТ
|;
|ВЫБРАТЬ * ИЗ ВТ_ВсеДокументы КАК ВТ_ВсеДокументы";
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ТекущаяДата()));
Запрос.УстановитьПараметр("ПометкаУдаления", Ложь);
Запрос.УстановитьПараметр("Проведен", Истина);
РезультатЗапроса = Запрос.Выполнить();
Если требуется обработать документы без динамической склейки сложного SQL-запроса, можно последовательно перебрать документы через объектную модель, выгрузить ссылки в ТаблицаЗначений и передать ее параметром во временную таблицу запроса.
Рассмотрим, как устроен данный механизм:
ТЗДокументов = Новый ТаблицаЗначений;
ТЗДокументов.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("ДокументСсылка"));
ТЗДокументов.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
Для Каждого ДокМенеджер Из Документы Цикл
Выборка = ДокМенеджер.Выбрать(НачалоДня(ТекущаяДата()), КонецДня(ТекущаяДата()));
Пока Выборка.Следующий() Цикл
// Проверяем нужные условия
Если Выборка.ПометкаУдаления = Ложь И Выборка.Проведен = Истина Тогда
НоваяСтрока = ТЗДокументов.Добавить();
НоваяСтрока.Ссылка = Выборка.Ссылка;
НоваяСтрока.Дата = Выборка.Дата;
КонецЕсли;
КонецЦикла;
КонецЦикла;
// Помещаем полученную таблицу в запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Ссылка КАК Ссылка,
| ТЗ.Дата КАК Дата
|ПОМЕСТИТЬ ВТ_Документы
|ИЗ
| &ТЗДокументов КАК ТЗ";
Запрос.УстановитьПараметр("ТЗДокументов", ТЗДокументов);
Результат = Запрос.Выполнить();
Важно: этот способ значительно медленнее запросов к СУБД на больших объемах данных, так как считывает объекты в оперативную память сервера приложений построчно, однако он прост в отладке и не зависит от ограничений СУБД на количество объединений в одном запросе.
При работе с выборками по всей базе данных необходимо учитывать следующие архитектурные моменты:
ОБЪЕДИНИТЬ ВСЕ вызовет долгую компиляцию плана запроса СУБД или ошибку переполнения стека. Рекомендуется разбивать обход метаданных на пакеты по 20–30 документов и последовательно наполнять временную таблицу через ПОМЕСТИТЬ.Дата всегда проиндексирован. Указание границ периода в секции ГДЕ обязательно, иначе СУБД выполнит полное сканирование (Table Scan) всех таблиц за всю историю базы.