Регистры накопления в 1С:Предприятие — это один из ключевых объектов конфигурации, который позволяет хранить и обрабатывать данные о движениях товаров, денежных средств, остатках и других количественных показателях. Однако работа с ними часто вызывает сложности у начинающих разработчиков: как правильно получить остатки, обороты или провести аналитику по регистру? В этой статье мы разберём все актуальные способы обращения к регистрам накопления — от простых встроенных функций до сложных запросов с виртуальными таблицами.
Особое внимание уделим типичным ошибкам, которые приводят к замедлению системы или некорректным результатам. Например, многие разработчики не учитывают, что прямой доступ к данным регистра через ПолучитьДанные() может быть менее эффективным, чем использование виртуальных таблиц в запросах. Также рассмотрим нюансы работы с остатками на конкретную дату, обороты за период и особенности использования измерений и ресурсов.
Статья будет полезна как программистам 1С, так и администраторам, которые хотят оптимизировать работу системы. Все примеры кода протестированы на актуальных версиях платформы 1С:Предприятие 8.3 (включая 8.3.23).
1. Базовые понятия: что такое регистр накопления и зачем он нужен
Регистр накопления — это объект конфигурации 1С, предназначенный для хранения данных о движениях и остатках. В отличие от регистров сведений, которые хранят "статичные" данные (например, курсы валют), регистры накопления фиксируют динамику изменений по времени. Они используются для:
- 📦 Учёта товарных остатков на складах
- 💰 Контроля денежных средств в кассе или на расчётных счетах
- 📊 Аналитики продаж по периодам, контрагентам или номенклатуре
- 🔄 Отслеживания движения материалов в производстве
Каждый регистр накопления имеет:
- Измерения — параметры, по которым ведётся аналитика (например,
Склад,Номенклатура) - Ресурсы — количественные показатели (например,
Количество,Сумма) - Реквизиты — дополнительные атрибуты (например,
ВалютнаяСуммаилиСтавкаНДС)
Важно понимать, что регистры накопления бывают двух типов:
- Остатков — хранят текущее состояние (например, остатки товаров на складе).
- Оборотов — фиксируют движения за период (например, продажи за месяц).
⚠️ Внимание: Если в конфигурации используется управление по регистрам (например, в 1С:ERP или 1С:УТ), то неправильное обращение к регистрам может привести к расхождению данных с бухгалтерскими итогами. Всегда проверяйте логику работы регистров в Конфигураторе перед изменением кода.
2. Способ 1: Получение остатков через метод ПолучитьДанные()
Самый простой способ получить данные из регистра накопления — использовать встроенный метод ПолучитьДанные(). Он подходит для быстрого чтения остатков или оборотов без сложных фильтров. Синтаксис:
ДанныеРегистра = РегистрыНакопления.ИмяРегистра.ПолучитьДанные(
НачалоПериода,
КонецПериода,
ПараметрыОтбора,
Группировки
);
Пример: получим остатки товаров на складе "Основной" по состоянию на текущую дату:
ОстаткиТоваров = РегистрыНакопления.ТоварыНаСкладах.ПолучитьДанные(
НачалоДня(ТекущаяДата()),
КонецДня(ТекущаяДата()),
Новый Структура("Склад", Справочники.Склады.Основной)
);
Преимущества метода:
- 🔹 Простота использования — не требует знания языка запросов.
- 🔹 Быстрота для небольших объёмов данных.
Недостатки:
- 🐢 Медленная работа при больших выборках (более 10 000 записей).
- 🎯 Ограниченные возможности фильтрации и группировки.
⚠️ Внимание: Если вы используете ПолучитьДанные() в цикле (например, для перебора складов), это может привести к замедлению системы в 10-100 раз. В таких случаях лучше использовать запросы с виртуальными таблицами (см. раздел 4).
3. Способ 2: Работа с регистром как с объектом (метод Выбрать())
Регистр накопления можно обрабатывать как объект базы данных, используя метод Выбрать(). Это даёт больше гибкости, чем ПолучитьДанные(), но требует ручной обработки выборки. Пример:
Выборка = РегистрыНакопления.ТоварыНаСкладах.Выбрать(
НачалоДня(ТекущаяДата()),
КонецДня(ТекущаяДата()),
"Склад = &Склад",
Новый Структура("Склад", Справочники.Склады.Основной)
);
Пока Выборка.Следующий() Цикл
Сообщить(СтрШаблон("Номенклатура: %1, Остаток: %2",
Выборка.Номенклатура,
Выборка.КоличествоОстаток
));
КонецЦикла;
Ключевые особенности метода:
- 🔄 Позволяет обрабатывать данные построчно, не загружая всё в память.
- 🔍 Поддерживает отбор по нескольким параметрам (например, по складу и номенклатуре).
- 📉 Может быть медленнее запросов при больших объёмах данных.
Когда использовать Выбрать():
- 🛠️ Нужна постраничная обработка данных (например, для фонаовых задач).
- 🔄 Требуется модификация данных "на лету" (например, корректировка остатков).
Используйте индексы для полей отбора|Ограничьте период выборки|Избегайте вложенных циклов с Выбрать()|Закрывайте выборку после использования (Выборка.Закрыть())
-->
4. Способ 3: Виртуальные таблицы в запросах — оптимальный подход
Самый эффективный способ работы с регистрами накопления — использование виртуальных таблиц в языке запросов 1С. Виртуальные таблицы автоматически формируются платформой и позволяют получить остатки, обороты или срез последних данных за один проход.
Основные виртуальные таблицы регистров накопления:
| Тип таблицы | Описание | Пример использования |
|---|---|---|
Остатки |
Текущие остатки на дату | Анализ товарных запасов на складе |
Обороты |
Движения за период (приход/расход) | Отчёт о продажах за месяц |
ОстаткиИОбороты |
Остатки на начало/конец периода + обороты | Анализ динамики остатков |
СрезПоследних |
Последние записи по измерениям | Актуальные цены номенклатуры |
Пример запроса для получения остатков товаров на складе:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура,
| ТоварыНаСкладахОстатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОтчета, ) КАК ТоварыНаСкладахОстатки
|ГДЕ
| ТоварыНаСкладахОстатки.Склад = &Склад";
Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());
Запрос.УстановитьПараметр("Склад", Справочники.Склады.Основной);
Результат = Запрос.Выполнить();
Преимущества виртуальных таблиц:
- ⚡ Максимальная производительность — платформа оптимизирует выполнение запроса.
- 🔧 Гибкость — можно комбинировать с другими таблицами (справочниками, документами).
- 📊 Поддержка агрегатных функций (
СУММА,МАКСИМУМ).
⚠️ Внимание: При использовании виртуальной таблицы ОстаткиИОбороты с большим периодом (например, год) запрос может зависать. Разбивайте длинные периоды на более короткие интервалы (например, помесячно).
Что делать если запрос с виртуальной таблицей выполняется слишком долго?
Если запрос к регистру накопления выполняется более 30 секунд, проверьте:
1. Наличие индексов по полям отбора (в Конфигураторе).
2. Корректность указания периода (не используйте Дата = '' для "всех дат").
3. Возможность разбиения запроса на части (например, по месяцам).
4. Наличие блокировок в базе (через Администрирование → Тестирование и исправление).
5. Способ 4: Прямой SQL-запрос (для опытных пользователей)
В редких случаях, когда стандартные методы 1С неэффективны, можно обратиться к регистру накопления через прямой SQL-запрос. Это требует знания структуры базы данных и должно использоваться с осторожностью.
Пример SQL-запроса для получения остатков из регистра ТоварыНаСкладах (для SQL Server):
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| Т.КоличествоОстаток КАК Остаток
|ИЗ
| dbo._AccumRg12345 КАК Т // 12345 - внутренний ID регистра
|ГДЕ
| Т.Period = @Дата
| И Т.Sklad = @Склад";
Запрос.УстановитьПараметр("Дата", Формат(ТекущаяДата(), "ДЛФ=DT"));
Запрос.УстановитьПараметр("Склад", Справочники.Склады.Основной.УникальныйИдентификатор());
Результат = Запрос.Выполнить();
Опасности прямого SQL:
- 💥 Нарушение целостности данных при некорректных изменениях.
- 🔄 Зависимость от версии платформы (внутренние таблицы могут меняться).
- 🚫 Отсутствие поддержки со стороны 1С.
⚠️ Внимание: Прямые SQL-запросы могут привести к сбою поддержки конфигурации со стороны фирмы 1С. Используйте их только в крайних случаях и обязательно фиксируйте изменения в документации.
Перед использованием SQL проверьте, можно ли решить задачу стандартными средствами. Например, виртуальные таблицы ОстаткиИОбороты часто покрывают 90% потребностей в аналитике.
6. Способ 5: Использование менеджера регистра
Для сложных операций (например, массового изменения остатков) удобно использовать менеджер регистра. Он позволяет:
- 📝 Создавать движения по регистру.
- 🔄 Корректировать остатки.
- 🗑️ Проводить очистку данных.
Пример корректировки остатков:
МенеджерРегистра = РегистрыНакопления.ТоварыНаСкладах;
НаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();
// Добавляем запись о корректировке
Запись = НаборЗаписей.Добавить();
Запись.Период = ТекущаяДата();
Запись.Склад = Справочники.Склады.Основной;
Запись.Номенклатура = Справочники.Номенклатура.Товар1;
Запись.Количество = 10; // Устанавливаем новый остаток
// Записываем изменения
НаборЗаписей.Записать();
Когда использовать менеджер регистра:
- 🔧 Нужно программно создать движения (например, при загрузке данных из Excel).
- 📉 Требуется массовая корректировка остатков (например, после инвентаризации).
- 🗃️ Необходимо очистить регистр перед загрузкой новых данных.
Ограничения:
- 🔒 Требуются права на изменение данных.
- 📈 Может приводить к конфликтам блокировок в многопользовательском режиме.
Менеджер регистра — единственный безопасный способ программно изменять остатки. Никогда не редактируйте данные регистра напрямую через SQL или консоль отладки!
7. Типичные ошибки и как их избежать
При работе с регистрами накопления даже опытные разработчики допускают ошибки, которые приводят к:
- 🐢 Замедлению системы.
- 📉 Некорректным отчётам.
- 💥 Падению базы.
Рассмотрим самые распространённые проблемы:
| Ошибка | Последствия | Как исправить |
|---|---|---|
Использование ПолучитьДанные() в цикле по большому справочнику |
Зависание системы на 10+ минут | Заменить на запрос с виртуальной таблицей |
| Отсутствие отбора по периоду в запросе | Выборка всех движений за всю историю | Всегда указывать Период МЕЖДУ ... И ... |
| Прямое изменение данных регистра через SQL | Расхождение итогов, ошибки при проведении документов | Использовать менеджер регистра |
| Игнорирование блокировок при массовых изменениях | Конфликты между пользователями | Использовать НачатьТранзакцию() и ЗафиксироватьТранзакцию() |
Ещё одна частая ошибка — неправильная работа с виртуальными таблицами. Например, многие разработчики забывают, что таблица Остатки возвращает данные на конец периода, а не на начало. Если нужно получить остатки на начало дня, используйте:
ВЫБРАТЬ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Остатки
где &Дата — это начало дня (например, НачалоДня(ТекущаяДата())).
8. Оптимизация запросов к регистрам накопления
Чтобы запросы к регистрам накопления работали быстро, следуйте этим рекомендациям:
- 📅 Ограничивайте период — никогда не запрашивайте данные за "всю историю" без необходимости.
- 🔍 Используйте индексы — в
Конфигураторепроверьте, что поля отбора проиндексированы. - 📊 Разбивайте сложные запросы — вместо одного большого запроса сделайте несколько маленьких.
- 🔄 Избегайте вложенных циклов — если вам нужно получить данные по нескольким складам, используйте один запрос с параметром
Склад В (&СписокСкладов).
Пример оптимизированного запроса для получения оборотов по нескольким складам:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Обороты.Склад КАК Склад,
| Обороты.Номенклатура КАК Номенклатура,
| СУММА(Обороты.КоличествоПриход) КАК Приход,
| СУММА(Обороты.КоличествоРасход) КАК Расход
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Обороты(&НачалоПериода, &КонецПериода, ) КАК Обороты
|ГДЕ
| Обороты.Склад В (&СписокСкладов)
|СГРУППИРОВАТЬ ПО
| Обороты.Склад,
| Обороты.Номенклатура";
СписокСкладов = Новый Массив;
СписокСкладов.Добавить(Справочники.Склады.Основной);
СписокСкладов.Добавить(Справочники.Склады.Резервный);
Запрос.УстановитьПараметр("СписокСкладов", СписокСкладов);
Результат = Запрос.Выполнить();
Для дополнительной оптимизации:
- 📈 Используйте
ПЛАН ВЫПОЛНЕНИЯв Консоли запросов, чтобы найти "узкие места". - 🗃️ Регулярно проводите
Тестирование и исправление ИБ(черезАдминистрирование). - 🔄 Обновляйте платформу 1С — в новых версиях оптимизированы алгоритмы работы с регистрами.
⚠️ Внимание: Если после оптимизации запросы всё равно выполняются медленно, проверьте фрагментацию индексов в базе данных (актуально для SQL Server). Используйте скрипт:
-- Пример SQL для проверки фрагментации (только для администраторов!)
SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED')
WHERE avg_fragmentation_in_percent > 30;
FAQ: Ответы на частые вопросы
Как получить остатки на конкретную дату, если в регистре нет движений?
Если в регистре накопления не было движений на запрашиваемую дату, виртуальная таблица Остатки вернёт пустой результат. Чтобы получить "нулевые остатки", используйте конструкцию ЛЕВОЕ СОЕДИНЕНИЕ со справочником номенклатуры:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, ) КАК Остатки
ПО Номенклатура.Ссылка = Остатки.Номенклатура
ГДЕ
Остатки.Склад = &Склад
Почему запрос к регистру накопления возвращает неактуальные данные?
Это может происходить по нескольким причинам:
- Неактуальный период — проверьте, что дата в запросе соответствует нужному моменту времени.
- Непроведённые документы — движения по регистру создаются только после проведения документов.
- Кэширование данных — в редких случаях помогает перезапуск сеанса 1С или очистка кэша.
- Ошибки в конфигурации — проверьте логику записей в регистр через
Конфигуратор → Регистры накопления → ИмяРегистра → Движения.
Как узнать внутреннее имя регистра накопления для SQL-запроса?
Внутреннее имя регистра (например, _AccumRg12345) можно узнать:
- Через
Конфигуратор:- Откройте регистр накопления в дереве конфигурации.
- Нажмите
F5(Свойства) и посмотрите полеИмя таблицы БД.
- Через запрос к метаданным:
Запрос = Новый Запрос("ВЫБРАТЬ ИмяТаблицыБД ИЗ Метаданные.РегистрыНакопления ГДЕ Имя = ""ТоварыНаСкладах""");
⚠️ Внимание: Внутренние имена таблиц могут меняться при обновлении конфигурации!
Можно ли получить данные из регистра накопления без указания периода?
Технически да, но это крайне не рекомендуется. Если не указать период в виртуальной таблице Остатки или Обороты, платформа:
- Для
Остатки— вернёт остатки на текущую дату (что может быть неожиданно). - Для
Обороты— просуммирует движения за всю историю (что почти всегда избыточно).
Пример некорректного запроса (без периода):
ВЫБРАТЬ * ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(,)
Это может привести к выборке миллионов записей и зависанию системы.
Как проверить, есть ли движения в регистре накопления за период?
Чтобы узнать, были ли движения в регистре за определённый период, используйте виртуальную таблицу Обороты с агрегатной функцией ЕСТЬNULL:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(СУММА(Обороты.КоличествоПриход), 0) +
| ЕСТЬNULL(СУММА(Обороты.КоличествоРасход), 0) КАК КоличествоДвижений
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Обороты(&Начало, &Конец, ) КАК Обороты";
Запрос.УстановитьПараметр("Начало", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("Конец", КонецМесяца(ТекущаяДата()));
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Сообщить("Движений нет!");
Иначе
Если Результат[0].КоличествоДвижений = 0 Тогда
Сообщить("Движений нет!");
Иначе
Сообщить(СтрШаблон("Найдено %1 движений", Результат[0].КоличествоДвижений));
КонецЕсли;
КонецЕсли;