Как универсально получить структуру хранения базы данных и типы реквизитов в 1С

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

При выгрузке или анализе структуры хранения информационной базы 1С часто возникает необходимость сопоставить физические имена таблиц и полей в СУБД с соответствующими объектами метаданных, их реквизитами и типами данных. Функция ПолучитьСтруктуруХраненияБазыДанных() позволяет узнать имена таблиц и колонок в SQL, однако сопоставление их с типами данных реквизитов требует обращения к дереву метаданных.

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

Суть проблемы и исходный подход

В начальной реализации разбора структуры данных часто используют явную проверку имени объекта через условные операторы Если ... ИначеЕсли:


Если стр.Метаданные = "Документ.СудебноеРешениеПоИску" Тогда
    мета = Метаданные.Документы.СудебноеРешениеПоИску.Реквизиты;
ИначеЕсли стр.Метаданные = "Документ.СудебноеРешениеПоИску.ТабличнаяЧасть.ДополнительныеРеквизиты" Тогда
    мета = Метаданные.Документы.СудебноеРешениеПоИску.ТабличныеЧасти.ДополнительныеРеквизиты.Реквизиты;
КонецЕсли;

Такой код жестко привязан к конкретному документу и его табличным частям. При изменении состава метаданных или попытке обработать другой объект алгоритм приходится переписывать вручную.

Решение через динамическое обращение к коллекциям

Один из способов сделать код универсальным — использовать разбор строки с полным именем метаданных с помощью СтрРазделить() и индексированный доступ к свойствам объектов метаданных в квадратных скобках:


ИмяМетаДляОбращения = "Документ." + ОбъектСохраняемый;
МассивИменМетаданных = Новый Массив();
МассивИменМетаданных.Добавить(ИмяМетаДляОбращения);

т = ПолучитьСтруктуруХраненияБазыДанных(МассивИменМетаданных);

Для Каждого стр Из т Цикл
    Область.Параметры.Индекс = стр.ИмяТаблицыХранения;
    Область.Параметры.Реквизит = стр.Метаданные;
    
    Строка = СтрРазделить(стр.Метаданные, ".");
    Попытка
        // Обращение к реквизитам табличной части
        мета = Метаданные[ВидОбъекта][Строка[1]].ТабличныеЧасти[Строка[3]].Реквизиты;
    Исключение
        // Обращение к реквизитам шапки объекта
        мета = Метаданные[ВидОбъекта][Строка[1]].Реквизиты;
    КонецПопытки;

    Для Каждого стр1 Из стр.Поля Цикл
        Область.Параметры.Индекс = стр1.ИмяПоляХранения;
        Область.Параметры.Реквизит = стр1.ИмяПоля;
        Область.Параметры.Тип = "";

        Для Каждого мет Из мета Цикл
            Если мет.Имя = стр1.ИмяПоля Тогда
                Область.Параметры.Тип = мет.Тип;
                Прервать;
            КонецЕсли;
        КонецЦикла;

        ТабДокумент.Вывести(Область);
    КонецЦикла;
КонецЦикла;

Этот подход решает задачу динамического получения реквизитов, однако конструкция Попытка ... Исключение и разбор строки вручную усложняют код.

Оптимальный способ: использование Метаданные.НайтиПоПолномуИмени

В платформе 1С предусмотрен встроенный метод Метаданные.НайтиПоПолномуИмени(). Он принимает полное имя объекта метаданных (которое возвращает свойство стр.Метаданные из функции ПолучитьСтруктуруХраненияБазыДанных()) и сразу возвращает соответствующий объект метаданных без необходимости разбирать строку.

При сопоставлении полей структуры хранения с реквизитами важно учитывать следующие нюансы:

  1. Стандартные реквизиты: В коллекции .Реквизиты хранятся только пользовательские реквизиты. Системные поля (такие как Ссылка, Дата, Номер, НомерСтроки) содержатся в коллекции .СтандартныеРеквизиты.
  2. Составные типы данных: В колонке ИмяПоляХранения находятся физические имена колонок СУБД (которые для составных типов могут содержать суффиксы вроде _TYPE или _RRef). Поиск реквизита в метаданных необходимо выполнять по значению из колонки ИмяПоля.

Проанализируем итоговый оптимизированный алгоритм:


МассивИменМетаданных = Новый Массив();
МассивИменМетаданных.Добавить("Документ.СудебноеРешениеПоИску");

СтруктураХранения = ПолучитьСтруктуруХраненияБазыДанных(МассивИменМетаданных);

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

    Для Каждого СтрПоле Из СтрТаблица.Поля Цикл
        Область.Параметры.Индекс = СтрПоле.ИмяПоляХранения;
        Область.Параметры.Реквизит = СтрПоле.ИмяПоля;
        Область.Параметры.Тип = "";

        // 1. Поиск среди пользовательских реквизитов
        ИскомыйРеквизит = ОбъектМетаданных.Реквизиты.Найти(СтрПоле.ИмяПоля);
        
        // 2. Поиск среди стандартных реквизитов, если не найден в пользовательских
        Если ИскомыйРеквизит = Неопределено И ОбъектМетаданных.СтандартныеРеквизиты <> Неопределено Тогда
            ИскомыйРеквизит = ОбъектМетаданных.СтандартныеРеквизиты.Найти(СтрПоле.ИмяПоля);
        КонецЕсли;

        Если ИскомыйРеквизит <> Неопределено Тогда
            Область.Параметры.Тип = ИскомыйРеквизит.Тип;
        КонецЕсли;

        ТабДокумент.Вывести(Область);
    КонецЦикла;
    
КонецЦикла;

Сочетание Метаданные.НайтиПоПолномуИмени() и метода .Найти() позволяет отказаться от громоздких циклов поиска по именам и делает обработку структуры данных быстрой и лаконичной.

← На главную