Мазмұнға өту
Tez Base

Перенос базы 1С с PostgreSQL на MS SQL: смещение дат, индексы и приёмка

· Жарияланды:

PostgreSQL SQL Server и PostgreSQL

Перенос базы 1С с PostgreSQL на MS SQL Server штатно делается командой ibcmd infobase replicate, она есть в платформе с 8.3.23. До запуска у человека три вопроса: какое смещение дат ставить приёмнику, создадутся ли индексы и как потом убедиться, что данные доехали целыми.

Числа сняты на трёх базах и подписаны. Стенд - тестовая бухгалтерская база, PostgreSQL 17.9 на Windows, приёмник MS SQL Server 2022, платформа 8.3.27, 22 ГБ и 11 396 таблиц. УПП - база с боевыми данными на 8.3.20. УТ 11.5 - типовая на 8.3.27. Базы разного размера и возраста, их числа между собой не складываем.

Какое смещение дат ставить при переносе на MS SQL

Ставьте 2000. Это значение --target-date-offset, которое ibcmd для MS SQL подставляет сам, если его не трогать.

Минимальная дата платформы 01.01.0001, она же пустая дата. При смещении 0 платформа кладёт всё, что раньше 1753 года, в 01.01.1753. При смещении 2000 год 1 становится годом 2001 и хранится без потерь. На стенде колонки дат в приёмнике были объявлены как datetime2, который год 1 держит спокойно, так что режет именно платформа.

Первый прогон стенда мы запустили с нулём: источник на PostgreSQL живёт со смещением 0, пересчитывать даты не придётся. Для надёжности посмотрели две работающие базы MS SQL с тем же нулём. Обе оказались пустыми и доказали только то, что платформа такое смещение принимает.

Прогон вернул код 0 и “Репликация информационной базы успешно завершена”. Весь stderr занял одну строку на 208 байт:

[WARN] Таблица '_InfoRgXXXX' содержит значения типа Дата, которые не могут быть
записаны в MS SQL Server с нулевым смещением дат

Это же сообщение платформа показывает окном, когда в базу с нулевым смещением пишут дату раньше 1753 года. Шаги для обоих случаев собраны на странице ошибки о нулевом смещении дат.

В PostgreSQLВ MS SQL при смещении 0
0001-01-01 00:00:001753-01-01 00:00:00
0001-01-02 00:00:001753-01-01 00:00:00

Пустая дата и соседняя слились в одно значение, и ЗначениеЗаполнено() на ней вернёт истину. Задета одна таблица из 11 396, в ней шесть строк, и после переноса их по-прежнему шесть. Повторный прогон со смещением 2000 дал пустой stderr, а диапазон дат той же таблицы на уровне СУБД сдвинулся ровно на 2000 лет: 0001-01-01 .. 2026-07-20 стало 2001-01-01 .. 4026-07-20.

Как узнать смещение текущей базы

  • В СУБД. Смещение лежит в служебной таблице _YearOffset. У источника стенда на PostgreSQL она пустая, это ноль. У приёмника после переноса там 2000.
  • Средствами платформы, если база на MS SQL. Поищите даты раньше 1753 года. Нашлись - смещение у базы ненулевое: при нуле таким значениям там негде лежать.
  • В кластере. Смещение указывается и при регистрации базы в кластере. Приёмник регистрируйте с тем же значением, что стоит у самой базы.

Сколько дат испортит смещение 0

Граница равна 1753 минус смещение, при смещении от 1752 проверять нечего. Прогон проверки при нуле:

БазаЧто нашлось
Стенд1 таблица, 6 строк
УТ 11.541 675 значений в 119 реквизитах, 1 480 таблиц за 10 секунд. Первым идёт производственный календарь, 8 893 значения
УПП3 502 101 значение в 30 реквизитах, и это оценка снизу: прогон ограничили бюджетом в 9 секунд, он прошёл 270 таблиц из 4 493. Крупнее всех три реквизита регистра расчёта, от 412 до 978 тысяч пустых дат

В регистрах себестоимости и производственном календаре пустая дата штатная, так что ноль ломает и чистую типовую базу.

Где обход обычно промахивается:

  • даты документов и периоды регистров лежат в полях без метаданных, их узнают по имени хранения _Date_Time и _Period;
  • дата составного реквизита живёт в колонке _T и достаётся через ВЫРАЗИТЬ(... КАК ДАТА);
  • из назначений таблиц нужны Основная, ТабличнаяЧасть и Константа, запрос к оборотам падает с “Поле не найдено”.

