Тестирование и исправление базы 1С (дальше — ТиИ) — режим конфигуратора, который проверяет таблицы информационной базы и исправляет найденное: индексы, итоги регистров, неверные записи, ссылки на удалённые объекты. Он требует монопольного режима, а исправление необратимо, поэтому порядок важнее набора галочек: копия, «Только тестирование», разбор отчёта и лишь потом исправление.
Для медленной базы ТиИ — не первое средство: сначала найдите причину тормозов. «Ошибку формата потока» у одного-двух пользователей обычно снимает очистка кэша.
Если база не открывается после сбоя динамического обновления, одного тестирования мало — см. ошибка динамического обновления 1С. А если база просто разрослась за годы, поможет свёртка базы.
Окно называется «Тестирование и исправление информационной базы», названия — по конфигуратору 8.3.27. По справке, проверки целостности, пересчёт итогов, реструктуризация и реиндексация есть в обоих вариантах базы, сжатие — только в файловом.
| Галочка | Что делает |
|---|---|
| Реиндексация таблиц информационной базы | Заново строит индексы таблиц, данные не меняет |
| Проверка логической целостности информационной базы | Сверяет записи со структурой конфигурации: типы значений, NULL, уникальность, нумерацию. При исправлении может удалить записи: «Неверные записи удалены.» |
| Проверка ссылочной целостности информационной базы | Ищет ссылки на несуществующие объекты, работает только вместе с логической |
| Пересчет итогов | Пересчитывает таблицы итогов регистров по движениям, движения не трогает |
| Сжатие таблиц информационной базы | Файловая база: убирает пустое место, оставшееся от удалённых данных |
| Реструктуризация таблиц информационной базы | Пересоздаёт таблицы и переносит в них данные, это долго |
| Проверка логической целостности расширений конфигурации, Реструктуризация таблиц расширений конфигурации | То же для данных расширений; реструктуризация — только вместе с проверкой и в режиме исправления |
Остальные пункты 8.3.27 (двоичные данные, размещение таблиц, автономная конфигурация) без причины не трогайте.
Монопольный режим нужен всем режимам, кроме проверок расширений. По риску для данных:
| Ситуация | Галочки | Режим |
|---|---|---|
| Профилактика, непонятные ошибки | Логическая и ссылочная целостность | Только тестирование |
| «<Объект не найден>» в документах | Логическая и ссылочная целостность | Тест, затем исправление с «Создавать объекты» |
| Не сходятся остатки | Пересчет итогов | Исправление или код без простоя |
| Ошибка индекса | Реиндексация | Тестирование и исправление |
| Ошибка в схеме базы данных | Реструктуризация | Тестирование и исправление |
| Файловая база после аварии | chdbfl, затем реиндексация, логическая и ссылочная целостность, итоги | Тест, затем исправление |
Утилита лежит в каталоге bin платформы (C:\Program Files\1cv8\<версия>\bin\chdbfl.exe), окно — «Проверка физической целостности файла БД». Она проверяет сам файл 1Cv8.1CD, поэтому её запускают до ТиИ, когда база закрыта и в конфигураторе, и в «1С:Предприятии».
| Итог chdbfl | Что дальше |
|---|---|
| «Ошибок не обнаружено» | Файл цел, дальше — ТиИ |
| «Обнаружены несоответсвия данных и индексов таблиц, требуется реиндексация» (так в программе) | ТиИ с реиндексацией |
| «Обнаружены нарушения ограничений уникальности индексов. Продолжать работать можно.» | ТиИ: реиндексация, логическая целостность |
| «Есть неприятности, но продолжать работать можно», «Обнаружены повреждения, при работе могут возникать ошибки, требуется восстановление» | Исправление chdbfl на копии, затем ТиИ |
| «Часть данных повреждена или безвозвратно утеряна, требуется восстановление» | Резервная копия; исправленному файлу — только после сверки |
| «База данных полностью разрушена, восстановление невозможно» | Только резервная копия |
Клиент-серверную базу физически проверяют средствами СУБД (в MS SQL Server — DBCC CHECKDB), а индексы и статистики обслуживают планом обслуживания СУБД, а не ТиИ.
Всё из окна доступно ключом /IBCheckAndRepair пакетного конфигуратора, так проверку ставят на ночь в планировщик. Скрипт делает копию в .dt и только тестирует, исправление включите после разбора лога.
@echo off
rem tii-night.cmd - копия и тестирование базы 1С по расписанию (Планировщик заданий Windows).
rem Перед запуском закройте вход в базу: пакетному конфигуратору тоже нужен монопольный режим.
rem Имя и пароль администратора базы задайте переменными V8USER и V8PWD, а не в тексте файла.
rem В базе без пользователей уберите из RUN ключи /N и /P. Файл сохраните в ANSI.
setlocal
rem 1cv8.exe той версии платформы, на которой работает база.
set V8="C:\Program Files\1cv8\<версия>\bin\1cv8.exe"
set IB=/F "D:\Bases\Trade"
rem Клиент-серверная база: set IB=/S "srv01\trade"
set OUT=D:\Backup\tii
if not exist "%OUT%" mkdir "%OUT%"
set RUN=%V8% DESIGNER %IB% /N "%V8USER%" /P "%V8PWD%" /DisableStartupDialogs
rem 1. Копия в dt. Не выгрузилась - дальше не идём, смотрим 1-dump.log.
%RUN% /DumpIB "%OUT%\before.dt" /Out "%OUT%\1-dump.log"
if errorlevel 1 exit /b 1
rem 2. Только тестирование логической и ссылочной целостности: данные не меняются.
%RUN% /IBCheckAndRepair -LogAndRefsIntegrity -TestOnly /Out "%OUT%\2-test.log"
rem 3. Исправление: раскомментируйте, когда разберёте 2-test.log и выберете варианты.
rem -TimeLimit остановит работу через 5 часов, -UseStartPoint в следующий раз продолжит с места остановки.
rem -IBCompression (сжатие) добавляйте только для файловой базы.
rem %RUN% /IBCheckAndRepair -ReIndex -LogAndRefsIntegrity -RecalcTotals -BadRefCreate -BadDataCreate -TimeLimit:005:00 -UseStartPoint /Out "%OUT%\3-repair.log"
endlocal
Ключи: -ReIndex (реиндексация), -LogIntegrity (логическая целостность), -LogAndRefsIntegrity (логическая и ссылочная, можно ограничить: -LogAndRefsIntegrity Catalogs,Documents), -RecalcTotals (итоги), -IBCompression (сжатие), -Rebuild (реструктуризация), -TestOnly (только тестирование); варианты исправления — -BadRefCreate, -BadRefClear, -BadRefNone, -BadDataCreate, -BadDataDelete. Для клиент-серверной базы есть -JobsCount: число системных фоновых заданий, 0 — по числу ядер сервера. Лог /Out пишется в UTF-8.
Галочке «Пересчет итогов» нужен монопольный режим, методам менеджера регистра — нет: хватит права «Управление итогами». Первая функция сверяет остатки по итогам с суммой движений и отвечает, нужен ли пересчёт. Как устроены итоги — в статье о регистре накопления.
// Серверный общий модуль (например, ОбслуживаниеИтогов) или модуль объекта обработки.
// Сверяет остатки регистра накопления вида «Остатки»: по итогам и по сумме движений.
// Возвращает таблицу расхождений: измерения, <Ресурс>ПоИтогам, <Ресурс>ПоДвижениям.
// Пустая таблица - итоги сходятся с движениями, пересчитывать нечего.
Функция РасхожденияИтоговРегистра(Знач ИмяРегистра, Знач МаксимумСтрок = 1000) Экспорт
МетаРегистр = Метаданные.РегистрыНакопления.Найти(ИмяРегистра);
Если МетаРегистр = Неопределено
Или МетаРегистр.ВидРегистра <> Метаданные.СвойстваОбъектов.ВидРегистраНакопления.Остатки Тогда
ВызватьИсключение СтрШаблон("%1 - не регистр накопления остатков", ИмяРегистра);
КонецЕсли;
// При выключенных итогах виртуальная таблица Остатки недоступна.
Если Не РегистрыНакопления[МетаРегистр.Имя].ПолучитьИспользованиеИтогов() Тогда
ВызватьИсключение СтрШаблон("У регистра %1 выключены итоги", ИмяРегистра);
КонецЕсли;
ПоляИтогов = Новый Массив;
ПоляДвижений = Новый Массив;
Группировка = Новый Массив;
Связи = Новый Массив;
ПоляРезультата = Новый Массив;
Условия = Новый Массив;
Для Каждого Измерение Из МетаРегистр.Измерения Цикл
Поле = Измерение.Имя;
ПоляИтогов.Добавить(СтрШаблон("Т.%1 КАК %1", Поле));
ПоляДвижений.Добавить(СтрШаблон("Д.%1 КАК %1", Поле));
Группировка.Добавить("Д." + Поле);
Связи.Добавить(СтрШаблон("Ит.%1 = Дв.%1", Поле));
ПоляРезультата.Добавить(СтрШаблон("ЕСТЬNULL(Ит.%1, Дв.%1) КАК %1", Поле));
КонецЦикла;
Для Каждого Ресурс Из МетаРегистр.Ресурсы Цикл
Поле = Ресурс.Имя;
ПоляИтогов.Добавить(СтрШаблон("Т.%1Остаток КАК %1", Поле));
ПоляДвижений.Добавить(СтрШаблон("СУММА(ВЫБОР КОГДА Д.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)"
+ " ТОГДА Д.%1 ИНАЧЕ -Д.%1 КОНЕЦ) КАК %1", Поле));
ПоляРезультата.Добавить(СтрШаблон("ЕСТЬNULL(Ит.%1, 0) КАК %1ПоИтогам", Поле));
ПоляРезультата.Добавить(СтрШаблон("ЕСТЬNULL(Дв.%1, 0) КАК %1ПоДвижениям", Поле));
Условия.Добавить(СтрШаблон("ЕСТЬNULL(Ит.%1, 0) <> ЕСТЬNULL(Дв.%1, 0)", Поле));
КонецЦикла;
Текст = Новый Массив;
Текст.Добавить("ВЫБРАТЬ " + СтрСоединить(ПоляИтогов, ", "));
Текст.Добавить("ПОМЕСТИТЬ ПоИтогам");
Текст.Добавить("ИЗ РегистрНакопления." + МетаРегистр.Имя + ".Остатки КАК Т");
Текст.Добавить(";");
Текст.Добавить("ВЫБРАТЬ " + СтрСоединить(ПоляДвижений, ", "));
Текст.Добавить("ПОМЕСТИТЬ ПоДвижениям");
Текст.Добавить("ИЗ РегистрНакопления." + МетаРегистр.Имя + " КАК Д");
Текст.Добавить("ГДЕ Д.Активность"); // неактивные записи в итоги не входят
Если Группировка.Количество() > 0 Тогда
Текст.Добавить("СГРУППИРОВАТЬ ПО " + СтрСоединить(Группировка, ", "));
КонецЕсли;
Текст.Добавить(";");
Текст.Добавить("ВЫБРАТЬ " + ?(МаксимумСтрок > 0, "ПЕРВЫЕ " + Формат(МаксимумСтрок, "ЧГ=0") + " ", "")
+ СтрСоединить(ПоляРезультата, ", "));
Текст.Добавить("ИЗ ПоИтогам КАК Ит ПОЛНОЕ СОЕДИНЕНИЕ ПоДвижениям КАК Дв");
Текст.Добавить("ПО " + ?(Связи.Количество() > 0, СтрСоединить(Связи, " И "), "ИСТИНА"));
Текст.Добавить("ГДЕ " + СтрСоединить(Условия, " ИЛИ "));
Запрос = Новый Запрос(СтрСоединить(Текст, Символы.ПС));
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
Пример: РасхожденияИтоговРегистра("ТоварыНаСкладах"), регистр есть в УТ 11.4 и КА 2.5. Вторая функция обходит все регистры: включает итоги, оставшиеся выключенными после упавшей загрузки, и пересчитывает только регистры с расхождениями. С Исправлять = Ложь ничего не меняет — подходит для ночного регламентного задания.
// В том же модуле, что и РасхожденияИтоговРегистра. Монопольный режим не нужен,
// нужно право «Управление итогами». Исправлять = Ложь - только отчёт, база не меняется.
// Исправлять = Истина - включить выключенные итоги и пересчитать регистры остатков,
// у которых итоги разошлись с движениями. Возвращает строки отчёта и пишет их в журнал.
Функция ПроверитьИтогиРегистров(Знач Исправлять = Ложь) Экспорт
Отчет = Новый Массив;
ВидОстатки = Метаданные.СвойстваОбъектов.ВидРегистраНакопления.Остатки;
Для Каждого МетаРегистр Из Метаданные.РегистрыНакопления Цикл
Имя = МетаРегистр.Имя;
Менеджер = РегистрыНакопления[Имя];
ЭтоОстатки = (МетаРегистр.ВидРегистра = ВидОстатки);
Если Не ПравоДоступа("УправлениеИтогами", МетаРегистр) Тогда
Отчет.Добавить(Имя + ": нет права «Управление итогами», пропущен");
Продолжить;
КонецЕсли;
// Оборотный регистр в режиме агрегатов итоги не использует.
Если Не ЭтоОстатки И Менеджер.ПолучитьРежимАгрегатов() Тогда
Продолжить;
КонецЕсли;
Попытка
Если Не Менеджер.ПолучитьИспользованиеИтогов() Тогда
Отчет.Добавить(Имя + ": итоги выключены, отчёты по регистру не работают");
Если Исправлять Тогда
// При включении платформа досчитает итоги по движениям, записанным без них.
Менеджер.УстановитьИспользованиеИтогов(Истина);
Отчет.Добавить(Имя + ": итоги включены");
КонецЕсли;
Продолжить;
КонецЕсли;
Если Не ЭтоОстатки Тогда
Продолжить; // для оборотных регистров сверку не строим
КонецЕсли;
Расхождения = РасхожденияИтоговРегистра(Имя, 10);
Если Расхождения.Количество() = 0 Тогда
Продолжить;
КонецЕсли;
Отчет.Добавить(СтрШаблон("%1: итоги расходятся с движениями, строк: %2%3", Имя,
Расхождения.Количество(), ?(Расхождения.Количество() = 10, " и больше", "")));
Если Исправлять Тогда
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
Менеджер.ПересчитатьИтоги();
Отчет.Добавить(СтрШаблон("%1: итоги пересчитаны за %2 с", Имя,
Формат((ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало) / 1000, "ЧДЦ=1; ЧН=0")));
КонецЕсли;
Исключение
Отчет.Добавить(Имя + ": " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЦикла;
// Регистры бухгалтерии сверкой не проверяем, только включаем выключенные итоги.
Для Каждого МетаРегистр Из Метаданные.РегистрыБухгалтерии Цикл
Менеджер = РегистрыБухгалтерии[МетаРегистр.Имя];
Если ПравоДоступа("УправлениеИтогами", МетаРегистр) И Не Менеджер.ПолучитьИспользованиеИтогов() Тогда
Отчет.Добавить(МетаРегистр.Имя + ": итоги регистра бухгалтерии выключены");
Если Исправлять Тогда
Менеджер.УстановитьИспользованиеИтогов(Истина);
Отчет.Добавить(МетаРегистр.Имя + ": итоги включены");
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЗаписьЖурналаРегистрации("ОбслуживаниеБазы.ПроверкаИтогов",
?(Отчет.Количество() = 0, УровеньЖурналаРегистрации.Информация, УровеньЖурналаРегистрации.Предупреждение),
, , ?(Отчет.Количество() = 0, "Расхождений итогов не найдено", СтрСоединить(Отчет, Символы.ПС)));
Возврат Отчет;
КонецФункции
Сообщения ТиИ видны в окне конфигуратора и в логе /Out, а в журнале регистрации есть события «Тестирование и исправление. Ошибка», «…Предупреждение» и «…Сообщение» (где найти журнал). Функции ниже соберут их в таблицу и сводку.
// Что нашло и исправило «Тестирование и исправление»: записи журнала регистрации
// с событиями «Тестирование и исправление. Ошибка / Предупреждение / Сообщение».
// Серверный код, нужно право «Журнал регистрации». ДатаНачала - время запуска ТиИ.
Функция ЖурналТестированияИИсправления(Знач ДатаНачала, Знач ДатаОкончания = Неопределено) Экспорт
События = Новый Массив;
События.Добавить("_$InfoBase$_.VerifyAndRepairImportant"); // ... Ошибка
События.Добавить("_$InfoBase$_.VerifyAndRepairMessage"); // ... Предупреждение
События.Добавить("_$InfoBase$_.VerifyAndRepairInfo"); // ... Сообщение
Отбор = Новый Структура("ДатаНачала, Событие", ДатаНачала, События);
Если ЗначениеЗаполнено(ДатаОкончания) Тогда
Отбор.Вставить("ДатаОкончания", ДатаОкончания);
КонецЕсли;
Записи = Новый ТаблицаЗначений;
ВыгрузитьЖурналРегистрации(Записи, Отбор,
"Дата,Уровень,ПредставлениеСобытия,ПредставлениеМетаданных,Комментарий");
Записи.Сортировать("Дата");
Возврат Записи;
КонецФункции
// Сводка для отчёта: сколько записей каждого вида.
Функция СводкаТестированияИИсправления(Знач Записи) Экспорт
Сводка = Записи.Скопировать(, "ПредставлениеСобытия");
Сводка.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
Сводка.ЗаполнитьЗначения(1, "Количество");
Сводка.Свернуть("ПредставлениеСобытия", "Количество");
Сводка.Сортировать("Количество Убыв");
Строки = Новый Массив;
Для Каждого СтрокаСводки Из Сводка Цикл
Строки.Добавить(СтрШаблон("%1: %2", СтрокаСводки.ПредставлениеСобытия, СтрокаСводки.Количество));
КонецЦикла;
Возврат ?(Строки.Количество() = 0, "Записей тестирования и исправления нет", СтрСоединить(Строки, Символы.ПС));
КонецФункции
Форма внешней обработки: проверка и пересчёт итогов, журнал ТиИ за сутки. Функции выше положите в модуль объекта этой обработки.
// Модуль формы внешней обработки «Обслуживание итогов».
// Функции из листингов выше лежат в модуле объекта этой обработки.
// Команды формы: ПроверитьИтоги, ИсправитьИтоги, ЖурналТиИЗаСутки.
&НаКлиенте
Процедура ПроверитьИтоги(Команда)
ПоказатьОтчет(ПроверитьИтогиНаСервере(Ложь), "Проверка итогов");
КонецПроцедуры
&НаКлиенте
Процедура ИсправитьИтоги(Команда)
Оповещение = Новый ОписаниеОповещения("ИсправитьИтогиЗавершение", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Пересчитать итоги регистров с расхождениями? На большой базе это долго.",
РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ИсправитьИтогиЗавершение(Ответ, ДополнительныеПараметры) Экспорт
Если Ответ = КодВозвратаДиалога.Да Тогда
ПоказатьОтчет(ПроверитьИтогиНаСервере(Истина), "Пересчёт итогов");
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ЖурналТиИЗаСутки(Команда)
ПоказатьОтчет(ЖурналТиИНаСервере(), "Тестирование и исправление за сутки");
КонецПроцедуры
&НаСервере
Функция ПроверитьИтогиНаСервере(Исправлять)
Отчет = РеквизитФормыВЗначение("Объект").ПроверитьИтогиРегистров(Исправлять);
Возврат ?(Отчет.Количество() = 0, "Расхождений итогов не найдено", СтрСоединить(Отчет, Символы.ПС));
КонецФункции
&НаСервере
Функция ЖурналТиИНаСервере()
Обработка = РеквизитФормыВЗначение("Объект");
Записи = Обработка.ЖурналТестированияИИсправления(ТекущаяДатаСеанса() - 86400);
Строки = Новый Массив;
Строки.Добавить(Обработка.СводкаТестированияИИсправления(Записи));
Для Каждого Запись Из Записи Цикл
Строки.Добавить(СтрШаблон("%1 %2: %3", Запись.Дата, Запись.ПредставлениеСобытия, Запись.Комментарий));
КонецЦикла;
Возврат СтрСоединить(Строки, Символы.ПС);
КонецФункции
&НаКлиенте
Процедура ПоказатьОтчет(Текст, Заголовок)
Документ = Новый ТекстовыйДокумент;
Документ.УстановитьТекст(Текст);
Документ.Показать(Заголовок);
КонецПроцедуры
ВидРегистра: сверка возможна только у регистров остатков — у оборотных нет таблицы Остатки и поля ВидДвижения.ПолучитьИспользованиеИтогов(): при выключенных итогах виртуальные таблицы недоступны, функция сразу называет причину.ГДЕ Д.Активность: неактивные движения в итоги не входят, без отбора будут ложные расхождения.ПОЛНОЕ СОЕДИНЕНИЕ с ЕСТЬNULL ловит строки, которые есть только с одной стороны.ПолучитьРежимАгрегатов(): оборотный регистр в режиме агрегатов итоги не использует.УстановитьИспользованиеИтогов(Истина) по справке досчитывает итоги по движениям, записанным при выключенных итогах. ПересчитатьИтоги() — полный пересчёт, есть ещё ПересчитатьИтогиЗаПериод() и ПересчитатьТекущиеИтоги().ВыгрузитьЖурналРегистрации принимает в отборе Событие массив системных имён; без права «Журнал регистрации» вызывает исключение.Нет, нужен монопольный режим. По справке пакетного режима без него обходятся только проверка и реструктуризация расширений. Итоги пересчитывают без простоя — обработкой «Управление итогами и агрегатами» или кодом выше.
Логическую и ссылочную целостность в режиме «Только тестирование», для файловой базы — после chdbfl.exe. Реиндексация, итоги, сжатие и реструктуризация — операции, а не проверки: их добавляют по отчёту.
Зависит от размера базы и галочек — засеките время на копии. Прервать можно Ctrl+Break, а работать окнами помогают «Прервать выполнение проверки через» и «Продолжить прерванное ранее тестирование».
Метода встроенного языка нет: по расписанию ТиИ запускают пакетным конфигуратором, как в скрипте выше. Из кода доступны сверка и пересчёт итогов и чтение журнала ТиИ.
Если база не открывается, chdbfl сообщает о безвозвратной потере данных, а свежей копии нет, не экспериментируйте на единственном экземпляре. Опишите симптомы, приложите логи /Out и разместите задачу на площадке: оценка бесплатная, ответ в течение рабочего дня. Другие работы с базой — в разделе услуг.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.