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