Все даты таблицы проверяются за один проход:

// Граница = Дата(1753 - Смещение, 1, 1). Псевдоним источнику не даём,
// иначе он однажды совпадёт с именем реквизита.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
|	КОЛИЧЕСТВО(ВЫБОР КОГДА Дата < &Граница ТОГДА 1 КОНЕЦ) КАК Kolvo0,
|	МИНИМУМ(ВЫБОР КОГДА Дата < &Граница ТОГДА Дата КОНЕЦ) КАК Minim0,
|	КОЛИЧЕСТВО(ВЫБОР КОГДА ВЫРАЗИТЬ(Значение КАК ДАТА) < &Граница ТОГДА 1 КОНЕЦ) КАК Kolvo1
|ИЗ
|	Документ.ВашДокумент";
Запрос.УстановитьПараметр("Граница", Дата(1753, 1, 1)); // смещение 0
// Печатая минимум, добавьте ДП=01.01.0001, иначе Формат() вернёт пустую строку.

Индексы на MS SQL: что создастся и что упадёт потом

Предел ключа индекса у MS SQL 900 байт для кластерного и 1700 для некластерного начиная с версии 2016. На 2012 и 2014 для любого индекса 900. У PostgreSQL около 2704, и ключ, который в источнике живёт годами, на приёмнике может выйти за предел.

Мы ждали, что перенос стенда на таком ключе упадёт. Он прошёл:

Замер на стендеЗначение
Создано индексов, по выводу ibcmd24 535, ошибок 0
Ключей шире 900 байт37, из них 14 кластерных
Ключей шире 1700 байт0
Самый широкий ключ1234 байта, 6 колонок

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

Что меняет версия SQL Server

ПриёмникСтенд: 37 ключей шире 900 байтУПП: 59 ключей шире 900 байт
2012 и 2014за пределом все 37за пределом все 59
2016 и новее23 некластерных в пределе, 14 кластерных за 90026 шире 1700 за пределом любой версии, 33 в серой зоне

Серая зона возникает, потому что платформа не сообщает, какой индекс кластерный. Ключ от 901 до 1700 байт на 2016 скорее всего пройдёт, но у кластерного предел 900. Точный ответ даст пробный перенос копии и запрос к приёмнику:

-- Ключи шире 900 байт: объявленная ширина и вид индекса.
SELECT o.name AS tablica, i.name AS indeks, i.type_desc AS vid,
       SUM(c.max_length) AS bajt, COUNT(*) AS kolonok
FROM sys.indexes AS i
JOIN sys.objects AS o        ON o.object_id = i.object_id AND o.type = 'U'
JOIN sys.index_columns AS ic ON ic.object_id = i.object_id AND ic.index_id = i.index_id
                            AND ic.is_included_column = 0
JOIN sys.columns AS c        ON c.object_id = ic.object_id AND c.column_id = ic.column_id
GROUP BY o.name, i.name, i.type_desc
HAVING SUM(c.max_length) > 900
ORDER BY bajt DESC;

Как посчитать ширину ключа до переноса

Колонки каждого индекса с именами хранения отдаёт ПолучитьСтруктуруХраненияБазыДанных(). Байты складываем по суффиксу имени хранения: колонки _TYPE, _RTRef, _RRRef, _S, _N составного реквизита указывают на один реквизит, и счёт по его типу прибавляет полную ширину к каждой. На одном индексе УПП так вышло 8 316 байт при настоящих 2 107.

Колонка или суффиксБайтКолонка или суффиксБайт
_TYPE1_S2 на символ строки
_RTRef, TRef4_N5, 9, 13 или 17 по разрядности
_RRRef, RRef, _IDRRef16_L, _Marked, _Posted, _Folder1
_Period, _T, любая дата6_LineNo, _KeyField5
_Code, _Description2 на символ длины_Version8 в объекте, 16 в служебной таблице

Дата занимает 6 байт: платформа заводит datetime2 с такой точностью. Восьмёрка от старого datetime добавляет по 2 лишних байта на каждую дату в ключе. Этот расчёт мы сверили с самой СУБД на УПП: 59 ключей шире 900 байт против 59, 26 шире 1700 против 26.

