Ситуации, когда массовая обработка данных в 1С привела к ошибкам (например, неправильная замена ссылок на сотрудников, как в данном случае), требуют оперативного вмешательства. Если изменения уже зафиксированы транзакцией, простая отмена действий невозможна. В этом случае нам на помощь приходит механизм восстановления базы данных SQL Server на определенный момент времени (Point-in-Time Recovery).
В этом материале мы подробно разберем, как выполнить эту процедуру, используя SQL Server Management Studio (SSMS) и T-SQL скрипты. Мы также обсудим, как избежать потери данных других пользователей и как проанализировать журнал транзакций.
Прежде чем приступать к действиям, давайте убедимся, что наша среда соответствует требованиям для восстановления на момент времени. Если эти условия не соблюдены, восстановиться можно будет только на момент создания последнего полного бэкапа.
Проверить это можно в свойствах базы данных на вкладке Options (Параметры).
Частая ошибка, с которой мы сталкиваемся при попытке восстановления — сообщение от SQL Server: "The tail of the log for the database... has not been backed up". Это означает, что в активном журнале транзакций есть данные, которые еще не попали в архив. SQL Server защищает нас от случайной потери этих последних данных.
Перед началом восстановления у нас есть два пути:
NO_TRUNCATE.WITH REPLACE (Перезаписать базу).Давайте рассмотрим самый безопасный сценарий. Вместо того чтобы сразу "убивать" рабочую базу (Production) и накатывать старую версию (что остановит работу всей компании и уничтожит работу других пользователей), мы восстановим копию базы под новым именем (например, Accounting_Restore).
Преимущества такого подхода:
Разберем процесс по шагам, используя графический интерфейс. Интерфейс может незначительно отличаться в разных версиях, но суть остается прежней.
Совет: Если вы восстанавливаете поверх существующей, нажимайте правой кнопкой на саму базу -> Tasks -> Restore -> Database.
Если история бэкапов сохранилась в msdb, SQL Server может сам заполнить список файлов при выборе базы-источника. Убедитесь, что галочки стоят напротив полного бэкапа и всех последующих логов.
Нажмите кнопку Timeline (Временная шкала) или найдите раздел "Point in time" (К моменту времени).
Использование скриптов дает больше контроля и понимания процесса. Для восстановления на момент времени критически важно использовать параметр STOPAT.
Предположим, нам нужно восстановиться на состояние 25 апреля 2023 года, 14:30:00.
Шаг 1: Восстановление полного бэкапа в режиме NORECOVERY
Режим NORECOVERY оставляет базу в состоянии "Восстановление" (Restoring...), позволяя накатывать следующие файлы логов. База будет недоступна для пользователей.
-- Если восстанавливаем поверх текущей базы, переводим её в SINGLE_USER, чтобы сбросить соединения 1С
USE master;
ALTER DATABASE Bukhgalteriya SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE Bukhgalteriya
FROM DISK = 'D:\Backups\Bukhgalteriya_Full.bak'
WITH NORECOVERY, REPLACE;
Шаг 2: Накат промежуточных бэкапов логов (если есть)
Если между полным бэкапом и целевым временем было несколько бэкапов лога, восстанавливаем их по очереди, также в режиме NORECOVERY.
RESTORE LOG Bukhgalteriya
FROM DISK = 'D:\Backups\Bukhgalteriya_Log_1000.trn'
WITH NORECOVERY;
Шаг 3: Накат последнего лога с параметром STOPAT
В последнем файле, который содержит наше целевое время, мы используем параметр STOPAT и переводим базу в рабочее состояние (RECOVERY).
RESTORE LOG Bukhgalteriya
FROM DISK = 'D:\Backups\Bukhgalteriya_Log_1500.trn'
WITH RECOVERY,
STOPAT = '2023-04-25 14:30:00';
После выполнения этой команды база данных станет доступна и будет содержать данные ровно на 14:30:00. Все транзакции, совершенные после этого времени (даже если они есть в файле бэкапа), будут отброшены.
В вопросе автора прозвучала важная мысль: "Как посмотреть в журнале какие были исправления?". Стандартный журнал регистрации 1С может показать, кто и когда запускал обработку, но не всегда показывает детали изменений SQL.
Если нам нужно точное время транзакции на уровне SQL, мы можем воспользоваться системной (недокументированной, но популярной) функцией fn_dblog. Она читает активный журнал транзакций.
Посмотрим на пример запроса, который поможет найти массовые изменения:
USE Bukhgalteriya;
SELECT
[Current LSN],
[Transaction ID],
[Operation],
[Context],
[AllocUnitName],
[Begin Time],
[Transaction Name]
FROM
fn_dblog(NULL, NULL)
WHERE
[Begin Time] > '2023-04-25 14:00:00' -- Время начала поиска
AND [Operation] = 'LOP_BEGIN_XACT' -- Ищем начала транзакций
ORDER BY
[Begin Time] DESC;
В результатах мы ищем транзакции, которые начались в предполагаемое время ошибки. Если обработка меняла тысячи записей, мы увидим огромную активность, связанную с операциями LOP_MODIFY_ROW. Найдя точное время [Begin Time] ошибочной транзакции, мы можем выбрать время для восстановления на 1 секунду раньше.
Подводя итог, выделим главные шаги для безопасного решения проблемы: