31 янв. 2016 г.

Просмотр структуры базы данных

Казалось бы, зачем изобретать очередной "велосипед", когда можно найти среди готовых решений что-то более менее подходящее. Но, как говорится, дьявол в деталях. В моем случае среди найденных вариантов не было ни одного под управляемые формы с иерархическим просмотром структуры метаданных. Стоит отметить, что мой первоначальный вариант заполнял таблицы полей и индексов на сервере для выделенной строки. Но при просмотре структуры конфигураций 1С: ERP задержка при передаче контекста формы с клиента на сервер и обратно составила около 2 секунд. Поэтому для обеспечения комфорта просмотра заполнение таблиц полей и индексов было перенесено на клиент, данные для заполнения формировались в неконтекстных серверных процедурах.




Код обработки:

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
 
 ОбновитьТаблицы();
 
КонецПроцедуры

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

&НаСервере
Процедура ДобавитьМетаданныеОбъекта(ИндексыСтрок, СтрокаХранения)

 Родитель = "Метаданные";
 
 Если ПустаяСтрока(СтрокаХранения.Метаданные) Тогда
  МетаданныеОбъекта = СтрокаХранения.Назначение;
 Иначе
  МетаданныеОбъекта = СтрЗаменить(СтрокаХранения.Метаданные, ".", Символы.ПС);
  УровеньОбъекта = СтрЧислоСтрок(МетаданныеОбъекта);
  ОбъектМетаданных = СтрПолучитьСтроку(МетаданныеОбъекта, УровеньОбъекта);
  Если НЕ ОбъектМетаданных = СтрокаХранения.Назначение Тогда
   МетаданныеОбъекта = МетаданныеОбъекта + Символы.ПС + СтрокаХранения.Назначение;
  КонецЕсли;
 КонецЕсли;
 
 УровеньОбъекта = СтрЧислоСтрок(МетаданныеОбъекта);
 
 Для Сч = 1 По УровеньОбъекта Цикл
  ОбъектМетаданных  = СтрПолучитьСтроку(МетаданныеОбъекта, Сч);
  МетаданныеПуть = Родитель + "." + ОбъектМетаданных;
  ИдентификаторСтроки = ИндексыСтрок[МетаданныеПуть];
  Если ИдентификаторСтроки = Неопределено Тогда
   ИдентификаторРодитель = ИндексыСтрок[Родитель];
   СтрокаРодитель = СтруктураИБ.НайтиПоИдентификатору(ИдентификаторРодитель);
   СтрокаОбъект = СтрокаРодитель.ПолучитьЭлементы().Добавить();
   СтрокаОбъект.Объект = ОбъектМетаданных;
   ИндексыСтрок.Вставить(МетаданныеПуть, СтрокаОбъект.ПолучитьИдентификатор());
  Иначе
   СтрокаОбъект = СтруктураИБ.НайтиПоИдентификатору(ИдентификаторСтроки);
  КонецЕсли;
  Родитель = МетаданныеПуть;
 КонецЦикла;
 
 СтрокаРодитель = СтрокаОбъект.ПолучитьРодителя();
 ЗаполнитьЗначенияСвойств(СтрокаОбъект, СтрокаХранения);
 СтрокаРодитель.ИмяТаблицыХранения = СтрокаРодитель.ИмяТаблицыХранения + ?(ПустаяСтрока(СтрокаРодитель.ИмяТаблицыХранения), "", "; ") + СтрокаОбъект.ИмяТаблицыХранения;
 СтруктураПолей = Новый Структура("Поля, Индексы", СтрокаХранения.Поля, СтрокаХранения.Индексы);
 СтрокаОбъект.Адрес = ПоместитьВоВременноеХранилище(СтруктураПолей, УникальныйИдентификатор);

КонецПроцедуры // ДобавитьМетаданныеОбъекта()

&НаКлиенте
Процедура СтруктураИБПриАктивизацииСтроки(Элемент)
 
 ОбновитьСвязанныеПоля();
 
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьСвязанныеПоля()

 ПоляКоличество = 0;
 ИндексыКоличество = 0;
 ТекущиеДанные = Элементы.СтруктураИБ.ТекущиеДанные;
 ОбновитьСвязанныеПоляКлиент(ТекущиеДанные.Адрес);

КонецПроцедуры // ОбновитьСвязанныеПоля()

&НаКлиенте
Процедура ОбновитьСвязанныеПоляКлиент(АдресПолей)

 ДеревоИндексов.ПолучитьЭлементы().Очистить();
 ПоляОбъекта.Очистить();
 Если ЗначениеЗаполнено(АдресПолей) Тогда
  СтруктураЗаполнения = ПолучитьСтруктуруЗаполнения(АдресПолей);
  ЗаполнитьПоляКлиент(СтруктураЗаполнения.Поля);
  ЗаполнитьИндексыКлиент(СтруктураЗаполнения.Индексы);
 КонецЕсли;

