В практике администрирования «классических» баз данных на платформе 1С:Предприятие 7.7 под управлением СУБД MS SQL Server 2000 (или более поздних версий) иногда случаются критические сбои. Один из наиболее опасных сценариев — повреждение или шифрование файла метаданных 1C.MD вирусом-вымогателем с последующей попыткой администраторов «быстро решить проблему» путем подмены 1C.MD файлом от старого бэкапа или другой конфигурации. В результате такого вмешательства запускается автоматическая реструктуризация таблиц СУБД, ломаются внутренние идентификаторы, затираются данные в ключевых системных таблицах, таких как общий журнал документов _1SJOURN, а сами документы «исчезают» из интерфейса программы.
В данной статье мы подробно рассмотрим, как спасти данные в подобной ситуации, восстановить очищенный журнал документов _1SJOURN, сопоставить таблицы СУБД с правильными метаданными и выполнить корректное слияние уцелевшей информации с прошлыми резервными копиями.
Для понимания логики восстановления необходимо разобраться, как СУБД хранит данные «в семерке». Вся структура базы жестко привязана к словарю данных — файлу 1cv7.dds, который генерируется конфигуратором на основе файла метаданных 1C.MD. Имена физических таблиц в SQL (например, DH2296, SC33, RA412) формируются на базе внутренних 36-ричных идентификаторов объектов метаданных (документов, справочников, регистров).
Когда в систему подсовывается «чужой» или устаревший файл 1C.MD, происходит следующее:
DH*, DT*) и справочников (SC*), чьи идентификаторы не совпали с новым файлом метаданных, очищаются либо пересоздаются._1SJOURN: Общий журнал документов хранит метаданные обо всех документах системы. Изменение состава общих реквизитов или попытка выполнить процедуру «Тестирование и исправление» (ТиИ) при несоответствующем 1C.MD приводит к лавинообразному удалению записей из этой таблицы. Поскольку ссылки на виды документов становятся некорректными, 1С просто очищает строки журнала.В результате мы получаем ситуацию: физические данные (шапки DH* и табличные части DT* документов) в SQL-базе частично или полностью уцелели, но в интерфейсе 1С журналы документов абсолютно пусты, так как запись в _1SJOURN для них отсутствует.
Перед тем как приступать к сложным операциям восстановления, проанализируем состояние системного каталога SQL-сервера. Очень часто при реструктуризации платформы 1С 7.7 не удаляет старые таблицы физически сразу, а переименовывает их, присваивая временные префиксы. Мы можем попытаться найти оригинальные данные непосредственно в схеме базы данных.
Запустим анализатор запросов (Query Analyzer или SQL Server Management Studio) и выполним следующий запрос для поиска таблиц, имена которых начинаются с префиксов O_ или содержат временные маркеры:
SELECT name
FROM sysobjects
WHERE xtype = 'U'
AND (name LIKE 'O[_]%' OR name LIKE '%temp%' OR name LIKE 'tmp%');
Если в результате выполнения запроса вы обнаружите таблицы вроде O__1SJOURN или O_DH2296, вам крупно повезло. Это оригинальные таблицы, сохраненные системой перед началом реструктуризации. Их можно переименовать обратно (предварительно сохранив текущие структуры) и восстановить работоспособность базы за несколько минут.
Если резервных копий таблиц в системном каталоге СУБД не обнаружилось, нам придется реконструировать общий журнал документов _1SJOURN вручную. В 1С 7.7 документы хранятся распределенно:
_1SJOURN.DH[ID] (где ID — идентификатор документа).DT[ID].Наша задача — собрать данные из всех уцелевших таблиц шапок документов DH* и на их основе заново заполнить строки в таблице _1SJOURN. Рассмотрим структуру ключевых полей таблицы _1SJOURN, которые нам необходимо заполнить:
IDDOC — уникальный 9-символьный идентификатор документа (содержится во всех таблицах DH* и DT*).IDDOCDEF — числовой идентификатор вида документа (сопоставляется через файл 1cv7.dds).DATE_TIME_IDDOC — строка из 23 символов, содержащая дату (в формате YYYYMMDD), время записи и 9-символьный IDDOC.DOCNO — номер документа.CLOSED — статус проведения документа (флаг: 1 — проведен, 0 — не проведен).ISMARK — пометка на удаление (1 — удален, 0 — нет).ACTIVES — признак активности движений (обычно совпадает с CLOSED).Выясним точные идентификаторы видов документов (IDDOCDEF). Для этого нам понадобится исправный файл метаданных 1C.MD (актуальный на момент ведения учета) и сгенерированный на его основе файл словаря 1cv7.dds. Откроем 1cv7.dds в любом текстовом редакторе и найдем описание таблиц документов. Пример структуры описания в DDS:
=========================================================================
# Поля таблицы журнала документов _1SJOURN
=========================================================================
Table _1SJOURN (Журнал документов)
...
=========================================================================
# Описание таблицы документа "Реализация"
=========================================================================
Table DH2296 (Документ Реализация)
...
Из этого файла мы узнаем, что таблице DH2296 соответствует определенный вид документа. Теперь мы можем написать SQL-скрипт для переноса данных. Проанализируем пример наполнения таблицы _1SJOURN из таблицы шапки конкретного документа DH2296:
-- Пример запроса для реконструкции записей в _1SJOURN
INSERT INTO _1SJOURN (
IDDOC,
IDDOCDEF,
DATE_TIME_IDDOC,
DOCNO,
CLOSED,
ISMARK,
ACTIVES,
DSPERSONLY
)
SELECT
dh.IDDOC,
2296, -- Здесь указываем числовой IDDOCDEF для данного вида документа из DDS
-- Конструируем DATE_TIME_IDDOC. Если точное время утеряно, подставляем начало дня '000000'
CONVERT(varchar(8), dh.SP2298, 112) + '000000' + dh.IDDOC,
dh.SP2297, -- Поле номера документа (имя реквизита берем из DDS)
1, -- CLOSED (предполагаем проведенным, далее скорректируем по регистрам)
0, -- ISMARK
1, -- ACTIVES
0
FROM DH2296 dh
WHERE dh.IDDOC NOT IN (SELECT IDDOC FROM _1SJOURN);
Обратите внимание: точную дату и время документа (для формирования поля DATE_TIME_IDDOC) можно восстановить гораздо точнее, если использовать смежные таблицы связей или движений:
_1SCRDOC: Хранит ссылки на родительские и подчиненные документы. Поле CHILD_DATE_TIME_IDDOC содержит абсолютно точную строку даты и времени подчиненного документа, откуда ее можно напрямую вытащить запросом SELECT.RA*: Каждое движение в регистрах накопления содержит поле IDDOC и точную дату записи._1SENTRY: Если в конфигурации ведется бухгалтерский учет, точные даты операций, привязанные к документам, содержатся именно здесь.Если номера документов (DOCNO) или их точные даты были полностью стерты из таблиц СУБД (например, при усечении столбцов во время реструктуризации), неоценимую помощь может оказать текстовый файл журнала регистрации, расположенный на сервере по пути [КаталогБазы]\SYSLOG\1cv7.log. Этот файл не хранится в СУБД, поэтому он гарантированно не пострадал от реструктуризации таблиц SQL.
Каждая строка журнала регистрации содержит информацию о создании, изменении или проведении документа, включая его внутренний 9-символьный IDDOC, строковый номер и дату. Рассмотрим пример строки из этого файла:
20191128;16:54:06;Администратор;C;DocUnp;Реализация № РУ-00125 от 28.11.19;B7Z8;...
В этой строке:
20191128;16:54:06 — точные дата и время операции.Реализация № РУ-00125 от 28.11.19 — представление документа, откуда можно легко извлечь номер (РУ-00125).B7Z8 — это 36-ричное представление IDDOC (в СУБД оно будет дополняться пробелами до 9 символов, превращаясь в " B7Z8 ").Написав простой парсер (например, на Python или 1С), можно считать файл 1cv7.log, разобрать строки, сопоставить IDDOC с реальными номерами и датами, а затем сформировать набор SQL-инструкций UPDATE для окончательной корректировки таблицы _1SJOURN.
Если база данных подверглась сильному разрушению и восстановить ее «напрямую» в испорченном каталоге не получается, наиболее надежным решением будет использование стратегии слияния. Рассмотрим этот алгоритм по шагам.
Допустим, у нас есть две базы данных, развернутые на SQL-сервере:
Выполним последовательные шаги по слиянию данных:
Шаг 4.1. Генерация актуального словаря метаданных
Возьмите исправный, актуальный файл 1C.MD. Поместите его в каталог базы DB_Feb. Запустите конфигуратор этой базы в монопольном режиме, внесите любое незначительное изменение (например, добавьте пробел в описание конфигурации) и сохраните ее. Платформа создаст корректный файл словаря 1cv7.dds, соответствующий структуре рабочей базы на момент аварии.
Шаг 4.2. Перенос и слияние справочников
Справочники в 1С 7.7 хранятся в таблицах SC* и не зависят от общего журнала документов. Все новые элементы справочников (контрагенты, номенклатура, договоры), созданные пользователями с февраля по ноябрь, физически сохранились в базе DB_Corr. Нам необходимо перенести их в базу DB_Feb.
Для каждой таблицы справочника выполним SQL-запрос слияния по следующему принципу (на примере справочника контрагентов SC33):
-- Переносим новые элементы справочника, которых нет в февральской базе
INSERT INTO DB_Feb.dbo.SC33 (
ID, CODE, DESCR, ISFOLDER, ISMARK, PARENTID, VERSTAMP
)
SELECT
corr.ID, corr.CODE, corr.DESCR, corr.ISFOLDER, corr.ISMARK, corr.PARENTID, corr.VERSTAMP
FROM DB_Corr.dbo.SC33 corr
WHERE corr.ID NOT IN (SELECT ID FROM DB_Feb.dbo.SC33);
Аналогично необходимо поступить с таблицей периодических реквизитов справочников _1SCONST. Это критически важно, так как там хранятся истории цен, периодические реквизиты договоров и константы.
Шаг 4.3. Реконструкция и перенос документов в целевую базу
После того как справочники синхронизированы и содержат все актуальные элементы, мы можем перенести документы. Сначала выполняем реконструкцию таблицы _1SJOURN в поврежденной базе DB_Corr по методике, описанной в Шаге 2.
После успешного восстановления журнала в DB_Corr переносим строки за период с февраля по ноябрь в целевую базу DB_Feb:
-- Перенос записей журнала документов за недостающий период
INSERT INTO DB_Feb.dbo._1SJOURN
SELECT *
FROM DB_Corr.dbo._1SJOURN
WHERE CAST(LEFT(DATE_TIME_IDDOC, 8) AS datetime) >= '20190201'
AND IDDOC NOT IN (SELECT IDDOC FROM DB_Feb.dbo._1SJOURN);
После переноса записей журнала необходимо скопировать строки самих документов из таблиц шапок (DH*) и табличных частей (DT*), а также связи из таблицы подчиненных документов (_1SCRDOC). Пример переноса данных шапки документа DH2296:
INSERT INTO DB_Feb.dbo.DH2296
SELECT *
FROM DB_Corr.dbo.DH2296
WHERE IDDOC NOT IN (SELECT IDDOC FROM DB_Feb.dbo.DH2296);
И табличной части DT2296:
INSERT INTO DB_Feb.dbo.DT2296
SELECT *
FROM DB_Corr.dbo.DT2296
WHERE IDDOC NOT IN (SELECT IDDOC FROM DB_Feb.dbo.DT2296);
Шаг 4.4. Очистка таблиц итогов регистров и пересчет
Таблицы движений регистров накопления (RA*) переносятся вместе с документами по аналогичной схеме. Однако таблицы остатков и итогов (RG*) переносить напрямую из поврежденной базы ни в коем случае нельзя, так как они гарантированно содержат некорректные и рассогласованные данные.
Для восстановления правильных остатков поступим следующим образом:
RG*) в целевой базе DB_Feb с помощью команды TRUNCATE TABLE [ИмяТаблицы].1C.MD.Платформа 1С:Предприятие самостоятельно прочитает все перенесенные документы, их движения в таблицах RA* и заново рассчитает актуальные остатки на каждую контрольную точку, полностью заполнив таблицы RG* корректными данными.
Применяя предложенную методику последовательной реконструкции связей таблиц СУБД и используя уцелевшие лог-файлы операционной системы, можно полностью восстановить работоспособность даже сильно поврежденной базы данных 1С 7.7 без потери оперативной информации за последние месяцы работы.