Как в скриптах через SQL-DMO или SQL-запросы проверить режим доступа к базе данных (MULTI_USER или SINGLE_USER)?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При настройке ночных регламентных заданий — например, резервного копирования и последующего группового перепроведения документов в 1С:Предприятии 7.7 — администраторы часто сталкиваются с ошибкой ALTER DATABASE failed because a lock could not be placed on database. Эта ситуация возникает, когда платформа 1С пытается инициализировать сеанс в момент, когда база данных SQL Server всё ещё заблокирована другим процессом или находится в монопольном режиме SINGLE_USER.

Разберем причины возникновения этой ошибки, а также по шагам рассмотрим методы программной проверки готовности базы к многопользовательской работе с помощью SQL-DMO, ADO и системных функций SQL Server.

Почему 1С 7.7 выдает ошибку ALTER DATABASE при запуске

Проанализируем механику старта платформы. При каждом подключении к базе данных Microsoft SQL Server клиентская часть 1С 7.7 выполняет служебные проверки параметров базы данных и пытается применить системную инструкцию ALTER DATABASE (например, для проверки параметров автоматического обновления статистики или параметров блокировок).

Для выполнения команды ALTER DATABASE СУБД требует установки монопольной блокировки на уровне метаданных всей базы (блокировка типа LCK_M_X). Если в этот момент база находится в режиме SINGLE_USER и занята другим процессом (например, выгрузкой) либо в ней остаются незавершенные транзакции или активные клиентские подключения, попытка заблокировать базу завершается ошибкой, а запуск 1С зависает в сеансе.

Способ 1. Использование прямого свойства SingleUser в SQL-DMO

Объектная модель SQL-DMO (SQL Distributed Management Objects) содержит специальное свойство для определения монопольного режима базы данных. У объекта Database доступно булево свойство SingleUser.

Посмотрим на пример кода на VBScript, проверяющего список баз:


Set oServer = CreateObject("SQLDmo.SqlServer")
oServer.LoginSecure = True
oServer.Connect "localhost"

For Each oDatabase In oServer.Databases
    If Not oDatabase.SystemObject Then
        If oDatabase.SingleUser Then
            WScript.Echo "База " & oDatabase.Name & " находится в режиме SINGLE_USER!"
        Else
            WScript.Echo "База " & oDatabase.Name & " доступна в режиме MULTI_USER."
        End If
    End If
Next

oServer.DisConnect
Set oServer = Nothing

Важное ограничение: если база данных уже занята другим монопольным процессом, обращение к некоторым свойствам объекта oDatabase через SQL-DMO может вызвать исключение перехвата контекста СУБД. Поэтому рассмотрим более надежные подходы.

Способ 2. Проверка доступности базы через процедуру sp_helpdb

Выясним, как использовать системную процедуру sp_helpdb через метод ExecuteWithResults объекта SQL-DMO. Если база занята и недоступна для чтения метаданных, процедура возвращает пустой результат, что позволяет скрипту сделать паузу и отложить запуск 1С.


Function dbIsBusy(psBdName)
    dbIsBusy = False
    On Error Resume Next
    
    Set oServer = CreateObject("SQLDmo.SqlServer")
    oServer.LoginSecure = True
    oServer.Connect "(local)"
    
    Set oResult = oServer.ExecuteWithResults("sp_helpdb '" & psBdName & "'")
    
    If Err.Number <> 0 Or oResult.Rows = 0 Then
        dbIsBusy = True
    End If
    
    oServer.DisConnect
    Set oServer = Nothing
    On Error GoTo 0
End Function

Способ 3. Безопасный опрос через системную функцию DATABASEPROPERTYEX (рекомендуемый)

Наиболее надежный способ, исключающий взаимные блокировки при опросе состояния, — подключение через ADODB.Connection к системной базе master с вызовом скалярной функции DATABASEPROPERTYEX. Данная функция считывает свойства из системного каталога уровня сервера, не обращаясь к самой базе и не пытаясь захватить в ней блокировку.

Разберем реализацию функции на VBScript:


Function GetDatabaseUserAccess(dbName)
    Dim conn, rs, connStr, sql
    
    connStr = "Provider=SQLOLEDB;Data Source=(local);Initial Catalog=master;Integrated Security=SSPI;"
    Set conn = CreateObject("ADODB.Connection")
    conn.Open connStr
    
    sql = "SELECT CAST(DATABASEPROPERTYEX('" & dbName & "', 'UserAccess') AS VARCHAR(50)) AS AccessMode, " & _
          "       CAST(DATABASEPROPERTYEX('" & dbName & "', 'Status') AS VARCHAR(50)) AS DbStatus"
          
    Set rs = conn.Execute(sql)
    
    If Not rs.EOF Then
        GetDatabaseUserAccess = rs("AccessMode")
    Else
        GetDatabaseUserAccess = "UNKNOWN"
    End If
    
    rs.Close
    conn.Close
    Set rs = Nothing
    Set conn = Nothing
End Function

' Пример использования в цикле ожидания
Dim status
status = GetDatabaseUserAccess("ERP_TEST")

If status = "MULTI_USER" Then
    WScript.Echo "База готова к работе. Запускаем пакетное перепроведение..."
Else
    WScript.Echo "База занята режимом: " & status & ". Ожидаем освобождения."
End If

Функция DATABASEPROPERTYEX('ИмяБазы', 'UserAccess') возвращает следующие значения:

  1. MULTI_USER — база доступна для всех пользователей (нормальный режим для запуска 1С);
  2. SINGLE_USER — монопольный режим, доступ только для одного подключения;
  3. RESTRICTED_USER — доступ разрешен только членам ролей db_owner, dbcreator или sysadmin.

Способ 4. Принудительный перевод базы в MULTI_USER с отключением зависших сессий

Если резервное копирование завершено, но предыдущий сеанс или скрипт оставил соединение активным, перед стартом 1С можно выполнить принудительный сброс режима с завершением всех мешающих процессов:


ALTER DATABASE ERP_TEST
SET MULTI_USER
WITH ROLLBACK IMMEDIATE;

Параметр WITH ROLLBACK IMMEDIATE немедленно откатывает все незавершенные сторонние транзакции и разрывает активные подключения, переводя базу в многопользовательский режим без ожидания их естественного закрытия.

Рекомендации по организации пакетных заданий

Чтобы исключить возникновение конфликтов блокировок между архивацией и перепроведением, придерживайтесь следующих правил:

  1. Откажитесь от штатной выгрузки 1С 7.7 в пользу нативного бэкапа SQL: создание резервной копии командой BACKUP DATABASE средствами самого SQL Server не требует перевода базы в SINGLE_USER и не блокирует чтение/запись данных.
  2. Контролируйте завершение процессов операционной системы: если скрипт запускает 1cv7s.exe в пакетном режиме, обязательно проверяйте факт полного закрытия процесса в диспетчере задач перед стартом следующего шага.
  3. Учитывайте регламентное обслуживание: не запускайте пакетное перепроведение одновременно с ночной перестройкой индексов и проверкой целостности DBCC CHECKDB.
← На главную