На УТ 11.5 самый широкий ключ у регистра сведений ЛогированиеПакетовОбмена, 2 167 байт, из них 2 058 даёт строковый реквизит Идентификатор. Лечится укорачиванием строки или снятием индексирования.

Служебные таблицы платформы

Их ключи в конфигурации не поправить. На УПП (8.3.20) из 59 ключей шире 900 байт 18 пришлись на служебные таблицы: хранилища настроек по 1 152 байта (_UserId 128 + _ObjectKey 512 + _SettingsKey 512) и тела сообщений сервисов интеграции, где один _MessageBodyKey занимает 1 024. На стенде (8.3.27) хранилище индексируется по числовым хешам, ключ 556 байт. Базы различаются и платформой, и конфигурацией, так что связь с версией платформы пока наблюдение. Выхода два: обновить платформу до переноса или взять приёмник посвежее.

Чем заменить подсчёт строк при приёмке

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

ВеличинаЛовитНе видит
Число строкпотерю строкизменение значений
Минимальная и максимальная дата документов и периодовсхлопывание в 1753 годчисла и строки
Число пустых дат по реквизитам из проверкипустые даты, ставшие 01.01.1753всё прочее
Суммы ресурсов крупных регистровпотерю строк, порчу чиселдаты: в сумму они не входят
Байты двоичных данных и конфигурациипорчу хранилищприкладные данные

Запросом платформы величины на обеих сторонах должны совпасть точно. На уровне СУБД стенд после прогона со смещением 2000 сошёлся по конфигурации (1 840 918 604 байта), двоичным данным (1 439 365 954 байта), схеме базы, трём регистрам и двум денежным ресурсам. Строки на PostgreSQL считайте count(*): n_live_tup у стенда показывал ноль по всем таблицам, ANALYZE после восстановления кластера никто не запускал.

Когда копия и рабочая база лежат на одном MS SQL, сверку за период через EXCEPT и контрольные суммы мы разбирали в статье про тестовую базу, которая отстала от рабочей.

Регламент переезда

ЭтапДействиеЗачем
ДоПроверить платформуreplicate есть с 8.3.23, из файловой базы в клиент-серверную вендор миграцию не поддерживает
ДоПроверить даты со смещением будущего приёмникаузнать, сколько значений ляжет в 1753 год
ДоПосчитать ключи под версию SQL Serverукоротить реквизиты, обновить платформу или выбрать 2016+
ДоСнять контрольный срез на источникебез величин “до” сверять не с чем
ДоСоздать базу-приёмник заранее--target-create-database создаёт базу по model, а готовую берёт молча
ДоПодобрать потоки под свободную памятьу стенда было 2 ГБ свободных: --jobs-count=2 --target-jobs-count=2, пик ibcmd 623 МБ
Во времяПисать stderr в отдельный файлпредупреждение о датах уходит только туда
Во времяСмотреть [INFO] Перенесено таблиц и занятое месторазмер предрасширенного файла стоит на месте
Во времяУдалённо запускать через планировщикStart-Process внутри Invoke-Command умирает с WinRM-сессией, как было у нас при переезде на Linux
ПослеОткрыть stderr при любом коде возвратапустой файл тоже результат
ПослеПовторить срез и сравнитьтаблица величин выше
ПослеЗарегистрировать базу с тем же смещениемкопии заодно --scheduled-jobs-deny=on у rac infobase create

В model на инстансе стенда стояла модель восстановления FULL, поэтому приёмник мы создали сами: 28 ГБ данных и 8 ГБ журнала сразу, Cyrillic_General_CI_AS, SIMPLE на время заливки. С мгновенной инициализацией файлов на это ушло 19,9 секунды. Настройки нового SQL Server после переезда проверьте чек-апом СУБД под 1С.

Грабли вокруг переноса

Аргументы с пробелами. Адрес вида 127.0.0.1 port=..., переданный массивом аргументов, рвётся по пробелу. С --descr у rac так же: слово из описания ушло в адрес сервера. Аргументы собираем одной строкой, значения с пробелами в кавычках, без точки с запятой после порта.

Пакетный конфигуратор для проверки базы. С /DisableStartupDialogs он отвечает “Пользователь ИБ не идентифицирован”, хотя база цела. Без ключа показывает окно входа и висит до ручного закрытия. Для автоматической приёмки не годится ни один вариант.

