Изменение структуры баз 1С 7.7 без долгой реструктуризации. Часть 1. Справочники
На днях встретил вопрос на форуме про возможность внесения изменений в конфигурацию без долгого сохранения в рабочей базе большого объема.
Вот решил поделиться опытом, как это делал я. База у нас была объемом порядка 120 Гб. К базе обращался сайт в режиме 24/7, поэтому важно было быстро сохранять изменения и желательно без последующего монопольного запуска для восстановления индексов и процедур и без отключения пользователей от базы.
Это первая часть статьи и посвящена она справочникам. С одной стороны — это самый простой объект, с другой стороны, именно про справочник спрашивалось на форуме.
Если статья будет востребована, то я напишу аналогичные про документы, регистры и может еще что.
- Описание
- Подробнее
Описание
Смысл моей реализации – выполнение структурных изменений в базе SQL средствами самого SQL сервера с последующей заменой файлов 1cv7.md и 1cv7.dds в рабочей базе. Причем так, чтобы 1С не «догадалась», что эти изменения сделала не она сама.
Опытным путем можно проверить, что, например, изменение интерфейсов, прав, перечислений – не являются, по сути, структурными изменениями. Поэтому в данном случае достаточно просто подменить 1cv7.md.
Совсем по другому дело обстоит со справочниками, документами, регистрами и т.п.
Предупреждение: все алгоритмы проверяйте на тестовых базах и не забывайте про бэкап.
Помните: drop table и truncate table выполняются очень быстро 
Все скрипты приведены для MS SQL версии 2005 и выше. Для версии 2000 будут некоторые отличия в синтаксисе.
Итак, начнем. Будем двигаться от простого к сложному.
После каждого шага Вы можете подменять файлы md и dds в базе Тест и проверять работоспособность.
Для начала создадим самую простую конфигурацию (на sql сервере назовем ее Test). Это будет «рабочая база». Заполним ее немного данными.
Выгрузку конфигурации до изменений и после всех изменений, при желании, можно скачать в приложенном файле.
Через выгрузку-загрузку (для быстроты) создадим еще одну базу (на sql сервере назовем ее Test2). Это будет «копия». Причем копию при реальной работе можно и нужно делать без данных: т.е. просто скопировать 1cv7.md в пустую папку и запустить конфигуратор. Так изменения будут сохранятся гораздо быстрее.
Теперь наши изменения: в конфигураторе изменения вносим в базе Тест2, скрипты выполняем в контексте базы Тест.
1. Добавление признака «Отбор для реквизита» для реквизита «Вес» справочника Номенклатура.
В зависимости от наличия владельца и/или родителя в этом случае могут быть созданы 1 или 2 индекса. В нашем случае создался один индекс «VI14» с полями «Вес», «Наименование» и «row_id».
Сравнение файлов dds до и после изменений:
Задача самая простая: создать такой же индекс в рабочей базе. Предварительно проверяем его существование и удаляем, если он есть.
В этом, и некоторых последующих случаях, можно попросить SSMS сделать скрипт за нас. В нем будет много относительно лишнего кода – я постараюсь лишнее удалять, чтобы не путать.
Текст скрипта:
USE Test
GO
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[SC12]’) AND name = N’VI14′)
DROP INDEX [VI14] ON [dbo].[SC12]
GO
CREATE UNIQUE NONCLUSTERED INDEX [VI14] ON [dbo].[SC12] ([SP14] ASC, [DESCR] ASC, [ROW_ID] ASC)
2. Добавление колонки «Поставщик» (Строка (50)) в справочник Номенклатура.
Здесь мы рассматриваем добавление колонки в существующий справочник, в котором уже есть данные.
Основной нюанс здесь в том, что 1С 7.7 не допускает хранение NULL в колонках таблицы. Поэтому создавая колонку, необходимо указать дефолтовое значение для добавляемой колонки.
В нашем случае это будет пустая строка: ‘’. Если бы тип данных был «Число», то пустое значение было бы 0.
У агрегатных объектов, например, если бы колонка была типа «Справочник.Контрагенты» — пустое значение было бы ‘ 0 ’, если просто «Справочник» — то ‘ 0 0 ’ и т.д.
Сохраним изменения в базе Тест2:
Посмотрим отличия в DDS:
Дам некоторые пояснения по скрипту. В MS SQL указание значения по умолчанию – это создание объекта constraint в таблице.
Мой совет: чтобы данный скрипт можно было выполнить несколько раз в одной базе (например, в случае какого-либо сбоя) лучше давать констрейтантам явные имена. Если написать просто:
alter table SC12 add sp26 char(50) not null default »
то констрейнт будет иметь сгенерированное имя, причем при каждом удалении и создании колонки имя констрейнта будет меняться.
Перед удалением колонки нужно удалять все объекты, в которых данная колонка задействована (кроме таблиц конечно), в т.ч. констрейнты (вот здесь и понадобится имя), индексы и т.п.
В нашем случае индексов по колонке нет, поэтому удаляем констрейнт, затем саму колонку (если они есть) и создаем новую колонку нужного типа.
Текст скрипта:
use Test
go
if exists(select 1 from sys.objects where name = ‘df_sc12_sp26’ AND parent_object_id = OBJECT_ID(‘dbo.SC12’))
alter table SC12 drop constraint df_sc12_sp26
go
if exists (select 1 from sys.columns where name = ‘sp26’ AND object_id = OBJECT_ID(‘dbo.SC12’))
alter table SC12 drop column sp26
go
alter table SC12 add sp26 char(50) not null constraint df_sc12_sp26 default »
3. Изменение типа колонки «Поставщик» справочника Номенклатура
При изменении типа колонки, например изменении длины поля типа «строка», или изменение разрядности «числа», можно воспользоваться скриптом, приведенным ниже.
Поменяем длину поля Поставщик с 50 на 10 символов.
Обратите внимание: если в таблице уже есть строки больше 10 символов – преобразование на SQL из 50 в 10 не будет выполнено:

