Оценка размера данных базы в МБ, количество записей объектов и дерево метаданных
База данных - Статистика базы данных
Файлы
ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.
Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний
Оформите подписку на компанию для решения рабочих задач
Оформить подписку и скачать решение со скидкойВы можете заказать платную доработку или адаптацию этой разработки под вашу конфигурацию на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.
Обработка читает метаданные и строит дерево объектов конфигурации, а по кнопке считает у каждого объекта количество записей и вес данных. Под объектом видны его табличные части с числом строк, у каждой таблицы — её имя в базе данных. Если встать на строку, внизу появится состав объекта: стандартные и собственные реквизиты, табличные части, измерения и ресурсы с типами. Двойной щелчок открывает форму списка самого объекта. В шапке — имя и версия конфигурации и версия платформы.
Точный вес даёт только СУБД, поэтому на закладке «Размер из СУБД» обработка выдаёт готовый запрос для PostgreSQL или MS SQL. Его результат загружают обратно, и в дереве встают настоящие размеры таблиц вместе с индексами, итогами регистров и таблицами самой платформы. Работает обработка на любой конфигурации, файловой и клиент-серверной, и ничего не меняет — только читает.
Дерево видов метаданных: у каждого вида количество объектов, суммарный вес и число записей; в шапке имя и версия конфигурацииКоличество записей
Обработка обходит коллекции Метаданные[ИмяКоллекции] и у каждого объекта берёт ПолноеИмя(). Выглядит оно как Справочник.Товары и подставляется прямо в текст запроса. Отдельный запрос на каждую таблицу — это запрос в цикле, а стандарты 1С его запрещают. Поэтому таблицы идут пачками по 30, и каждая пачка собирается в один запрос через ОБЪЕДИНИТЬ ВСЕ:
&НаСервере
Процедура ВыполнитьПачку(Пачка, Итог)
Части = Новый Массив;
Для Каждого ПолноеИмя Из Пачка Цикл
Части.Добавить("ВЫБРАТЬ """ + ПолноеИмя + """ КАК Таблица, КОЛИЧЕСТВО(*) КАК Всего ИЗ " + ПолноеИмя);
КонецЦикла;
Попытка
Запрос = Новый Запрос(СтрСоединить(Части, " ОБЪЕДИНИТЬ ВСЕ "));
Выборка = Запрос.Выполнить().Выбрать();
Исключение
ЗаписатьОшибку("Пачка подсчёта записей не прошла, считаем по одной",
ИнформацияОбОшибке(), УровеньЖурналаРегистрации.Предупреждение);
Для Каждого ПолноеИмя Из Пачка Цикл
Итог.Вставить(ПолноеИмя, ПосчитатьЗаписи(ПолноеИмя));
КонецЦикла;
Возврат;
КонецПопытки;
Пока Выборка.Следующий() Цикл
Итог.Вставить(СокрЛП(Выборка.Таблица), Выборка.Всего);
КонецЦикла;
КонецПроцедуры
Если пачка не прошла, её таблицы пересчитываются по одной: виновник остаётся с прочерком, а соседи получают свои числа. Ошибка при этом не пропадает молча — ЗаписатьОшибку кладёт её в журнал регистрации с событием «ОбследованиеБазы».
У констант, перечислений и журналов документов запрос тоже отработает, только толку от результата не будет: у константы в таблице всегда одна строка, перечисление вернёт число своих значений, то есть метаданных, а журнал отдаст документы, которые уже посчитаны в строке «Документы». Поэтому обработка считает записи только у тех видов, которые их действительно хранят: справочники, документы, планы, регистры, последовательности, бизнес-процессы и задачи — без такого отбора итог завышен.
Табличные части справочников, документов, планов, бизнес-процессов и задач уходят в те же пачки. Здесь есть тонкость: язык запросов и структура хранения называют табличную часть по-разному, и обработка держит оба имени:
// Так табличную часть зовёт структура хранения…
Строка.ЧМетаданные = Строка.ЧВладелец + ".ТабличнаяЧасть." + ТабличнаяЧасть.Имя;
// …а так — язык запросов
Строка.ЧЗапрос = Строка.ЧВладелец + "." + ТабличнаяЧасть.Имя;
По первому имени обработка находит таблицу в базе данных, по второму считает строки. Имена таблиц — _Document164, _Document164_VT3638 — отдаёт ПолучитьСтруктуруХраненияБазыДанных(Объекты, Истина): второй параметр просит имена таблиц СУБД, а не имена в терминах 1С. Вызов один на весь вид, и стоит он заметно дороже обычного обхода метаданных, поэтому имена появляются вместе с расчётом, а не при открытии.
Поиск «ЗаказПокупателя»: под документом — его табличные части с числом строк и именами таблиц в базе данныхВес данных объекта
ПолучитьСтруктуруХраненияБазыДанных() отдаёт имена таблиц, поля и индексы, но размера в ней нет. Размер возвращает другой метод — ПолучитьРазмерДанныхБазыДанных(<ОбластьПоиска>, <ВключитьОбъекты>, <ИсключитьОбъекты>). Задуман он для разделённых данных: при ОбластьПоиска = Неопределено платформа считает всю базу. Чтобы получить вес одного объекта, обработка кладёт в область пустой массив, а сам объект передаёт вторым параметром:
&НаСервере
Функция РазмерОбъекта(ОбъектМД)
Область = Новый Массив;
Объекты = Новый Массив;
Объекты.Добавить(ОбъектМД);
Попытка
Возврат ПолучитьРазмерДанныхБазыДанных(Область, Объекты);
Исключение
ЗаписатьОшибку("Размер не получен: " + ОбъектМД.ПолноеИмя(),
ИнформацияОбОшибке(), УровеньЖурналаРегистрации.Ошибка);
Возврат Неопределено;
КонецПопытки;
КонецФункции
Метод возвращает байты и не требует обращения к СУБД, поэтому файловая база считается наравне с серверной. Для объектов без данных — модулей, ролей, подсистем — он отдаёт ноль, а не исключение. Попытка ловит только настоящие ошибки, например нехватку прав, и пишет их в журнал.
Пачкой размеры не посчитать: на массив объектов метод вернёт одну сумму на всех. Поэтому вызов идёт по каждому объекту, и стоит он около 20 мс даже у объекта почти без данных.
Этот размер — только данные, без индексов. Строки табличных частей в него входят, а отдельно размер табличной части метод не отдаёт: на её метаданные он возвращает ноль. Итоги и обороты регистров в размер регистра не входят вовсе — метод считает только основную таблицу, то есть движения. Поэтому под регистром показываются его служебные таблицы — итоги, обороты, итоги по счетам, значения субконто — с именем таблицы в базе данных. Список таблиц отдаёт ПолучитьСтруктуруХраненияБазыДанных(Регистры, Истина). Сколько эти таблицы весят на самом деле, платформа не скажет, а СУБД скажет — об этом ниже.
Расчёт по кнопке
Время расчёта растёт вместе с числом объектов. На демо-базе с 245 объектами это доли секунды, а на базе УНФ с 17 тысячами объектов полный расчёт идёт около двух минут. Считай обработка всё при открытии — форма висела бы прямо на старте. Поэтому при открытии она читает только описание конфигурации: дерево видов и объектов появляется сразу, колонки «Размер» и «Записей» пока пустые, отборы по ним недоступны.
Расчёт запускают две кнопки. «Рассчитать всё» проходит по всей базе, «Рассчитать вид» — только по тому виду, на строке которого стоит курсор. Вторая правит кэш выборочно, через ДанныеФормыВЗначение, поэтому посчитанное раньше по другим видам остаётся на месте.
Виды накапливаются, и пока посчитаны не все, итог показывает, по скольким объектам он сложен. Пустая колонка и прочерк значат разное: прочерк стоит там, где считать нечего, а пустота — там, где ещё не считали.
Посчитан один вид: у справочников числа есть, у остальных пусто, а итог говорит, по скольким объектам он сложенТочный размер из СУБД
Размер от платформы удобен тем, что есть всегда, но он неполный: без индексов, без итогов регистров и без таблиц самой платформы. Всё это знает только СУБД. Сама обработка в неё не ходит — для этого ей нужен был бы пароль от сервера баз данных. Поэтому на закладке «Размер из СУБД» она отдаёт готовый запрос. Его выполняют в pgAdmin, psql или SQL Server Management Studio на базе данных этой информационной базы, а результат загружают обратно файлом или вставляют в поле. Для PostgreSQL запрос такой:
SELECT c.relname || ';' || GREATEST(c.reltuples, 0)::bigint || ';'
|| (pg_total_relation_size(c.oid) - pg_indexes_size(c.oid)) || ';'
|| pg_indexes_size(c.oid) AS line
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname = current_schema()
ORDER BY pg_total_relation_size(c.oid) DESC;
Запрос отдаёт одну колонку line вида «таблица;строк;данные;индексы». Такой результат одинаково копируется из любого инструмента и не зависит от его разделителей и заголовков: строку заголовка и итог «(N rows)» обработка пропускает сама. Данные считаются как полный размер таблицы без индексов, поэтому в них входит и TOAST — там PostgreSQL держит большие значения, например хранилища значений. Число строк берётся из статистики (reltuples): это оценка, а не точный подсчёт. Для MS SQL обработка даёт запрос с той же колонкой по sys.tables, sys.partitions и sys.allocation_units.
Закладка «Размер из СУБД»: запрос для PostgreSQL, загруженный результат и итог по всей базеДальше каждую таблицу нужно отнести к объекту 1С. Это делает та же ПолучитьСтруктуруХраненияБазыДанных, на этот раз по всей конфигурации. Для поиска обработка складывает её в соответствие по имени таблицы:
Итог = Новый Соответствие;
Структура = ПолучитьСтруктуруХраненияБазыДанных(, Истина);
Для Каждого Строка Из Структура Цикл
Итог.Вставить(НРег(Строка.ИмяТаблицыХранения),
Новый Структура("Имя, Метаданные, Назначение", Строка.ИмяТаблицыХранения,
Строка.Метаданные, Строка.Назначение));
КонецЦикла;
Ключ — имя в нижнем регистре: PostgreSQL хранит имена строчными, MS SQL — как в структуре хранения, и сравнение без регистра подходит обоим. По всей конфигурации вызов занимает несколько секунд, поэтому результат читается один раз и лежит во временном хранилище формы. Потом каждая строка результата находит своего владельца:
Описание = Структура[НРег(Таблица.ДТаблица)];
Если Описание = Неопределено И НЕ СтрНачинаетсяС(Таблица.ДТаблица, "_") Тогда
// Таблицы самой платформы: конфигурация, параметры, пользователи
Таблица.ДВладелец = "!";
ИначеЕсли Описание = Неопределено Тогда
// В структуре этой конфигурации такой таблицы нет
Таблица.ДВладелец = "?";
Иначе
// Имя — как его пишет 1С: PostgreSQL отдаёт имена строчными
Таблица.ДТаблица = Описание.Имя;
Таблица.ДМетаданные = Описание.Метаданные;
Таблица.ДНазначение = Описание.Назначение;
Таблица.ДВладелец = ?(ПустаяСтрока(Описание.Метаданные), "!", ВладелецТаблицы(Описание.Метаданные));
КонецЕсли;
У табличной части в колонке Метаданные стоит Документ.ЗаказПокупателя.ТабличнаяЧасть.Запасы, у итогов регистра — имя самого регистра. ВладелецТаблицы берёт первые две части имени, и все таблицы объекта сходятся к нему. Таблиц самой платформы — config, params, v8users, binarydata — в структуре хранения нет, и имя у них без подчёркивания. Таблица с подчёркиванием, которой в структуре нет, принадлежит расширению, удалённому объекту или вообще другой базе.
После разбора колонка «Размер» в дереве превращается в «В СУБД»: данные и индексы всех таблиц объекта. Рядом встаёт колонка «По 1С» с прежним размером от платформы, и разница видна сразу. Под объектом появляются строки «Основная» и «Прочие таблицы», так что строки под ним — основная таблица, табличные части, итоги и мелочь вроде регистрации изменений — в сумме дают его размер. Таблицы платформы и таблицы, которых нет в конфигурации, идут отдельными группами в конце, и итог дерева совпадает с весом всей базы. Отбор «Размер больше» работает по размерам из СУБД, и расчёт для этого не нужен: веса известны сразу после разбора.
Размеры из СУБД в дереве: у регистра «Запасы» основная таблица, итоги и прочие таблицы складываются в его размер, рядом — размер по 1СТот же результат целиком лежит на вкладке «Таблицы по объектам»: вид — объект — все его таблицы, на каждом уровне самое тяжёлое сверху. Здесь данные и индексы идут раздельно, есть число строк и все таблицы объекта, включая основную, регистрацию изменений и настройки хранения итогов. Колонка «По платформе» заполняется, когда вид рассчитан на закладке «Объекты».
«Таблицы по объектам»: вид — объект — таблицы, данные и индексы раздельно, самое тяжёлое сверхуУ файловой базы отдельной СУБД нет, и на закладке остаётся только пояснение: размеры на закладке «Объекты» и есть всё, что можно узнать о весе её данных.
Дерево и состав объекта
Реквизит формы объявлен как ДеревоЗначений, а колонки — подчинённые ему реквизиты. Иерархию таблица рисует сама, свойство Отображение задавать не требуется. Готовое дерево обработка кладёт целиком: ЗначениеВРеквизитФормы(Дерево, "ОбъектыМетаданных") заменяет прежнее содержимое, так что чистить реквизит заранее не нужно.
Состав обработка читает по текущей строке:
&НаСервере
Процедура СобратьРазделы(Состав, ОбъектМД)
ЭтоРегистр = Метаданные.РегистрыСведений.Содержит(ОбъектМД)
ИЛИ Метаданные.РегистрыНакопления.Содержит(ОбъектМД)
ИЛИ Метаданные.РегистрыБухгалтерии.Содержит(ОбъектМД)
ИЛИ Метаданные.РегистрыРасчета.Содержит(ОбъектМД);
ЭтоСсылочный = Метаданные.Справочники.Содержит(ОбъектМД)
ИЛИ Метаданные.Документы.Содержит(ОбъектМД)
ИЛИ Метаданные.ПланыВидовХарактеристик.Содержит(ОбъектМД)
ИЛИ Метаданные.ПланыСчетов.Содержит(ОбъектМД)
ИЛИ Метаданные.ПланыВидовРасчета.Содержит(ОбъектМД)
ИЛИ Метаданные.ПланыОбмена.Содержит(ОбъектМД)
ИЛИ Метаданные.Задачи.Содержит(ОбъектМД)
ИЛИ Метаданные.БизнесПроцессы.Содержит(ОбъектМД);
Если ЭтоРегистр Тогда
ДобавитьРаздел(Состав, ОбъектМД.СтандартныеРеквизиты, "стандартные реквизиты");
ДобавитьРаздел(Состав, ОбъектМД.Измерения, "измерения");
ДобавитьРаздел(Состав, ОбъектМД.Ресурсы, "ресурсы");
ДобавитьРаздел(Состав, ОбъектМД.Реквизиты, "реквизиты");
ИначеЕсли ЭтоСсылочный Тогда
ДобавитьРаздел(Состав, ОбъектМД.СтандартныеРеквизиты, "стандартные реквизиты");
ДобавитьРаздел(Состав, ОбъектМД.Реквизиты, "реквизиты");
ДобавитьТабличныеЧасти(Состав, ОбъектМД.ТабличныеЧасти);
ИначеЕсли Метаданные.Перечисления.Содержит(ОбъектМД) Тогда
ДобавитьРаздел(Состав, ОбъектМД.СтандартныеРеквизиты, "стандартные реквизиты");
ДобавитьРаздел(Состав, ОбъектМД.ЗначенияПеречисления, "значения", Ложь);
ИначеЕсли Метаданные.Обработки.Содержит(ОбъектМД) ИЛИ Метаданные.Отчеты.Содержит(ОбъектМД) Тогда
ДобавитьРаздел(Состав, ОбъектМД.Реквизиты, "реквизиты");
ДобавитьТабличныеЧасти(Состав, ОбъектМД.ТабличныеЧасти);
КонецЕсли;
КонецПроцедуры
Разделы у каждого вида свои: у справочника нет измерений, у регистра — табличных частей. Обращение к отсутствующей коллекции бросает исключение, но ловить его Попыткой значит проверять условие исключением, а исключения нужны для ошибок. На тот же вопрос прямо отвечает метод Содержит у коллекции метаданных. У значений перечисления нет свойства Тип, поэтому они идут без типа — последним параметром Ложь.
Обращаться к объекту метаданных по имени свойства нельзя: Метаданные["Справочники"] работает, а ОбъектМД["Реквизиты"] — нет, только через точку. Поэтому коллекции перечислены в коде явно, а не собраны циклом по именам.
В СтандартныеРеквизиты попадает и то, что реквизитом не является: Ссылка, ВерсияДанных, Предопределенный и ИмяПредопределенныхДанных — эти четыре обработка отсеивает.
Строка(Тип) даёт синоним — «Контрагент». Чтобы получить имя из конфигурации, обработка спрашивает НайтиПоТипу:
Типы = Элемент.Тип.Типы();
Имена = Новый Массив;
Для Каждого Тип Из Типы Цикл
ОбъектТипа = Метаданные.НайтиПоТипу(Тип);
Если ОбъектТипа = Неопределено Тогда
Имена.Добавить(Строка(Тип));
Иначе
Имена.Добавить(ОбъектТипа.ПолноеИмя());
КонецЕсли;
КонецЦикла;
Возврат СтрСоединить(Имена, ", ");
У примитивных типов НайтиПоТипу возвращает Неопределено, и тогда в колонку идёт синоним. Составной тип обработка собирает целиком, через запятую.
Состав выбранного объекта: разделы, имена и типы; ссылочный тип показан именем из конфигурацииДвойной щелчок по объекту открывает его форму списка. Событие таблицы — Выбор, имя формы собирается из полного имени метаданных:
Если НЕ ЕстьФормаСписка(Данные.Вид) Тогда
СообщитьПользователю("У вида «" + Данные.Вид + "» формы списка нет: показывать нечего.");
Возврат;
КонецЕсли;
ОткрытьФорму(Данные.ПолноеИмя + ".ФормаСписка", , ЭтотОбъект);
СообщитьПользователю — процедура обработки на три строки: текст уходит через объект СообщениеПользователю, как требуют стандарты, а не через устаревший Сообщить.
Вид проверяется заранее, и это не перестраховка. Форма списка есть у справочников, документов, журналов, перечислений, планов, регистров, бизнес-процессов и задач; у константы форма значения, у отчёта и обработки — форма объекта. С несуществующим именем формы ОткрытьФорму не выбрасывает исключение, а молча ничего не делает: Попытка здесь бесполезна, обработчик отрабатывает вхолостую, и человек не понимает, нажал он или нет.
Сценарии
- Отбор «Размер больше» с порогом оставляет в дереве только тяжёлые объекты — сразу видно, на что ушло место.
- Размеры из СУБД показывают то, чего платформа не видит: сколько весят итоги регистров, индексы и таблицы самой платформы, например
config, где лежит конфигурация. - Группа «Нет в структуре этой конфигурации» находит таблицы удалённых объектов и расширений, которые занимают место в базе.
- Поиск по синониму или имени открывает состав чужой конфигурации без конфигуратора и без монопольного режима.
- Отбор «Записей больше 0» даёт список объектов с данными — перед переносом или свёрткой.
- Двойной щелчок по найденному объекту открывает его список: нашёлся тяжёлый справочник — сразу видно, что в нём лежит.
На чём проверено
Я проверял на платформе 1С:Предприятие 8.5 (8.5.1.1522) и «Управлении нашей фирмой» 3.0 (3.0.14.143). В веб-клиенте, на серверной базе с PostgreSQL. Файловую базу — на демонстрационной конфигурации, на платформе 8.5 (8.5.1.1150). Запрос для MS SQL я не проверял.
На 8.3 я не проверял. Может, откроется, а может, и нет.
Поставка
ObsledovanieBazy_1_1.epf — внешняя обработка; расширений и внешних компонент она не требует. Форму собирает программно, код открыт и лежит в модуле формы.
Проверено на следующих конфигурациях и релизах:
- Управление нашей фирмой, редакция 3.0, релизы 3.0.14.143
Вступайте в нашу телеграмм-группу Инфостарт