Этюд: Ручное восстановление таблицы из бэкапа

У меня случилась неприятность – в личной базе данных полетела колонка с текстом в таблице, где хранились сообщения, накопленные в почтовой переписке за 5 лет. После запуска CHDBFL текст сообщений исчез во всей таблице, в каждой ее записи!

Всё остальное не пострадало. Я решил не восстанавливать базу из архива (с потерей последних нескольких дней), а ручками перенести тексты из бэкапа. Об этом этюд. Он будет полезен в плане демонстрации ручных навыков работы с XML.

Структура данных

Сообщения хранились в документе пимСообщение. В нем было всего две колонки для текста сообщения:

Соответственно, нужно было восстановить только два этих поля.

Сообщений было 500 000. Стандартная ВыгрузкаЗагрузкаДанныхЧерезXML от 1С не справилась, написала – не хватает памяти.

Перенести нужно было быстро. Поэтому решил писать свой код, а не муторно искать готовое решение.

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

Код выполняю непосредственно в консоли кода.

Выгрузка в XML

Код по выгрузке выглядит так:

З = Новый Запрос("ВЫБРАТЬ Ссылка, ШаблонСообщения ИЗ Документ.пимСообщение");

Выборка = З.Выполнить().Выбрать();

ИмяФайла = "R:\1.xml";

 

ЗаписьXML = Новый ЗаписьXML();

ЗаписьXML.ОткрытьФайл(ИмяФайла, "windows-1251");

ЗаписьXML.ЗаписатьОбъявлениеXML();

ЗаписьXML.ЗаписатьНачалоЭлемента("items");

 

Сч = 0;

Пока Выборка.Следующий() Цикл

       Сч = Сч + 1;

       Если Сч % 100 = 0 Тогда

             Состояние("" + Сч + " из " + Выборка.Количество());

       КонецЕсли;

      

       ЗаписьXML.ЗаписатьНачалоЭлемента("item");

      

       ЗаписьXML.ЗаписатьНачалоЭлемента("link");

       ЗаписьXML.ЗаписатьТекст(ЗначениеВСтрокуВнутр(Выборка.Ссылка));

       ЗаписьXML.ЗаписатьКонецЭлемента();

      

       Попытка

             Значение = Выборка.Ссылка.Текст;

             ЗаписьXML.ЗаписатьНачалоЭлемента("text");

             ЗаписьXML.ЗаписатьТекст(Значение);

             ЗаписьXML.ЗаписатьКонецЭлемента();

       Исключение

       КонецПопытки;

      

       Попытка

             Значение = ЗначениеВСтрокуВнутр(Выборка.Ссылка.ТекстХран);

             ЗаписьXML.ЗаписатьНачалоЭлемента("texth");

             ЗаписьXML.ЗаписатьТекст(Значение);

             ЗаписьXML.ЗаписатьКонецЭлемента();

       Исключение

       КонецПопытки;

      

       ЗаписьXML.ЗаписатьКонецЭлемента();

      

КонецЦикла;

ЗаписьXML.ЗаписатьКонецЭлемента();

 

ЗаписьXML.Закрыть();

Загрузка из XML

Код по загрузке выглядит так:

 

ЧтениеXML = Новый ЧтениеXML();

ИмяФайла = "R:\1.xml";

ЧтениеXML.ОткрытьФайл(ИмяФайла);

 

Сч = 0;

ТекОбъект = Неопределено;

Пока ЧтениеXML.Прочитать() Цикл

       Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда

             //Сообщить("" + ЧтениеXML.ТипУзла  + ":" + ЧтениеXML.Имя );

             Если ЧтениеXML.Имя = "item" Тогда

                    Сч = Сч + 1;

                    Если Сч % 100 = 0 Тогда

                           Состояние(Сч);

                    КонецЕсли;

                    Если ТекОбъект <> Неопределено Тогда

                           ТекОбъект.Записать();

                           //Сообщить(ТекОбъект);

                    КонецЕсли;

                    ТекОбъект = Неопределено;

 

             ИначеЕсли ЧтениеXML.Имя = "link" Тогда

                    ЧтениеXML.Прочитать();

                    ТекСсылка = ЗначениеИзСтрокиВнутр(ЧтениеXML.Значение);

                    ТекОбъект = ТекСсылка.ПолучитьОБъект();

             ИначеЕсли ЧтениеXML.Имя = "text" Тогда

                    ЧтениеXML.Прочитать();

                    Если ТекОбъект <> Неопределено Тогда

                           ТекОбъект.Текст = ЧтениеXML.Значение;

                    КонецЕсли;

             ИначеЕсли ЧтениеXML.Имя = "texth" Тогда

                    ЧтениеXML.Прочитать();

                    ТекЗначение = ЗначениеИзСтрокиВнутр(ЧтениеXML.Значение);

                    //Сообщить("" + ТекОбъект + "" + ТекЗначение);

                    Если ТекОбъект <> Неопределено Тогда

                           ТекОбъект.ТекстХран = ТекЗначение;

                    КонецЕсли;

             КонецЕсли;

       КонецЕсли;

КонецЦикла;

 

Если ТекОбъект <> Неопределено Тогда

       ТекОбъект.Записать();

       //Сообщить(ТекОбъект);

КонецЕсли;

 

ЧтениеXML.Закрыть();