# Урезание логов и перевод баз 1С:Предприятие в модель SIMPLE

## <span class="">Назначение</span>

<span class="">Скрипт предназначен для </span>**<span class="">продуктивных (рабочих) баз 1С:Предприятие</span>**<span class=""> на Microsoft SQL Server. Его задача — перевести все пользовательские базы в модель восстановления </span>`SIMPLE`<span class=""> и освободить место, занимаемое файлами журналов транзакций (LDF).</span>

<span class="">Данный подход является стандартной практикой для продуктовых баз 1С, поскольку восстановление на момент времени (point-in-time recovery) в 1С связано с существенными сложностями и на практике применяется редко.</span>

> **<span class="">Внимание!</span>**<span class=""> Риски применения описаны в сноске в конце статьи. Ознакомьтесь с ними перед запуском скрипта на продуктовом контуре.</span>

---

## <span class="">Скрипт</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-sql"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">sql</span></div></div></div></div></div>```
----------------------------------------------------------
-- СЛУЖЕБНЫЕ ПЕРЕМЕННЫЕ
DECLARE @db_name      NVARCHAR(100); -- имя базы данных
DECLARE @db_name_q    NVARCHAR(300); -- экранированное имя базы
DECLARE @sql          NVARCHAR(MAX); -- текст динамического SQL

DECLARE cursor_size_srv CURSOR FOR

-- Выбираем все базы, кроме системных
SELECT name AS DBName
FROM sys.databases
WHERE name NOT IN ('master','msdb','model','tempdb')
ORDER BY name;

-- Цикл по всем базам, попавшим в выборку
OPEN cursor_size_srv;
FETCH NEXT FROM cursor_size_srv INTO @db_name;

WHILE (@@FETCH_STATUS = 0)
BEGIN
    -- Экранируем имя базы (оборачиваем в [ ] и удваиваем ])
    SET @db_name_q = QUOTENAME(@db_name);

    -- Формируем текст динамического SQL
    SET @sql = N'
        DECLARE @logname NVARCHAR(100);
        USE ' + @db_name_q + N';
        SELECT @logname = name FROM sys.database_files WHERE type = 1;
        ALTER DATABASE ' + @db_name_q + N' SET RECOVERY SIMPLE;
        DBCC SHRINKFILE (@logname, EMPTYFILE);';

    -- Выполняем
    EXEC (@sql);

    -- Следующая база данных
    FETCH NEXT FROM cursor_size_srv INTO @db_name;
END

CLOSE cursor_size_srv;
DEALLOCATE cursor_size_srv;
----------------------------------------------------------
```

<div class="md-code-block md-code-block-dark" id="bkmrk--1"><svg class="_9bc997d _33882ae" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg><svg class="_9bc997d _28d7e84" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg></div>---

## <span class="">Разбор логики работы</span>

### <span class="">1. Объявление переменных и курсора</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-sql-1"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">sql</span></div></div></div></div></div>```
DECLARE @db_name      NVARCHAR(100);
DECLARE @db_name_q    NVARCHAR(300);
DECLARE @sql          NVARCHAR(MAX);

DECLARE cursor_size_srv CURSOR FOR
SELECT name AS DBName
FROM sys.databases
WHERE name NOT IN ('master','msdb','model','tempdb')
ORDER BY name;
```

<div class="md-code-block md-code-block-dark" id="bkmrk--3"><svg class="_9bc997d _33882ae" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg><svg class="_9bc997d _28d7e84" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg></div><span class="">Курсор проходит по всем базам на экземпляре, исключая системные (</span>`master`<span class="">, </span>`msdb`<span class="">, </span>`model`<span class="">, </span>`tempdb`<span class="">). Это защищает от случайного изменения настроек системных баз.</span>

<span class="">Переменные:</span>

- `@db_name`<span class=""> — имя текущей базы из курсора.</span>
- `@db_name_q`<span class=""> — экранированное имя (в квадратных скобках).</span>
- `@sql`<span class=""> — текст динамического SQL для выполнения.</span>

### <span class="">2. Перебор баз в цикле</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-sql-2"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">sql</span></div></div></div></div></div>```
OPEN cursor_size_srv;
FETCH NEXT FROM cursor_size_srv INTO @db_name;

WHILE (@@FETCH_STATUS = 0)
BEGIN
    ...
    FETCH NEXT FROM cursor_size_srv INTO @db_name;