ibcmd infobase config save с выводом в файл. Команда требует входа в базу и при пустом stdin печатает “Имя пользователя:” в бесконечном цикле: 106 МБ одной строки за десять минут. Исходная база на PostgreSQL ведёт себя так же, это поведение утилиты.

Пользователи. Список пользователей копии мы сбросили сами, чтобы войти без пароля. Замер v8users дал ноль строк, а окно входа всё равно предлагало фамилию: имена платформа держит ещё и в таблице Params, в записи users.usr.

БСП и единственный администратор. Когда вход открылся, следующий запуск упал с сообщением, что не осталось бы ни одного пользователя с административными правами. Права при этом стояли. В УправлениеПользователями текущего пользователя вычёркивают из списка администраторов до проверки, ему ведь как раз меняют роли. Помог второй пользователь с полными правами, удалять его нельзя.

Усадка втрое. 22 ГБ на PostgreSQL стали 7 870 МБ данных на MS SQL. Полезного содержимого в двоичных данных источника было 1,44 ГБ, остальное раздутие хранилища, которое приехало вместе с физическим бэкапом кластера. Числа сняты разными способами, вычитать их бесполезно, а место под приёмник по объёму источника не планируйте.

Проверка дат и индексов одной обработкой

Обе проверки собраны в обработку “Проверка базы 1С перед переносом на MS SQL Server”, 10 SM на Инфостарте. Она ищет даты, которые не переживут выбранное смещение, и считает ключи индексов под версию SQL Server с разбором по реквизитам. К СУБД не подключается, в базу не пишет.

На пути в PostgreSQL всё наоборот: уникальный индекс становится строже, и перенос встаёт на невидимых символах. Мягче он там в одном месте: строки с пустым полем в ключе PostgreSQL дублями не считает, и служебные таблицы рядом с базой теряют защиту от повторов. Разбор в статье о миграции 1С на PostgreSQL, для того направления есть своя проверка перед переездом. А если уходите с PostgreSQL из-за скорости, сначала проверьте его настройку под 1С.

Репетицию переезда на копии, проверки до и сверку после мы делаем в рамках обслуживания баз 1С.

Мұны сіздің орныңызға жөндей аламын

Регламенттік қызмет көрсетуді база тұрақты жұмыс істейтіндей, бэкаптар кепілді түрде қалпына келетіндей және дискілер кенет таусылмайтындай баптаймыз.

Құны
450 000 ₸-денбір реттік баптау; сүйемелдеу - айына 150 000 ₸-ден; салықтарсыз

Жазуға ерте ме? Өзіңіз өлшеп көріңіз

Перенос на MS SQL отчитался успехом, а две даты стали одной

Өңдеуді ашу

INFOSTART TECH EVENT 2026: конференция для 1с-специалистов. если собираетесь, регистрируйтесь по нашей ссылке. Регистрация →

Мұны да оқыңыз

База 1С не открывается после обновления конфигурации: порядок действий, когда копии нет

Конфигуратор пишет "При обновлении данных, после последней реструктуризации, произошла критическая ошибка", пользователи в базу не попадают, копии до обновления нет. Разбираем по шагам: что сохранить первым, как по журналу регистрации найти этап, на котором упало, какие действия только добавляют ошибок, когда бросать починку старой базы и как через ibcmd выгрузить .dt из базы, которая не открывается, собрать новую и тихо подменить её в кластере. В конце - задания резервного копирования, после которых такая авария превращается в откат на минуту до обновления.

Остатки в 1С не сходятся с движениями: как найти сломанный месяц итогов и не пересчитывать всю историю

Отчёт по движениям даёт одно число, запрос к остаткам другое, перепроведение ничего не меняет. Разбираем, как отличить сломанные итоги от ошибки в движениях, каким запросом найти позицию и месяц расхождения, почему строку итогов на 1 августа чинит пересчёт за август, а не за июль, в каком порядке пересчитывать текущие итоги и как посмотреть саму строку итогов на SQL. Все шаги проверены на тестовой копии УТ 11.5 с испорченной строкой.

CHECKDB нашёл ошибки в базе 1С: порядок действий до REPAIR и после

CHECKDB советует repair_allow_data_loss и на битый индекс, и на потерянные документы. Разбираем по шагам, что делать в первый час: что сохранить до исправления, как по таблице и номеру индекса понять, что задето в 1С, как перестроить битый индекс без потерь и как получить список пропавших документов, пока REPAIR не удалил их молча. Проверено на копии УТ для Казахстана с испорченными страницами.