При проведении аудита информационной базы, подготовке к переносу данных, свертке или оптимизации конфигурации часто возникает задача: быстро определить, какие именно виды документов фактически создавались и используются пользователями, а какие остаются пустыми. Коллекция метаданных Метаданные.Документы содержит полный список всех объектов, заложенных разработчиками, однако она не дает ответа на вопрос, есть ли в таблицах реальные записи.
Разберем подробнее практические способы решения этой задачи с помощью встроенного языка 1С, выясним причины разницы в быстродействии между методами и проанализируем нюансы работы с правами доступа.
Самый простой и надежный подход заключается в обходе коллекции Метаданные.Документы в цикле. Для каждого вида документа формируется запрос, проверяющий наличие хотя бы одной записи в таблице базы данных. Конструкция ВЫБРАТЬ ПЕРВЫЕ 1 указывает СУБД прекратить поиск сразу после нахождения первой подходящей строки, что обеспечивает высокое быстродействие.
Посмотрим на пример реализации данного алгоритма:
СписокИспользуемых = Новый Массив;
Запрос = Новый Запрос;
Для Каждого МетаданныеДокумента Из Метаданные.Документы Цикл
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Док.Ссылка КАК Ссылка
|ИЗ
| Документ." + МетаданныеДокумента.Имя + " КАК Док";
Результат = Запрос.Выполнить();
Если НЕ Результат.Пустой() Тогда
СписокИспользуемых.Добавить(МетаданныеДокумента.Имя);
Сообщить("Используется: " + МетаданныеДокумента.Имя);
КонецЕсли;
КонецЦикла;
Разберем логику работы этого кода:
Метаданные.Документы.ИЗ текста запроса.Результат.Пустой() позволяет мгновенно проверить, вернулись ли данные, не тратя время и оперативную память на выгрузку записей в таблицу значений.Чтобы избежать выполнения сотен отдельных запросов к СУБД, текст запроса можно скомпоновать динамически, объединив выборки через ОБЪЕДИНИТЬ ВСЕ. Чтобы в результирующей таблице сразу видеть имя документа, добавим строковый литерал с именем метаданных в каждую ветку выборки.
Рассмотрим пример формирования такого запроса:
ТекстЗапроса = "";
Запрос = Новый Запрос;
Для Каждого МетаданныеДокумента Из Метаданные.Документы Цикл
Если НЕ ПустаяСтрока(ТекстЗапроса) Тогда
ТекстЗапроса = ТекстЗапроса + Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС;
КонецЕсли;
ТекстЗапроса = ТекстЗапроса +
"ВЫБРАТЬ ПЕРВЫЕ 1
| """ + МетаданныеДокумента.Имя + """ КАК ИмяДокумента
|ИЗ
| Документ." + МетаданныеДокумента.Имя + " КАК Док";
КонецЦикла;
Запрос.Текст = ТекстЗапроса;
ТаблицаИспользуемых = Запрос.Выполнить().Выгрузить();
Для Каждого Стр Из ТаблицаИспользуемых Цикл
Сообщить(Стр.ИмяДокумента);
КонецЦикла;
Важное ограничение: В масштабных конфигурациях (например, ERP или Комплексная автоматизация) количество документов исчисляется сотнями. Формирование одного гигантского запроса с сотнями предложений ОБЪЕДИНИТЬ ВСЕ может вызвать перегрузку синтаксического анализатора 1С или сбой оптимизатора СУБД. В таких системах рекомендуется объединять запросы пачками по 30–50 штук либо использовать пакетные запросы с точкой с запятой и методом Запрос.ВыполнитьПакет().
В платформе 1С существует возможность проверки наличия данных без написания текста запроса — с помощью метода менеджера Выбрать().
Посмотрим на пример с объектным обходом:
МассивИспользуемых = Новый Массив;
Для Каждого МетаданныеДокумента Из Метаданные.Документы Цикл
Выборка = Документы[МетаданныеДокумента.Имя].Выбрать();
Если Выборка.Следующий() Тогда
МассивИспользуемых.Добавить(МетаданныеДокумента.Имя);
КонецЕсли;
КонецЦикла;
Проанализируем производительность этого метода: вызов Выбрать() без параметров неявно генерирует тяжелый запрос к базе данных, который пытается подготовить курсор для чтения всей таблицы со всеми реквизитами. Даже если мы вызываем Выборка.Следующий() всего один раз, платформа выполняет избыточную работу на стороне сервера. Запрос с конструкцией ВЫБРАТЬ ПЕРВЫЕ 1 отрабатывает в разы быстрее, особенно на базах с большим объемом данных.
На практике пустой или тестовый документ, помеченный на удаление, не всегда означает реальное использование функционала. Чтобы отсечь ошибочные или неактуальные данные, в текст запроса следует добавить условия отбора:
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Док.Ссылка КАК Ссылка
|ИЗ
| Документ." + МетаданныеДокумента.Имя + " КАК Док
|ГДЕ
| НЕ Док.ПометкаУдаления
| И Док.Проведен
| И Док.Дата >= &ДатаНачала";
Такой отбор гарантирует, что в результирующий список попадут только те виды документов, которые действительно проводились пользователями начиная с указанной даты.
Если запускать обработку под учетной записью с ограниченными правами, обращение к документу, на который у пользователя нет прав чтения, вызовет системное исключение «Нарушение прав доступа» или приведет к искажению результатов из-за механизма RLS (ограничения доступа на уровне записей).
Для корректного сбора данных рекомендуется выполнять проверку в привилегированном режиме с помощью метода УстановитьПривилегированныйРежим(Истина) либо предварительно проверять наличие прав у пользователя функцией ПравоДоступа("Чтение", МетаданныеДокумента).