В процессе сопровождения систем на базе 1С:Предприятие часто возникают ситуации, когда необходимо восстановить хронологию изменений конкретного документа, но штатное версионирование в базе было отключено. Если у вас имеется архив ежедневных SQL-бэкапов, задача кажется решаемой, но трудоемкой. В этой статье мы подробно разберем, как автоматизировать процесс извлечения данных, проанализируем внутренние механизмы хранения версий в MS SQL и рассмотрим современные инструменты, позволяющие избежать ручного развертывания каждой копии.
Прежде чем приступать к восстановлению, проанализируем, как 1С помечает изменения на уровне базы данных. В каждой таблице документа (например, _Document123) существует поле _Version. Разберем его особенности:
rowversion (или timestamp в старых версиях). Это не дата и не время. Это уникальное двоичное число, которое автоматически инкрементируется при любой транзакции обновления строки._Version выше, чем в бэкапе от вторника, значит, документ модифицировался.datetime невозможен. Однако, сравнивая это значение между разными срезами данных, мы можем точно определить интервал времени, в который произошло изменение.Если у вас накопилось 100 бэкапов, последовательное развертывание каждого займет огромное количество времени. Проанализируем ситуацию: нам нужно найти момент, когда данные в документе изменились (например, сменилась статья затрат или сумма). Для этого эффективнее всего использовать алгоритм бинарного поиска:
Этот метод позволяет сократить количество итераций восстановления со 100 до всего 7-8, что значительно экономит ресурсы сервера и время специалиста.
Для тех, кто хочет полностью исключить «метр пробега мыши», рассмотрим вариант автоматизации. Самым мощным инструментом здесь выступает PowerShell с библиотекой dbatools. Мы можем написать скрипт, который будет в цикле брать файлы .bak, разворачивать их под временными именами, выгружать нужную строку из таблицы в общую сводную таблицу и удалять временную базу.
Разберем пример логики такого скрипта:
# Подключаем модуль для работы с SQL
Import-Module dbatools
$backups = Get-ChildItem "C:\Backups\*.bak"
$targetTable = "_Document150" # Имя таблицы документа в SQL
$docGuid = 0x812300155D05070111E265780A288E20 # GUID документа в формате binary(16)
foreach ($bak in $backups) {
$tempDbName = "Temp_Restore_" + $bak.BaseName
# Быстрое восстановление базы
Restore-DbaDatabase -SqlInstance "YOUR_SERVER" -Path $bak.FullName -DatabaseName $tempDbName
# SQL-запрос для извлечения данных
$query = "SELECT _Number, _Date_Time, _Version, '$($bak.Name)' as BackupSource
FROM [$tempDbName].dbo.[$targetTable]
WHERE _IDRRef = $docGuid"
# Выполняем и сохраняем результат в центральную таблицу
Invoke-DbaQuery -SqlInstance "YOUR_SERVER" -Database "AnalysisDB" -Query "INSERT INTO DocumentHistory $query"
# Удаляем временную базу
Remove-DbaDatabase -SqlInstance "YOUR_SERVER" -Database $tempDbName -Confirm:$false
}
Важный нюанс: Чтобы найти правильное имя таблицы (например, _Document150), воспользуемся методом 1С ПолучитьСтруктуруХраненияБазыДанных() в консоли запросов или программно. Если доступа к 1С нет, можно выполнить поиск по колонкам в SQL sys.columns, ориентируясь на типичные для документа имена полей.
Рассмотрим более продвинутый способ, который упоминался как «виртуальное» развертывание. Существуют сторонние решения (например, Redgate SQL Virtual Restore или ApexSQL Restore), которые позволяют «монтировать» файл бэкапа как полноценную базу данных без физического извлечения всех данных на диск.
Проанализируем преимущества этого подхода:
SQL Server Management Studio (SSMS) для выполнения любых SELECT запросов.Если ваша база данных работала в модели восстановления Full, то наиболее детальную информацию можно получить из логов транзакций. Выясним причину, почему это лучше обычных бэкапов: в логах фиксируется не просто состояние «на момент создания бэкапа», а каждое конкретное действие пользователя с указанием времени до миллисекунды.
Для чтения логов можно использовать системную функцию fn_dblog. Рассмотрим пример запроса для поиска изменений в конкретной таблице:
SELECT
[Transaction ID],
Operation,
Context,
[Begin Time],
[Transaction Name]
FROM sys.fn_dblog(NULL, NULL)
WHERE AllocUnitName LIKE '%_Document150%'
Этот метод требует глубоких знаний структуры SQL, поэтому на практике чаще применяются утилиты типа Transaction Log Analyzer, которые преобразуют бинарные данные лога в читаемый вид: «Пользователь А изменил поле Сумма с 100 на 200 в 14:05:01».
При извлечении данных напрямую из SQL-таблиц важно учитывать специфику хранения типов данных в 1С:
4023-05-20, значит, в 1С это 2023-05-20. При написании запросов всегда проверяйте это смещение._IDRRef идентификаторы хранятся в формате binary(16). Помните, что порядок байтов в SQL-представлении GUID отличается от того, как он выглядит в строковом виде в 1С. Для корректного поиска используйте преобразование типов.0x00 (Ложь) и 0x01 (Истина).Подводя итог, можно сказать: наиболее эффективный путь — это сочетание метода бинарного поиска для локализации проблемы и PowerShell-скриптов для автоматизации извлечения данных. Если же задача критически важна и требует поминутной точности, стоит инвестировать время в анализ логов транзакций.