Работа с регистрами сведений в 1С:Предприятие — одна из самых частых задач как для разработчиков, так и для опытных пользователей. Но когда речь заходит о том, чтобы узнать точное количество записей в таком регистре, многие сталкиваются с неожиданными сложностями. Почему стандартные методы иногда дают неверные результаты? Как избежать ошибок при подсчете в больших базах? И почему ВыбратьКоличество() может работать медленнее, чем прямой запрос к СУБД?

В этой статье мы разберем 5 проверенных способов получить количество строк в регистре сведений — от простых до продвинутых, с учетом особенностей разных версий платформы (8.3.20+). Особое внимание уделим производительности: вы узнаете, какой метод самый быстрый для базы с миллионом записей, а какой лучше не использовать в транзакциях. Также рассмотрим типичные ошибки, из-за которых подсчет может «врать», и дадим рекомендации по оптимизации кода.

1. Стандартный метод: ВыбратьКоличество()

Самый очевидный способ — использовать встроенный метод объекта РегистрСведенийМенеджер.ВыбратьКоличество(). Он прост в реализации и подходит для большинства задач:

КоличествоЗаписей = РегистрыСведений.ЦеныНоменклатуры.ВыбратьКоличество();

Однако у этого метода есть два серьезных недостатка:

  • 🐢 Низкая производительность на больших объемах данных (от 100 000 записей). Метод фактически перебирает все строки регистра, что может занять минуты.
  • 🔄 Не учитывает отборы по измерениям. Если вам нужно посчитать записи только для конкретного Номенклатуры или Периода, придется добавлять дополнительные параметры.

Пример с отбором:

Отбор = Новый Структура();

Отбор.Вставить("Номенклатура", СсылкаНаНоменклатуру);

КоличествоЗаписей = РегистрыСведений.ЦеныНоменклатуры.ВыбратьКоличество(Отбор);

⚠️ Внимание: В версиях платформы ниже 8.3.18 метод ВыбратьКоличество() мог возвращать некорректные значения при параллельной записи в регистр. Если вы работаете со старой базой, используйте альтернативные способы.
📊 Какой метод подсчета вы используете чаще?
ВыбратьКоличество()
Запрос к СУБД
Программный обход
Другое

2. Запрос к СУБД: самый быстрый способ

Для баз с миллионами записей оптимальным решением станет прямой SQL-запрос к системе управления базой данных. Этот метод работает в 10–100 раз быстрее, чем ВыбратьКоличество(), так как использует встроенные механизмы СУБД для подсчета строк.

Пример для Microsoft SQL Server:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| COUNT(*) КАК Количество

|ИЗ

| РегистрСведений.ЦеныНоменклатуры КАК Цены

|ГДЕ

| Цены.Номенклатура = &Номенклатура";

Запрос.УстановитьПараметр("Номенклатура", СсылкаНаНоменклатуру);

Результат = Запрос.Выполнить();

КоличествоЗаписей = Результат.Выгрузить()[0].Количество;

Ключевые преимущества:

  • Мгновенный результат даже для таблиц с 10+ млн записей.
  • 🎯 Точная работа с отборами по любым полям (измерениям, ресурсам, периодам).
  • 🔧 Возможность использовать GROUP BY для подсчета по группам (например, количество записей по каждой номенклатуре).
⚠️ Внимание: Синтаксис запроса зависит от типа СУБД (MS SQL, PostgreSQL, IBM DB2). Для PostgreSQL вместо COUNT(*) иногда эффективнее использовать COUNT(1).
💡

Если вам нужно посчитать записи за определенный период, добавьте в запрос условие Цены.Период МЕЖДУ &Начало И &Конец. Это ускорит выполнение за счет использования индексов по полю Период.

3. Программный обход: когда без него не обойтись

Иногда требуется не просто посчитать записи, но и проверить их содержимое (например, найти дубли или записи с пустыми ресурсами). В таких случаях приходится использовать программный обход с помощью Выбрать():

Выборка = РегистрыСведений.ЦеныНоменклатуры.Выбрать();

Количество = 0;

Пока Выборка.Следующий() Цикл

Количество = Количество + 1;

// Здесь можно добавить проверку данных

КонецЦикла;

Минусы этого подхода:

  • 🐌 Крайне низкая скорость — метод перебирает каждую строку последовательно.
  • 💥 Риск переполнения памяти при большом объеме данных (особенно в тонком клиенте).
  • 🔒 Блокирует таблицу на время выполнения, что может вызвать конфликты в многопользовательском режиме.

Когда стоит использовать:

  • 🔍 Для аудита данных (поиск ошибок, дублей, некорректных ссылок).
  • 📊 Когда нужно не только посчитать записи, но и обработать каждую (например, пересчитать ресурсы).
Как ускорить программный обход?