END
```

<div class="md-code-block md-code-block-dark" id="bkmrk--4"><svg class="_9bc997d _33882ae" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg><svg class="_9bc997d _28d7e84" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg></div><span class="">Стандартный паттерн работы с курсором: открыть, получить первую строку, обрабатывать в цикле, пока </span>`@@FETCH_STATUS = 0`<span class="">.</span>

### <span class="">3. Экранирование имени базы</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-sql-3"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">sql</span></div></div></div></div></div>```
SET @db_name_q = QUOTENAME(@db_name);
```

<div class="md-code-block md-code-block-dark" id="bkmrk--5"><svg class="_9bc997d _33882ae" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg><svg class="_9bc997d _28d7e84" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg></div><span class="">Функция </span>`QUOTENAME()`<span class=""> оборачивает имя в квадратные скобки и удваивает встречающиеся внутри </span>`]`<span class="">. Это делает скрипт устойчивым к любым именам баз:</span>

- **<span class="">Имена, начинающиеся с цифры</span>**<span class=""> (например, </span>`1C_Prod`<span class="">) — без экранирования вызывают синтаксическую ошибку, так как не являются допустимыми обычными идентификаторами SQL Server.</span>
- **<span class="">Имена с пробелами и спецсимволами</span>**<span class=""> (например, </span>`База 1С Прод`<span class="">).</span>

<span class="">Важно: </span>`QUOTENAME()`<span class=""> вызывается </span>**<span class="">на уровне внешнего скрипта</span>**<span class="">, а не внутри строки </span>`EXEC`<span class="">. В строку динамического SQL подставляется уже готовое значение </span>`@db_name_q`<span class="">.</span>

### <span class="">4. Формирование и выполнение динамического SQL</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-sql-4"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">sql</span></div></div></div></div></div>```
SET @sql = N'
    DECLARE @logname NVARCHAR(100);
    USE ' + @db_name_q + N';
    SELECT @logname = name FROM sys.database_files WHERE type = 1;
    ALTER DATABASE ' + @db_name_q + N' SET RECOVERY SIMPLE;
    DBCC SHRINKFILE (@logname, EMPTYFILE);';

EXEC (@sql);
```

<div class="md-code-block md-code-block-dark" id="bkmrk--6"><svg class="_9bc997d _33882ae" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg><svg class="_9bc997d _28d7e84" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg></div><span class="">Ключевые шаги внутри динамического SQL:</span>

- **`USE [db_name]`**<span class=""> — переключение контекста на текущую базу (нужно для </span>`sys.database_files`<span class="">).</span>
- **`SELECT @logname = name FROM sys.database_files WHERE type = 1`**<span class=""> — получаем логическое имя файла журнала (</span>`type = 1`<span class="">).</span>
- **`ALTER DATABASE [db_name] SET RECOVERY SIMPLE`**<span class=""> — переводим базу в простую модель восстановления.</span>
- **`DBCC SHRINKFILE (@logname, EMPTYFILE)`**<span class=""> — очищаем файл журнала, перемещая все страницы в другой файл той же файловой группы.</span>

### <span class="">5. Особенность объявления </span>`@logname`

<span class="">Переменная </span>`@logname`<span class=""> объявлена </span>**<span class="">внутри динамического SQL</span>**<span class="">, а не снаружи. Это сделано намеренно: при смене контекста через </span>`USE`<span class=""> переменная из внешнего блока была бы недоступна для </span>`DBCC SHRINKFILE`<span class="">.</span>

### <span class="">6. Типы переменных</span>

- `@db_name_q NVARCHAR(300)`<span class=""> — с запасом под скобки и удвоение символов </span>`]`<span class="">.</span>
- `@sql NVARCHAR(MAX)`<span class=""> — снимает ограничение в 4000 символов, если имя базы длинное или добавляются дополнительные команды.</span>
- <span class="">Строковые литералы помечены префиксом </span>`N`<span class=""> для корректной работы с Unicode (кириллица в именах баз).</span>

---

## <span class="">Рекомендации по применению</span>

### <span class="">Перед запуском</span>

1. **<span class="">Сделайте полный бэкап</span>**<span class=""> всех целевых баз. Это обязательное условие.</span>
2. **<span class="">Убедитесь, что модель SIMPLE допустима</span>**<span class=""> для всех баз, попадающих в выборку.</span>
3. **<span class="">Запускайте в окно обслуживания</span>**<span class=""> — </span>`SHRINKFILE`<span class=""> может блокировать операции записи.</span>
4. **<span class="">Проверьте initial size</span>**<span class=""> файла журнала. Если задано большое значение (например, 100 ГБ), шринк не уменьшит файл ниже этой отметки.</span>

### <span class="">После перевода в SIMPLE</span>

<span class="">Если требуется физически уменьшить файл журнала до разумного размера, добавьте после </span>`EMPTYFILE`<span class=""> команду:</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-sql-5"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">sql</span></div></div></div></div></div>```
DBCC SHRINKFILE (@logname, 512); -- 512 МБ, подберите под задачу
```

<div class="md-code-block md-code-block-dark" id="bkmrk--8"><svg class="_9bc997d _33882ae" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg><svg class="_9bc997d _28d7e84" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg></div>### <span class="">Компенсация отсутствия PITR</span>

<span class="">После перевода в </span>`SIMPLE`<span class=""> восстановление возможно только на момент последнего полного или дифференциального бэкапа. Необходимо:</span>

- **<span class="">Делать полные бэкапы часто</span>**<span class=""> — с учётом допустимого окна потери данных.</span>
- **<span class="">Использовать дифференциальные бэкапы</span>**<span class=""> каждые несколько часов для активных баз.</span>
- **<span class="">Регулярно проверять бэкапы восстановлением</span>**<span class=""> в тестовую базу.</span>

### <span class="">Проверка результата</span>

<div class="md-code-block md-code-block-dark" id="bkmrk-sql-6"><div class="md-code-block-banner-wrap"><div class="md-code-block-banner md-code-block-banner-lite"><div class="_121d384"><div class="d2a24f03"><span class="d813de27">sql</span></div></div></div></div></div>```
SELECT
    d.name AS DatabaseName,
    d.recovery_model_desc,
    f.name AS FileName,
    f.type_desc,
    f.size * 8 / 1024 AS SizeMB