То же произойдет, если попытаться преобразовать колонку типа «Строка» в «Число» при наличии записей, которые не приводятся безусловно к типу число:

Если какая-то колонка не используется и Вы хотите задействовать ее под новые данные, то ее предварительно нужно удалить и создать новую с тем же идентификатором и в том же порядковом месте. Это будет рассматриваться при описании документов в следующей части статьи.
В скрипте проверяем наличие колонки и меняем тип.
Текст скрипта:
use Test
go
if exists (select 1 from sys.columns where name = ‘sp26’ AND object_id = OBJECT_ID(‘dbo.SC12’))
alter table SC12 alter column sp26 char(10) not null
go
4. Добавление справочника «Контрагенты»
И последний на сегодня пример: добавление справочника в конфигурацию. Добавляем справочник Контрагенты с одним реквизитом «ИНН».
Изменений в DDS гораздо больше:
Здесь помимо создания самой таблицы в SQL, необходимо создать соответствующие индексы и процедуры.
В общем случае, достаточно создать только таблицу, подменить md и запустить базу монопольно – 1С остальное сделает сама. Но наши цели: 1. Понимание процесса; 2. Возможность запуска без монопольного режима.
При создании скриптов (в т.ч. получения текста сгенерированных процедур) удобно использовать функции SSMS, как это описано в шаге 1.
По самому скрипту: как обычно – проверяем наличие объекта, если есть удаляем, создаем объект. Проверку наличия индексов не проверяем, т.к. при удалении таблицы – индексы также удаляются.
Текст скрипта:
use Test
go
—Таблицы
if exists (select 1 from sys.objects where object_id = OBJECT_ID(‘dbo.SC27’))
drop table dbo.sc27
go
create table dbo.SC27(
[ROW_ID] [int] IDENTITY(1,1) NOT NULL,
[ID] [char](9) NOT NULL,
[CODE] [char](5) NOT NULL,
[DESCR] [char](25) NOT NULL,
[ISMARK] [bit] NOT NULL,
[VERSTAMP] [int] NOT NULL,
[SP29] [char](12) NOT NULL,
constraint [PK_SC27] primary key clustered ([ROW_ID] asc)
)
go
—Индексы
create unique nonclustered index CODE on dbo.SC27 (CODE ASC, ROW_ID ASC)
go
create unique nonclustered index DESCR on dbo.SC27 (DESCR ASC, ROW_ID ASC)
go
create unique nonclustered index IDD on dbo.SC27 (ID ASC)
go
—Процедуры
if exists (select 1 from sys.objects where object_id = OBJECT_ID(‘dbo._1sp_SC27_ByID’))
drop procedure [dbo].[_1sp_SC27_ByID]
go
create procedure dbo._1sp_SC27_ByID (@id CHAR(9)) AS
select * from SC27 with (nolock) where ID=@id
go
if exists (select 1 from sys.objects where object_id = OBJECT_ID(‘dbo._1sp_SC27_MaxID’))
drop procedure dbo._1sp_SC27_MaxID
go
create procedure dbo._1sp_SC27_MaxID (@id CHAR(9) OUTPUT) AS
set nocount on select @id=MAX(ID) from SC27 with (nolock) if @id is null select @id=’ ‘
go
if exists (select 1 from sys.objects where object_id = OBJECT_ID(‘dbo._1sp_SC27_MaxRowID’))
drop procedure dbo._1sp_SC27_MaxRowID
go
create procedure dbo._1sp_SC27_MaxRowID (@i int OUTPUT) AS
set nocount on select @i=MAX(ROW_ID) from SC27 with (nolock) if @i is null select @i=0
go
if exists (select 1 from sys.objects where object_id = OBJECT_ID(‘dbo._1sp_SC27_TLock’))
drop procedure dbo._1sp_SC27_TLock
go
create procedure dbo._1sp_SC27_TLock AS
set nocount on declare @i int select @i=1 from SC27 with (tablock, holdlock) where 0=1
go
if exists (select 1 from sys.objects where object_id = OBJECT_ID(‘dbo._1sp_SC27_TLockX’))
drop procedure dbo._1sp_SC27_TLockX
go
create procedure dbo._1sp_SC27_TLockX AS
set nocount on declare @i int select @i=1 from SC27 with (tablockx, holdlock) where 0=1
go