КонецПроцедуры // ОбновитьСвязанныеПоляКлиент()

&НаКлиенте
Процедура ЗаполнитьПоляКлиент(ДанныеПолей)

 Для Каждого СтрокаДанных Из ДанныеПолей Цикл
  ЗаполнитьЗначенияСвойств(ПоляОбъекта.Добавить(), СтрокаДанных);
 КонецЦикла;
 ПоляКоличество = ПоляОбъекта.Количество();

КонецПроцедуры // ЗаполнитьПоляКлиент()

&НаКлиенте
Процедура ЗаполнитьИндексыКлиент(ДанныеИндексов)

 ЭлементыДерева = ДеревоИндексов.ПолучитьЭлементы();
 Для Каждого ДанныеИндекса Из ДанныеИндексов Цикл
  ЭлементИндекс = ЭлементыДерева.Добавить();
  ЭлементИндекс.ИмяПоляХранения = ДанныеИндекса.ИмяПоляХранения;
  ПоляИндекса = ЭлементИндекс.ПолучитьЭлементы();
  Для Каждого СтрокаДанных Из ДанныеИндекса.СоставИндекса Цикл
   ЗаполнитьЗначенияСвойств(ПоляИндекса.Добавить(), СтрокаДанных);
  КонецЦикла;
  Элементы.ДеревоИндексов.Развернуть(ЭлементИндекс.ПолучитьИдентификатор());
 КонецЦикла;
 ИндексыКоличество = ЭлементыДерева.Количество();

КонецПроцедуры // ЗаполнитьПоляКлиент()

&НаСервереБезКонтекста
Функция ПолучитьСтруктуруЗаполнения(ИдентификаторСтруктуры)

 СтруктураЗаполнения = Новый Структура("Поля, Индексы", Новый Массив, Новый Массив);
 ТаблицыСтроки = ПолучитьИзВременногоХранилища(ИдентификаторСтруктуры);
 Если НЕ ТаблицыСтроки = Неопределено Тогда
  ЗаполнитьПоля(СтруктураЗаполнения.Поля, ТаблицыСтроки.Поля);
  ЗаполнитьИндексы(СтруктураЗаполнения.Индексы, ТаблицыСтроки.Индексы);
 КонецЕсли;
 
 Возврат СтруктураЗаполнения;

КонецФункции // ПолучитьСтруктуруЗаполнения()

&НаСервереБезКонтекста
Процедура ЗаполнитьПоля(ХранилищеПолей, ДанныеПолей)

 Для Каждого СтрокаДанных Из ДанныеПолей Цикл
  ХранилищеПолей.Добавить(ЗаполнитьСоставСтроки(СтрокаДанных));
 КонецЦикла;

КонецПроцедуры // ЗаполнитьПоля()

&НаСервереБезКонтекста
Процедура ЗаполнитьИндексы(ХранилищеПолей, ДанныеИндексов)

 Для Каждого ИндексОбъекта Из ДанныеИндексов Цикл
  СоставИндекса = Новый Массив;
  СтруктураИндекса = Новый Структура("ИмяПоляХранения, СоставИндекса", ИндексОбъекта.ИмяИндексаХранения, СоставИндекса);
  Для Каждого ПолеИндекса Из ИндексОбъекта.Поля Цикл
   СоставИндекса.Добавить(ЗаполнитьСоставСтроки(ПолеИндекса));
  КонецЦикла;
  ХранилищеПолей.Добавить(СтруктураИндекса);
 КонецЦикла;

КонецПроцедуры // ЗаполнитьИндексы()

&НаСервереБезКонтекста
Функция ЗаполнитьСоставСтроки(ДанныеСтруктуры)

 СтрокаСтруктуры = Новый Структура("ИмяПоляХранения, ИмяПоля, Метаданные");
 ЗаполнитьЗначенияСвойств(СтрокаСтруктуры, ДанныеСтруктуры);
 Возврат СтрокаСтруктуры;

КонецФункции // ЗаполнитьСоставСтроки()

СтруктураИБ_УФ.epf

7 комментариев:

  1. Спасибо, классная вещь!

    ОтветитьУдалить
  2. Спасибо! А содержимое самих таблиц нельзя просматривать (на чтение, конечно)?

    ОтветитьУдалить
    Ответы
    1. Метод ПолучитьСтруктуруХраненияБазыДанных возвращает только таблицу значений с описаниями структуры таблиц на стороне СУБД, без данных. При необходимости можете сделать доработку получения данных по имени таблицы, например через COM.

      Удалить
  3. Спасибо тебе хороший человек!

    ОтветитьУдалить
  4. Огромная благодарность! Крайне полезная вещь!

    ОтветитьУдалить