Используйте метод ВыбратьПоПериоду() вместо Выбрать(), если работаете с периодическим регистром. Это сократит объем данных. Также можно разбить обработку на пакеты по 1000 записей с помощью параметра ПакетныйРежим.

4. Виртуальные таблицы: альтернатива для отчетов

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

Пример запроса с виртуальной таблицей:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Количество(*) КАК КоличествоЗаписей

|ИЗ

| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаОтчета) КАК Цены

|ГДЕ

| Цены.Номенклатура В (&СписокНоменклатуры)";

Запрос.УстановитьПараметр("ДатаОтчета", ТекущаяДата());

Запрос.УстановитьПараметр("СписокНоменклатуры", МассивСсылок);

Результат = Запрос.Выполнить();

Преимущества виртуальных таблиц:

Тип виртуальной таблицы Когда использовать Ограничения
СрезПоследних() Для подсчета актуальных записей на дату Не показывает исторические данные
СрезПервых() Для анализа первых записей в периоде Может быть медленным на больших периодах
Обороты() Для подсчета изменений ресурсов Не подходит для подсчета строк
⚠️ Внимание: Виртуальные таблицы СрезПоследних() и СрезПервых() могут возвращать неточные результаты, если в регистре есть записи с одинаковым периодом и измерениями, но разными ресурсами. В таких случаях используйте явный запрос к регистру.

5. Прямой доступ к СУБД: для опытных разработчиков

В редких случаях, когда стандартные методы не работают (например, при восстановлении базы или диагностике ошибок), можно обратиться непосредственно к таблицам СУБД. Это требует знаний SQL и структуры хранения данных .

Пример для MS SQL Server:

-- Подсчет записей в регистре сведений "ЦеныНоменклатуры"

SELECT COUNT(*)

FROM [dbo].[_InfoRg123] -- где 123 - внутренний идентификатор регистра

WHERE _Fld124 = '0x1A2B3C4D' -- идентификатор номенклатуры в двоичном формате

Особенности метода:

  • 🔧 Требует прав доступа к СУБД и знания внутренней структуры .
  • 🚀 Максимальная скорость — подсчет происходит на уровне ядра СУБД.
  • ⚠️ Опасность повреждения данных при некорректных запросах.

Как узнать внутренний идентификатор регистра:

// В конфигураторе выполните:

МetaДанные = Метаданные.РегистрыСведений.ЦеныНоменклатуры;

Сообщить(МetaДанные.УникальныйИдентификатор()); // Вернет GUID регистра

💡

Прямой доступ к СУБД следует использовать только в крайних случаях, так как это нарушает инкапсуляцию данных и может привести к несовместимости при обновлении платформы.

Типичные ошибки и как их избежать

Даже опытные разработчики иногда допускают ошибки при подсчете записей в регистрах сведений. Вот самые распространенные:

  1. Игнорирование транзакций. Если вы считаете записи в транзакции, но не фиксируете её, результат может отличаться от реального состояния базы.
    ⚠️ Внимание: Всегда проверяйте, что подсчет выполняется вне транзакций или с учетом их состояния. Например:
    НачатьТранзакцию();
    

    Количество = РегистрыСведений.ЦеныНоменклатуры.ВыбратьКоличество();

    ЗафиксироватьТранзакцию(); // Без фиксации количество может быть неверным!

  2. Неучет прав доступа. Если у пользователя нет прав на чтение регистра, методы подсчета вернут 0 или вызовут ошибку.

    Убедиться, что у пользователя есть роль с правом чтения регистра|Проверить, что сеанс не заблокирован|Отключить временные ограничения на выполнение запроса-->

  3. Путаница с периодичностью. В периодических регистрах метод ВыбратьКоличество() без отбора по периоду может вернуть количество уникальных комбинаций измерений, а не общее число строк.

Ещё одна частая проблема — кэширование данных. Если вы несколько раз подряд вызываете ВыбратьКоличество() в одном сеансе, платформа может вернуть закэшированный результат, не обновляя его. Чтобы избежать этого, используйте:

РегистрыСведений.ЦеныНоменклатуры.ОчиститьКэш();

Оптимизация производительности: советы экспертов

Если вам регулярно приходится считать записи в больших регистрах, воспользуйтесь этими рекомендациями:

  • 📈 Используйте индексы. Убедитесь, что в регистре созданы индексы по тем полям, по которым вы чаще всего делаете отборы. Например, для регистра цен полезно проиндексировать Номенклатуру и Период.
  • Разбивайте большие запросы. Если нужно посчитать записи за длительный период (например, 5 лет), разбивайте его на кварталы или месяцы и суммируйте результаты.
  • 🔄 Избегайте подсчета в пиковые часы. Нагрузка на СУБД в рабочее время может замедлить выполнение запросов в 5–10 раз.
  • 📊 Кэшируйте результаты. Если количество записей нужно показывать часто (например, в дашборде), сохраняйте его в отдельной таблице и обновляйте по расписанию.

