Как перевести 1С 7.7 с DBF на SQL Server 2012 и новые версии СУБД: практическое руководство по миграции и оптимизации

Системный администратор 1С v7.7 Бухгалтерский учет IT и автоматизация бизнеса
← На главную

Рано или поздно любой администратор или разработчик, сопровождающий базы данных на платформе 1С:Предприятие 7.7, сталкивается с проблемой ограничения формата DBF. Когда размер одной из таблиц (чаще всего это таблица проводок 1SENTRY.DBF или таблица движений регистров 1SPART.DBF) приближается к критической отметке в 1 Гб или, тем более, 2 Гб, система начинает вести себя нестабильно: появляются регулярные сбои индексов, зависания транзакций и риск полной потери данных. Использование временных утилит вроде kernel33 лишь оттягивает неизбежный финал.

В этой статье мы подробно разберем, как продлить жизнь вашей информационной базе 1С 7.7, переведя ее на СУБД MS SQL Server 2012 или более современную версию. Мы детально рассмотрим технические нюансы, установку необходимых патчей, решение системных ошибок СУБД и оптимизацию быстродействия, которая критически важна после переезда.

---

Почему стандартная 1С 7.7 не работает с современными SQL-серверами "из коробки"

Платформа 1С 7.7 разрабатывалась во времена господства MS SQL Server 2000 (версия СУБД 8.0). Разработчики жестко привязали платформу к возможностям этой СУБД. При запуске 1С 7.7 выполняет несколько проверок:

  1. Проверка версии СУБД через ODBC. Если версия сервера отличается от MS SQL 2000 (например, это SQL 2008, 2012 или 2016), оригинальный движок 1С выдает ошибку совместимости и отказывается работать.
  2. Использование устаревших хранимых процедур. Платформа при инициализации пытается вызвать системную процедуру СУБД sp_dboption, которая использовалась в SQL 2000 для настройки параметров баз данных. Начиная с SQL Server 2012, корпорация Microsoft полностью удалила эту процедуру из системной базы данных master, из-за чего запуск 1С падает с критической ошибкой базы данных.
  3. Требование уровня совместимости (Compatibility Level). Штатная платформа требует, чтобы у базы данных был выставлен уровень совместимости "80" (SQL Server 2000). Новые СУБД (начиная с SQL Server 2014) физически больше не поддерживают этот уровень.

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

---

Шаг 1. Подготовка платформы и использование "секретного релиза" (Solution7)

Для обхода проверки версии SQL-сервера и корректной работы с современными драйверами ODBC используется модифицированная библиотека BkEnd.dll (в сообществе разработчиков известная как проект Solution7 или неофициальный релиз платформы 27.7).

Разберем, что делает этот патч и как его применить:

Важное требование безопасности в SQL Server: Пользователю СУБД, под которым 1С 7.7 будет подключаться к базе данных, помимо стандартной роли владельца базы db_owner, обязательно нужно выдать системное разрешение VIEW SERVER STATE. Без этого патч Solution7 не сможет опрашивать СУБД на предмет количества активных блокировок и подключений, что приведет к ошибкам монопольного доступа при входе пользователей.

---

Шаг 2. Восстановление процедуры sp_dboption в SQL Server 2012 и выше

Если вы разворачиваете базу на MS SQL Server 2012 или более новом сервере, то даже при использовании патченной библиотеки платформа упадет при первом старте, так как не найдет процедуру sp_dboption в базе master.

Проанализируем решение этой проблемы. Нам необходимо вручную воссоздать эту системную процедуру, которая будет перехватывать вызовы от 1С 7.7 и транслировать их в современные команды SQL-диалекта T-SQL (в частности, в конструкцию ALTER DATABASE).

Для этого запустите среду SQL Server Management Studio (SSMS), подключитесь к вашему серверу под учетной записью администратора (sa), выберите системную базу данных master, создайте новый запрос и выполните следующий SQL-скрипт:


USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[sp_dboption]
@dbname sysname = NULL, 
@optname varchar(35) = NULL, 
@optvalue varchar(10) = NULL 
as
set nocount on
declare @dbid int 
declare @catvalue int 
declare @optcount int 
declare @allstatopts int 
declare @alloptopts int 
declare @allcatopts int 
declare @exec_stmt nvarchar(max)
declare @fulloptname varchar(35)
declare @alt_optname varchar(50)
declare @alt_optvalue varchar(30)
declare @optnameIn varchar(35)
select @optnameIn = @optname
,@optname = LOWER (@optname collate Latin1_General_CI_AS)

select @allstatopts=number from master.dbo.spt_values where type = 'D'
and name = 'ALL SETTABLE OPTIONS'
select @allcatopts=number from master.dbo.spt_values where type = 'DC'
and name = 'ALL SETTABLE OPTIONS'
select @alloptopts=number from master.dbo.spt_values where type = 'D2'
and name = 'ALL SETTABLE OPTIONS'
if @dbname is null
begin
select 'Settable database options:' = name
from master.dbo.spt_values
where (type = 'D'
and number & @allstatopts <> 0
and number not in (0,@allstatopts)) 
or (type = 'DC'
and number & @allcatopts <> 0
and number not in (0,@allcatopts))
or (type = 'D2'
and number & @alloptopts <> 0
and number not in (0,@alloptopts))
order by name
return (0)
end

select @dbid = dbid
from master.dbo.sysdatabases
where name = @dbname

if @dbid is null
begin
raiserror(15010,-1,-1,@dbname)
print ' '
select 'Available databases:' = name
from master.dbo.sysdatabases
return (1)
end

if @optname is null
begin
select 'The following options are set:' = v.name
from master.dbo.spt_values v, master.dbo.sysdatabases d
where d.name=@dbname
and ((number & @allstatopts <> 0
and number not in (-1,@allstatopts)
and v.type = 'D'
and (v.number & d.status)=v.number)
or (number & @allcatopts <> 0
and number not in (-1,@allcatopts)
and v.type = 'DC'
and d.category & v.number <> 0)
or (number & @alloptopts <> 0
and number not in (-1,@alloptopts)
and v.type = 'D2'
and d.status2 & v.number <> 0))
return(0)
end
if @optvalue is not null and lower(@optvalue) not in ('true', 'false', 'on', 'off')
begin
raiserror(15241,-1,-1)
return (1)
end

select @optcount = count(*) ,@fulloptname = min(name)
from master.dbo.spt_values
where lower(name collate Latin1_General_CI_AS) like '%' + @optname + '%'
and ((type = 'D'
and number & @allstatopts <> 0
and number not in (-1,@allstatopts))
or (type = 'DC'
and number & @allcatopts <> 0
and number not in (-1,@allcatopts))
or (type = 'D2'
and number & @alloptopts <> 0
and number not in (-1,@alloptopts)))

if @optcount = 0
begin
raiserror(15011,-1,-1,@optnameIn)
print ' '
select 'Settable database options:' = name
from master.dbo.spt_values
where (type = 'D'
and number & @allstatopts <> 0
and number not in (-1,@allstatopts)) 
or (type = 'DC'
and number & @allcatopts <> 0
and number not in (-1,@allcatopts))
or (type = 'D2'
and number & @alloptopts <> 0
and number not in (-1,@alloptopts))
order by name
return (1)
end

if @optcount > 1
begin
raiserror(15242,-1,-1,@optnameIn)
print ' '
select duplicate_options = name
from master.dbo.spt_values
where lower(name collate Latin1_General_CI_AS) like '%' + @optname + '%'
and ((type = 'D'
and number & @allstatopts <> 0
and number not in (-1,@allstatopts))
or (type = 'DC'
and number & @allcatopts <> 0
and number not in (-1,@allcatopts))
or (type = 'D2'
and number & @alloptopts <> 0
and number not in (-1,@alloptopts))
)
return (1)
end