FROM sys.databases d
JOIN sys.master_files f ON f.database_id = d.database_id
ORDER BY d.name, f.type_desc;
```

<div class="md-code-block md-code-block-dark" id="bkmrk--9"><svg class="_9bc997d _33882ae" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg><svg class="_9bc997d _28d7e84" fill="none" height="12" viewbox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="M-5.24537e-07 0C-2.34843e-07 6.62742 5.37258 12 12 12L0 12L-5.24537e-07 0Z" fill="currentColor"></path></svg></div>### <span class="">Особенность 1С: журнал может не урезаться до минимума</span>

<span class="">Даже в модели </span>`SIMPLE`<span class=""> размер журнала транзакций определяется </span>**<span class="">максимальным размером одной транзакции</span>**<span class="">. В 1С это особенно заметно при массивных операциях: перепроведение документов, закрытие месяца, реструктуризация базы. Если типичная операция занимает несколько гигабайт в логе, минимальный размер LDF не будет меньше этой величины.</span>

<span class="">Если журнал снова быстро растёт после урезания — проверьте, нет ли аномально длительных операций в 1С.</span>

---

## <span class="">Сноска: риски применения</span>

> **<span class="">Внимание! Перед запуском скрипта на продуктовом контуре ознакомьтесь с рисками.</span>**

**<span class="">1. Потеря возможности восстановления на момент времени (PITR).</span>**

<span class="">Перевод базы в модель </span>`SIMPLE`<span class=""> делает невозможным восстановление на произвольный момент между полными бэкапами. При сбое вы сможете восстановиться только на момент последнего полного или дифференциального бэкапа. Вся работа, выполненная после него, будет потеряна.</span>

**<span class="">2. Несовместимость с технологиями репликации.</span>**

<span class="">Модель </span>`SIMPLE`<span class=""> не поддерживается для групп доступности Always On (AG), лог-шиппинга (Log Shipping) и репликации транзакций. Если эти технологии настроены, их работа будет нарушена.</span>

**<span class="">3. Невозможность точечного отката ошибок пользователей.</span>**

<span class="">При ошибочных действиях пользователя (массовое удаление, некорректное перепроведение документов, закрытие смены ККМ) откатить изменения до нужного момента без PITR невозможно — придётся восстанавливать базу из последнего бэкапа и повторно вводить данные.</span>

**<span class="">4. Блокировка операций во время SHRINKFILE.</span>**

`DBCC SHRINKFILE (... , EMPTYFILE)`<span class=""> может длительное время ожидать завершения активных транзакций. На продуктовой базе это приведёт к блокировкам и замедлению работы пользователей.</span>

**<span class="">5. Журнал может не уменьшиться.</span>**

<span class="">Даже после перевода в </span>`SIMPLE`<span class=""> и шринка файл журнала не станет меньше максимального размера одной транзакции. Для 1С характерны крупные транзакции (закрытие месяца, перепроведение, реструктуризация), поэтому ожидания «лога в 100 МБ» могут не оправдаться.</span>

**<span class="">6. Ограничение по initial size.</span>**

<span class="">Если в свойствах базы задан большой начальный размер файла журнала, шринк не уменьшит файл ниже этой отметки. Требуется предварительная корректировка начального размера.</span>

**<span class="">7. Необходимость частых бэкапов.</span>**

<span class="">После перехода в </span>`SIMPLE`<span class=""> критически важно обеспечить регулярные полные и дифференциальные бэкапы. Без них при сбое возможна потеря существенного объёма данных.</span>

**<span class="">8. Требования аудита и регуляторов.</span>**

<span class="">Некоторые отрасли и внутренние регламенты требуют возможности восстановления на произвольный момент времени. Перевод в </span>`SIMPLE`<span class=""> может нарушить эти требования.</span>

**<span class="">Рекомендация.</span>**<span class=""> Перед запуском скрипта на продуктовом контуре согласуйте решение с ответственными за сохранность данных и убедитесь, что выбранная стратегия резервного копирования компенсирует отсутствие PITR.</span>

---

## <span class="">Итог</span>

<span class="">Скрипт предназначен для продуктовых баз 1С:Предприятие и решает задачу перевода баз в модель </span>`SIMPLE`<span class=""> с освобождением журналов транзакций. Благодаря </span>`QUOTENAME()`<span class=""> он корректно работает с любыми именами баз, включая начинающиеся с цифры и содержащие пробелы. Перед запуском на рабочем контуре обязательно ознакомьтесь со сноской о рисках и обеспечьте регулярное резервное копирование.</span>