Пример оптимизированного запроса с разбивкой по периодам:

КоличествоОбщее = 0;

НачалоПериода = НачалоГода(ТекущаяДата());

КонецПериода = КонецГода(ТекущаяДата());

Пока НачалоПериода <= КонецПериода Цикл

КонецТекущегоМесяца = КонецМесяца(НачалоПериода);

Запрос = Новый Запрос(

"ВЫБРАТЬ COUNT(*)

ИЗ РегистрСведений.ЦеныНоменклатуры

ГДЕ Период МЕЖДУ &Начало И &Конец");

Запрос.УстановитьПараметр("Начало", НачалоПериода);

Запрос.УстановитьПараметр("Конец", КонецТекущегоМесяца);

КоличествоОбщее = КоличествоОбщее + Запрос.Выполнить().Выгрузить()[0][0];

НачалоПериода = НачалоСледующегоМесяца(НачалоПериода);

КонецЦикла;

⚠️ Внимание: В кластерных базах (например, с использованием 1С:ГИС или распределенных информационных баз) некоторые методы подсчета могут работать некорректно. В таких случаях обращайтесь к администратору кластера для настройки репликации счетчиков.

FAQ: Частые вопросы о подсчете записей

Можно ли узнать количество записей в регистре сведений без программирования?

Да, в режиме 1С:Предприятие можно использовать отчет "Анализ регистра сведений" (доступен в типовой конфигурации "Управление торговлей" или "ERP"). Он показывает количество записей с группировкой по измерениям. Путь: Отчеты → Стандартные → Анализ регистров сведений.

Также в конфигураторе можно открыть регистр в режиме Все функции (Alt+F12) и посмотреть количество строк в таблице данных (вкладка Данные). Однако этот способ подходит только для небольших регистров.

Почему ВыбратьКоличество() возвращает 0, хотя в регистре есть данные?

Причин может быть несколько:

  1. Отсутствуют права у текущего пользователя на чтение регистра.
  2. Вы используете отбор по периоду, но неверно указали дату (например, ищете записи на будущую дату).
  3. Регистр периодический, и вы не указали отбор по измерениям — метод может вернуть 0, если нет записей с пустыми измерениями.
  4. Данные не записаны в базу (например, после Записать() не была выполнена ЗафиксироватьТранзакцию()).

Проверьте запрос через Выбрать() с теми же отборами — это поможет локализовать проблему.

Как посчитать записи в регистре сведений с учетом прав доступа?

Если нужно учитывать права пользователя, используйте запрос с проверкой прав:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ РАЗРЕШЕННЫЕ

| COUNT(*) КАК Количество

|ИЗ

| РегистрСведений.ЦеныНоменклатуры КАК Цены

|ГДЕ

| Цены.Номенклатура = &Номенклатура";

Запрос.ПроверятьПраваДоступа = Истина;

Запрос.УстановитьПараметр("Номенклатура", СсылкаНаНоменклатуру);

Результат = Запрос.Выполнить();

Флаг ПроверятьПраваДоступа = Истина гарантирует, что будут учтены ограничения RLS (Row-Level Security).

Можно ли узнать количество записей в регистре сведений из внешней обработки?

Да, но есть нюансы:

  • Если обработка подключена в режиме предприятия, используйте стандартные методы (ВыбратьКоличество() или запрос).
  • Если обработка внешняя (запускается из конфигуратора), доступ к данным ограничен. В этом случае придется:
    • Использовать ПолучениеДанныхЧерезCOM (медленно и небезопасно).
    • Подключаться к базе через OLE DB или ADO (требует настройки прав).

Пример подключения через ADO:

Соединение = Новый COMОбъект("ADODB.Connection");

Соединение.Open("Provider=SQLOLEDB;Data Source=Сервер;Initial Catalog=База;User ID=Пользователь;Password=Пароль;");

Запрос = Новый COMОбъект("ADODB.Recordset");

Запрос.Open("SELECT COUNT(*) FROM _InfoRg123", Соединение);

Количество = Запрос.Fields(0).Value;

Как посчитать записи в регистре сведений для конкретного документа?

Если регистр сведений подчинен документу (например, регистр ВзаиморасчетыСКонтрагентами для документа РеализацияТоваровУслуг), используйте отбор по полю Регистратор:

Отбор = Новый Структура();

Отбор.Вставить("Регистратор", СсылкаНаДокумент);

КоличествоЗаписей = РегистрыСведений.ВзаиморасчетыСКонтрагентами.ВыбратьКоличество(Отбор);

Для непериодических регистров можно также использовать запрос:

Запрос.Текст =

"ВЫБРАТЬ COUNT(*)

ИЗ РегистрСведений.ВзаиморасчетыСКонтрагентами КАК Взаиморасчеты

ГДЕ Взаиморасчеты.Регистратор = &Документ";