if @optvalue is null
begin
select OptionName = v.name,
CurrentSetting = (case
when ( ((v.number & d.status) = v.number
and v.type = 'D')
or (d.category & v.number <> 0
and v.type = 'DC')
or (d.status2 & v.number <> 0
and v.type = 'D2')
)
then 'ON'
when not
( ((v.number & d.status) = v.number
and v.type = 'D')
or (d.category & v.number <> 0
and v.type = 'DC')
or (d.status2 & v.number <> 0
and v.type = 'D2')
)
then 'OFF'
end)
from master.dbo.spt_values v, master.dbo.sysdatabases d
where d.name=@dbname
and ((v.number & @allstatopts <> 0
and v.number not in (-1,@allstatopts) 
and v.type = 'D')
or (v.number & @allcatopts <> 0
and v.number not in (-1,@allcatopts) 
and v.type = 'DC')
or (v.number & @alloptopts <> 0
and v.number not in (-1,@alloptopts) 
and v.type = 'D2')
)
and lower(v.name) = lower(@fulloptname)
return (0)
end
select @catvalue = 0
select @catvalue = number
from master.dbo.spt_values
where lower(name) = lower(@fulloptname)
and type = 'DC'

if (@catvalue <> 0)
begin
select @alt_optvalue = (case lower(@optvalue)
when 'true' then 'true'
when 'on' then 'true'
else 'false'
end)
select @alt_optname = (case @catvalue
when 1 then 'publish'
when 2 then 'subscribe'
when 4 then 'merge publish'
else quotename(@fulloptname, '''')
end)
select @exec_stmt = quotename(@dbname, '[') + '.dbo.sp_replicationdboption'
EXEC @exec_stmt @dbname, @alt_optname, @alt_optvalue
return (0)
end

select @alt_optvalue = (case lower(@optvalue)
when 'true' then 'ON'
when 'on' then 'ON'
else 'OFF'
end)

select @fulloptname = lower(@fulloptname)
select @alt_optname = (case @fulloptname
when 'auto create statistics' then 'AUTO_CREATE_STATISTICS'
when 'auto update statistics' then 'AUTO_UPDATE_STATISTICS'
when 'autoclose' then 'AUTO_CLOSE'
when 'autoshrink' then 'AUTO_SHRINK'
when 'ansi padding' then 'ANSI_PADDING'
when 'arithabort' then 'ARITHABORT'
when 'numeric roundabort' then 'NUMERIC_ROUNDABORT'
when 'ansi null default' then 'ANSI_NULL_DEFAULT'
when 'ansi nulls' then 'ANSI_NULLS'
when 'ansi warnings' then 'ANSI_WARNINGS'
when 'concat null yields null' then 'CONCAT_NULL_YIELDS_NULL'
when 'cursor close on commit' then 'CURSOR_CLOSE_ON_COMMIT'
when 'torn page detection' then 'TORN_PAGE_DETECTION'
when 'quoted identifier' then 'QUOTED_IDENTIFIER'
when 'recursive triggers' then 'RECURSIVE_TRIGGERS'
when 'default to local cursor' then 'CURSOR_DEFAULT'
when 'offline' then (case @alt_optvalue when 'ON' then 'OFFLINE' else 'ONLINE' end)
when 'read only' then (case @alt_optvalue when 'ON' then 'READ_ONLY' else 'READ_WRITE' end)
when 'dbo use only' then (case @alt_optvalue when 'ON' then 'RESTRICTED_USER' else 'MULTI_USER' end)
when 'single user' then (case @alt_optvalue when 'ON' then 'SINGLE_USER' else 'MULTI_USER' end)
when 'select into/bulkcopy' then 'RECOVERY'
when 'trunc. log on chkpt.' then 'RECOVERY'
when 'db chaining' then 'DB_CHAINING'
else @alt_optname
end)
if @fulloptname = 'dbo use only'
begin
if @alt_optvalue = 'ON'
begin
if databaseproperty(@dbname, 'IsSingleUser') = 1
begin
raiserror(5066,-1,-1);
return (1)
end
end
else
begin
if databaseproperty(@dbname, 'IsDBOOnly') = 0
return (0)
end
end
if @fulloptname = 'single user'
begin
if @alt_optvalue = 'ON'
begin
if databaseproperty(@dbname, 'ISDBOOnly') = 1
begin
raiserror(5066,-1,-1);
return (1)
end
end
else
begin
if databaseproperty(@dbname, 'IsSingleUser') = 0
return (0)
end
end
select @alt_optvalue = (case @fulloptname
when 'default to local cursor' then (case @alt_optvalue when 'ON' then 'LOCAL' else 'GLOBAL' end)
when 'offline' then ''
when 'read only' then ''
when 'dbo use only' then ''
when 'single user' then ''
else @alt_optvalue
end)
if lower(@fulloptname) = 'select into/bulkcopy'
begin
if @alt_optvalue = 'ON'
begin
if databaseproperty(@dbname, 'IsTrunclog') = 1
select @alt_optvalue = 'RECMODEL_70BACKCOMP'
else
select @alt_optvalue = 'BULK_LOGGED'
end
else
begin
if databaseproperty(@dbname, 'IsTrunclog') = 1
select @alt_optvalue = 'SIMPLE'
else
select @alt_optvalue = 'FULL'
end
end
if lower(@fulloptname) = 'trunc. log on chkpt.'
begin
if @alt_optvalue = 'ON'
begin
if databaseproperty(@dbname, 'IsBulkCopy') = 1
select @alt_optvalue = 'RECMODEL_70BACKCOMP'
else
select @alt_optvalue = 'SIMPLE'
end
else
begin
if databaseproperty(@dbname, 'IsBulkCopy') = 1
select @alt_optvalue = 'BULK_LOGGED'
else
select @alt_optvalue = 'FULL'
end
end

select @exec_stmt = 'ALTER DATABASE ' + quotename(@dbname) + ' SET ' + @alt_optname + ' ' + @alt_optvalue + ' WITH NO_WAIT'
EXEC (@exec_stmt)
if @@error <> 0
begin
raiserror(15627,-1,-1)
return (1)
end
return (0) 
GO

Этот скрипт пересоздает процедуру sp_dboption, адаптируя ее к системной архитектуре SQL Server 2012 и выше, что решает проблему инициализации базы данных 1С 7.7.

---

Шаг 3. Алгоритм выгрузки и загрузки базы данных

Рассмотрим по шагам процесс непосредственной миграции данных из формата DBF на SQL:

  1. Проверка логической целостности базы. Перед выгрузкой в исходной DBF-базе запустите режим "Тестирование и исправление" (Конфигуратор -> Сервис -> Тестирование и исправление ИБ). Крайне важно избавиться от битых ссылок и дублирующихся уникальных идентификаторов (ID) в справочниках и документах. DBF-версия прощает дублирование ID, но SQL Server жестко пресечет попытку создания записи с дублирующимся первичным ключом и прервет загрузку базы с ошибкой.
  2. Создание файла выгрузки. В конфигураторе выберите "Администрирование" -> "Выгрузить данные" и сохраните базу в файл с расширением .dat.

    Обратите внимание: Если объем вашей исходной DBF-базы превышает 1.5–2 Гб, штатный конфигуратор 1С 7.7 может вылетать без ошибок в процессе создания .dat файла. В этом случае используйте стороннюю проверенную утилиту Unload_Dat_Fix, разработанную сообществом для корректного обхода лимита памяти процесса выгрузки.

  3. Подготовка базы на SQL Server. В SSMS создайте пустую базу данных (например, с именем db_1c77). Настройте для файлов данных и лога транзакций (MDF и LDF) достаточный размер первоначального выделения памяти и шаг авторасширения (например, 512 Мб), чтобы СУБД не тратила процессорное время на постоянное фрагментированное довыделение дискового пространства в процессе загрузки.
  4. Настройка подключения в Конфигураторе. Переключите режим работы информационной базы. Для этого скопируйте вашу исходную DBF-папку (в ней обязательно должны остаться файлы 1cv7.md, users.usr в каталоге usrdef, файлы конфигурации регламентированных отчетов и папка ExtForms). Все DBF и CDX файлы можно удалить — они больше не понадобятся. Запустите конфигуратор этой пустой базы, выберите "Администрирование" -> "Параметры базы данных SQL" и заполните параметры авторизации:
    • Имя сервера СУБД;
    • Имя базы данных на сервере;
    • Имя пользователя СУБД и пароль.
  5. Загрузка данных. Запустите операцию "Администрирование" -> "Загрузить данные" и укажите путь к ранее созданному файлу .dat. Конфигуратор создаст всю необходимую структуру таблиц в SQL-базе и заполнит их данными.

---

Шаг 4. Оптимизация производительности: почему SQL работает медленно и как это исправить

Переведя базу на SQL Server, вы сразу заметите парадоксальное явление: некоторые отчеты и групповые обработки (особенно групповое перепроведение документов) начали работать значительно медленнее, чем это было в локальном DBF-варианте. Разберем по шагам причины этого замедления и методы исправления ситуации.

Оптимизация 1. Прямые запросы вместо штатного механизма "черных запросов"

Штатный механизм запросов 1С 7.7 ("черные запросы") устроен по принципу последовательной выборки данных. Платформа делает огромное количество мелких запросов к СУБД, нагружая сеть и процессор сервера очередью транзакций. Для решения этой проблемы рекомендуется переписать ключевые отчеты и тяжелые алгоритмы проведения документов на прямые запросы T-SQL с использованием внешней компоненты 1С++.

Прямой запрос обращается напрямую к таблицам SQL Server, минуя транслятор 1С, и за один такт возвращает готовую выборку. Посмотрим на сравнительный пример кода.

Штатный "черный запрос" в 1С 7.7:


// Черный запрос работает крайне медленно на SQL
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = 
"//{{ЗАПРОС(СкладскойОстаток)
|Период с ВыбДата по ВыбДата;
|Товар = Регистр.ОстаткиТМЦ.Товар;
|Склад = Регистр.ОстаткиТМЦ.Склад;
|Количество = Регистр.ОстаткиТМЦ.Количество;
|Функция КонОст = КонОст(Количество);
|Группировка Товар без групп;
|Условие(Склад = ВыбСклад);
|//}}ЗАПРОС";
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
    Пока Запрос.Группировка(1) = 1 Цикл
        // Обработка данных
    КонецЦикла;
КонецЕсли;

Оптимизированный прямой запрос с использованием внешней компоненты 1С++:


// Прямой SQL-запрос выполняется в десятки раз быстрее
ODBCРекордсет = СоздатьОбъект("ODBCRecordset");
ТекстЗапроса = "
|SELECT 
|    Рег.SP112 as [Товар $Справочник.Номенклатура],
|    SUM(Рег.SP115) as Количество
|FROM 
|    $РегистрИтоги.ОстаткиТМЦ as Рег
|WHERE 
|    Рег.period = :ВыбДата
|    AND Рег.SP113 = :ВыбСклад
|GROUP BY 
|    Рег.SP112
|";
ODBCРекордсет.УстановитьТекстовыйПараметр("ВыбДата", НачалоМесяца(ВыбДата));
ODBCРекордсет.УстановитьТекстовыйПараметр("ВыбСклад", ВыбСклад);
ТаблицаРезультатов = ODBCРекордсет.ВыполнитьИнструкцию(ТекстЗапроса);
// В ТаблицаРезультатов мгновенно возвращается готовая таблица значений

Оптимизация 2. Порядок измерений в регистрах (Индексы)

В DBF-версии порядок следования измерений в регистрах практически не влиял на скорость работы. В SQL-версии от этого напрямую зависит структура создаваемых СУБД индексов.

Если в вашем регистре первым измерением стоит реквизит, по которому редко накладывается фильтр (например, "Проект" или "МОЛ"), а вторым — часто используемый "Товар", то при попытке получить остатки по конкретному товару SQL Server не сможет использовать индекс эффективно. Он будет вынужден делать операцию Table Scan (сканирование всей таблицы), что приведет к колоссальным задержкам.

Проанализируйте ваши регистры накопления в Конфигураторе. Измените порядок измерений так, чтобы первыми шли наиболее часто выбираемые и детализированные измерения (например, сначала "Товар", затем "Склад", и только потом — вспомогательные свойства).

Оптимизация 3. Избавление от мелких запросов в циклах

В DBF-версиях разработчики часто использовали метод получения остатков ВыгрузитьИтоги() или ПолучитьИтог() внутри циклов по строкам проводимого документа. На SQL-платформе такое проведение будет "умирать", так как каждая строка вызывает обращение к серверу через сеть.

Измените логику алгоритмов проведения документов:

  1. Перед началом цикла по строкам соберите все товары документа в список значений.
  2. Одним запросом выгрузите итоги по всем товарам из этого списка в таблицу значений в оперативной памяти.
  3. Внутри цикла по строкам документа делайте поиск по этой локальной таблице значений с помощью метода ПолучитьПараметрыТаблицы() или предварительно проиндексировав таблицу значений (например, средствами класса ИндексированнаяТаблицаЗначений из ВК 1С++).

Оптимизация 4. Решение проблемы Legacy Cardinality Estimation

Если вы развернули базу на MS SQL Server 2014, 2016 или новее, вы можете столкнуться с резким замедлением некоторых стандартных отчетов. Это связано с внедрением нового оптимизатора запросов (Cardinality Estimator).

Для баз 1С 7.7 рекомендуется принудительно вернуть СУБД к старому стилю оценки стоимости запросов. Для этого в свойствах базы данных выполните инструкцию:


ALTER DATABASE [db_1c77] 
SET COMPATIBILITY_LEVEL = 110; -- Соответствует уровню SQL 2012
GO
ALTER DATABASE SCOPED CONFIGURATION 
SET LEGACY_CARDINALITY_ESTIMATION = ON;
GO

Или запустите SQL Server с глобальным флагом трассировки 9481, который отключает новый оптимизатор на уровне всего экземпляра сервера.

---

Ошибки при выборке подчиненных документов без указания периода

Еще одна известная системная проблема при работе 1С 7.7 на SQL — медленная выборка или падение системы при выполнении метода ВыбратьПодчиненныеДокументы() без явного указания временного интервала.

Чтобы избежать этой проблемы, выполните глобальный поиск в вашей конфигурации по тексту .ВыбратьПодчиненныеДокументы( и принудительно задайте границы выборки, ограничив её разумными рамками:


// Вместо небезопасного вызова:
// Док.ВыбратьПодчиненныеДокументы();

// Используем явное ограничение периода:
Док.ВыбратьПодчиненныеДокументы(РабочаяДата() - 365, РабочаяДата() + 30);

Это убережет систему от сканирования всей таблицы связей документов с начала ведения учета.

---

Альтернативные решения для 1С 7.7

Если развертывание полноценного MS SQL Server кажется вам избыточным или дорогим с точки зрения лицензирования, можно рассмотреть следующие альтернативы, позволяющие уйти от ограничений DBF:

  1. SQLite (компонента 1SQLite): С помощью внешней компоненты 1С++ и модуля 1SQLite вы можете перевести базу в формат SQLite. База будет храниться в одном файле, лимит размера таблиц составит до нескольких терабайт, при этом не потребуется поднимать тяжелый сервер СУБД. Это идеальное решение для небольших баз данных (до 5–10 пользователей).
  2. PostgreSQL (SELTA@Etersoft): Существует коммерческий транслятор запросов SELTA, который позволяет 1С 7.7 работать с бесплатной СУБД PostgreSQL. Данное решение отлично зарекомендовало себя в условиях импортозамещения и экономии на лицензиях Windows/MS SQL.

Используя приведенные инструкции, вы сможете перевести свою информационную базу 1С 7.7 на SQL Server 2012 и выше, обеспечив ей стабильную работу и надежность хранения данных на многие годы вперед.

← На главную