В процессе разработки конфигураций на платформе 1С:Предприятие 8 программисты постоянно сталкиваются с необходимостью анализа объемов данных. Самая частая задача — определить точное число записей в наборе данных, будь то временная таблица, результат выборки из базы или объект метаданных. От корректности этого значения часто зависит логика работы алгоритма, формирование отчетов или блокировка проведения документов.
Существует несколько фундаментально разных подходов к решению этой задачи, каждый из которых имеет свои нюансы производительности и области применения. Выбор конкретного метода зависит от того, где именно хранятся данные: в оперативной памяти в виде объекта ТаблицаЗначений или непосредственно в базе данных SQL. Неправильный выбор способа подсчета может привести к существенному замедлению работы системы при больших объемах информации.
В этой статье мы детально разберем синтаксис встроенного языка, методы оптимизации запросов и типичные ошибки, которые допускают начинающие разработчики. Вы узнаете, как использовать свойство Количество и функцию ЕСТЬNULL для получения точных результатов в любых сценариях.
Подсчет строк в объекте ТаблицаЗначений
Наиболее распространенный сценарий в коде 1С — работа с объектом типа ТаблицаЗначений. Это универсальный контейнер для хранения табличных данных в оперативной памяти. Для получения числа строк в такой таблице используется встроенное свойство Количество. Оно возвращает целочисленное значение, равное текущему числу записей в наборе.
Обращение к этому свойству является крайне быстрой операцией, так как значение хранится внутри объекта и не требует перебора данных. Однако Попытка применить его к обычному массиву или списку значений приведет к ошибке выполнения.
⚠️ Внимание: Свойство
Количествовозвращает общее число строк, включая пустые, если они были добавлены программно. Если ваша логика требует игнорировать строки с пустыми ключевыми полями, потребуется дополнительный цикл или отбор.
Рассмотрим пример программного создания таблицы и получения числа строк. В данном фрагменте кода мы инициализируем объект, добавляем несколько записей и выводим итоговое значение в консоль или сообщение пользователю.
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Номенклатура", ТипОписанияТипов(Новый ОписаниеТипов("СправочникСсылка.Номенклатура")));
ТЗ.Колонки.Добавить("Количество", ТипОписанияТипов(Новый ОписаниеТипов("Число")));
// Добавляем строки
ТЗ.Добавить();
ТЗ.Добавить();
ТЗ.Добавить();
Сообщить("Всего строк: " + ТЗ.Количество);
При работе с большими массивами данных, загруженными из базы, свойство Количество остается эффективным инструментом. Однако, если таблица формируется динамически в цикле, убедитесь, что вы не вызываете пересчет лишних метаданных внутри цикла, хотя само свойство к ним не относится.
Используйте метод ТЗ.Количество() только после завершения формирования таблицы. Внутри циклов добавления строк это свойство обновляется автоматически, но частое обращение к нему в условиях сложной вложенности может незначительно влиять на читаемость кода.
Использование агрегатной функции в запросах к базе данных
Когда данные находятся непосредственно в базе данных, наиболее производительным способом получения числа строк является использование языка запросов 1С. Платформа транслирует такие запросы в нативный SQL диалект используемой СУБД (MSSQL, PostgreSQL, Oracle). Для подсчета используется ключевое слово КОЛИЧЕСТВО() или COUNT() в терминологии SQL.
Главное преимущество такого подхода заключается в том, что подсчет происходит на стороне сервера баз данных. Это означает, что по сети передается всего одно число, а не весь набор записей, что критически важно для клиент-серверных вариантов работы 1С:Предприятие. Перегрузка канала передачи данных — одна из частых причин тормозов в распределенных системах.
Синтаксис запроса предполагает создание временной таблицы или использование вложенного запроса для агрегации. Ниже приведен пример корректного построения запроса для получения числа элементов справочника, удовлетворяющих определенному условию.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КОЛИЧЕСТВО(*) КАК ЧислоСтрок
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ЛОЖЬ";
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
Если Выборка.Следующий() Тогда
КоличествоТоваров = Выборка.ЧислоСтрок;
КонецЕсли;
Важно отметить, что использование КОЛИЧЕСТВО(*) считается более предпочтительным, чем КОЛИЧЕСТВО(Поле), если вам нужно просто число строк, так как первый вариант оптимизирован движком базы данных. Поле ЧислоСтрок в примере выше является псевдонимом, который мы задали для удобства обращения в коде 1С.
Всегда старайтесь выполнять подсчет строк на стороне СУБД через запрос, если данные еще не загружены в память. Это экономит оперативную память сервера 1С и снижает сетевой трафик.
Работа с выборками и объектами метаданных
Иногда разработчики работают не с таблицами значений или запросами, а напрямую с выборками данных, полученными через объект ВыборкаИзРезультатаЗапроса или обход дерева метаданных. В таких случаях свойство Количество может быть недоступно напрямую, либо его использование потребует предварительной загрузки всех данных в память, что недопустимо для больших объемов.
Для объектов типа ВыборкаДанных не существует встроенного свойства, возвращающего общее число записей без их перебора. Это связано с природой курсоров в базах данных: они предназначены для последовательного чтения, а не для мгновенного получения метаданных о размере набора. Попытка реализовать подсчет через цикл Пока Выборка.Следующий() Цикл приведет к полному прочтению набора данных, что может занять значительное время.
Если вы оказались в ситуации, когда у вас есть только открытая выборка, а получить количество нужно обязательно, единственным надежным способом остается выполнение отдельного запроса с агрегатной функцией, как описано в предыдущем разделе. Не пытайтесь эмулировать подсчет программным перебором, если объем данных превышает несколько тысяч записей.
- 🚀 Используйте
ТаблицаЗначений.Количестводля данных в оперативной памяти. - 🗄️ Используйте
ВЫБРАТЬ КОЛИЧЕСТВО(*)для данных в базе данных. - ⛔ Избегайте программного перебора выборки только ради подсчета строк.
- 📉 Учитывайте блокировки при подсчете в транзакциях.
Особое внимание следует уделить работе с иерархическими справочниками. При использовании ВЫБОРКА ИЗ с параметром ИЕРАРХИЯ, количество полученных строк может отличаться от простого числа элементов из-за способа обхода дерева. В запросах это регулируется псевдонимом Родитель и уровнем иерархии.
Нюансы подсчета с учетом отборов и условий
Часто требуется посчитать не все строки подряд, а только те, которые удовлетворяют сложным критериям. В объекте ТаблицаЗначений для этого существует метод НайтиСтроки, который возвращает новую таблицу значений, содержащую только найденные записи. Количество строк в этой новой таблице и будет искомым значением.
Альтернативный вариант — использование метода Фильтр или создание отбора непосредственно в запросе перед агрегацией. Важно понимать разницу между отбором на стороне клиента (после загрузки данных) и на стороне сервера (в тексте запроса). Серверный отбор всегда предпочтительнее с точки зрения производительности.
Рассмотрим пример, где нам нужно посчитать количество товаров с определенной единицей измерения, используя метод НайтиСтроки. Мы создаем структуру отбора, передаем ее в метод и анализируем результат.
Отбор = Новый Структура;
Отбор.Вставить("ЕдиницаИзмерения", Справочники.ЕдиницыИзмерения.НайтиПоНаименованию("Штука"));
НайденныеСтроки = ТЗ.НайтиСтроки(Отбор);
КоличествоШтук = НайденныеСтроки.Количество;
В запросах условия формируются в блоке ГДЕ. Здесь можно использовать логические операторы И, ИЛИ, а также проверки на ЕСТЬNULL. Правильное составление условий критически влияет на план выполнения запроса и скорость получения ответа от СУБД.
⚠️ Внимание: При использовании условий с полями, по которым не установлены индексы в базе данных, запрос с
КОЛИЧЕСТВО(*)может выполняться долго (Full Table Scan). Проверяйте индексы в режиме предприятия или через анализ планов запросов.
Сравнение производительности методов
Выбор метода подсчета напрямую влияет на время отклика системы. Для наглядного сравнения различных подходов составим таблицу, демонстрирующую ожидаемое поведение системы в зависимости от объема данных и выбранного способа.
| Метод | Объем данных | Нагрузка на сеть | Рекомендация |
|---|---|---|---|
ТЗ.Количество |
До 1 млн строк | Отсутствует (память) | Идеально для временных таблиц |
ВЫБРАТЬ КОЛИЧЕСТВО(*) |
Любой (до млрд) | Минимальная (1 число) | Лучший выбор для базы данных |
Цикл Пока..Следующий |
Более 10 тыс. | Высокая (все данные) | Только для малых выборок |
ТЗ.НайтиСтроки |
Зависит от отбора | Средняя (копия данных) | Для сложной фильтрации в памяти |
Как видно из таблицы, работа с базой данных через SQL-агрегацию выигрывает у любых клиентских методов при работе с большими данными. Однако, если данные уже загружены в ТаблицуЗначений для других целей (например, для отображения в форме), повторный запрос в базу будет избыточным.
В ситуациях, когда конфигурация работает в файловом варианте, разница в производительности может быть менее заметна на малых объемах, но становится критичной при росте базы. Всегда проводите профилирование кода в режиме Предприятие с включенным замером производительности, чтобы убедиться в правильности выбора.
Секреты оптимизации COUNT()
В некоторых СУБД (например, PostgreSQL) запрос COUNT() может быть медленным на очень больших таблицах без подходящих индексов. В 1С можно использовать хитрость: если точность не критична до единицы, можно запросить_approximate_count, но стандартными средствами 1С лучше полагаться на индексы по полям отбора.
Обработка пустых значений и исключительных ситуаций
При подсчете строк важно различать понятие "пустая таблица" и "ошибка выполнения". Методы 1С, как правило, устойчивы к пустым наборам данных: свойство Количество вернет 0, а выборка по запросу просто не сделает ни одного шага Следующий(). Однако, существуют нюансы при работе с объединениями запросов (ОБЪЕДИНИТЬ).
Если вы используете ОБЪЕДИНИТЬ ВСЕ, количество строк будет суммой строк всех подзапросов. Если же используется обычное ОБЪЕДИНИТЬ (без ВСЕ), система автоматически удалит дубликаты, и итоговое количество может оказаться меньше суммы составляющих. Это поведение должно учитываться при планировании логики отчетов.
Также стоит упомянуть обработку ошибок при выполнении запросов. Если запрос синтаксически верен, но не может быть выполнен из-за блокировок или прав доступа, метод Выполнить() выбросит исключение. Оберните критичные участки кода в конструкцию Попытка..Исключение, чтобы gracefully обработать ситуацию, когда получение количества невозможно.
Попытка
Результат = Запрос.Выполнить();
Количество = Результат.Выбрать().ЧислоСтрок;
Исключение
Количество = -1; // Код ошибки
ЗаписьЖурналаРегистрации(..);
КонецПопытки;
☑️ Проверка перед подсчетом
В заключение, правильный подсчет строк — это баланс между удобством кода и производительностью системы. Понимание внутреннего устройства платформы 1С:Предприятие и принципов работы СУБД позволяет писать эффективный код, который не замедляет работу пользователей даже при росте базы данных до миллионов записей.
В чем разница между ТЗ.Количество и циклом перебора?
Свойство ТЗ.Количество возвращает сохраненное значение размера коллекции мгновенно (O(1)). Цикл перебора требует прохождения по всем элементам (O(N)), что при больших N занимает существенное время процессора.
Можно ли использовать КОЛИЧЕСТВО(Поле) вместо КОЛИЧЕСТВО(*)?
Да, но КОЛИЧЕСТВО(Поле) не учитывает строки, где данное поле имеет значение NULL. КОЛИЧЕСТВО(*) считает абсолютно все строки результата, независимо от содержимого полей.
Почему запрос с COUNT(*) выполняется долго?
Чаще всего причина в отсутствии индекса по полям, участвующим в условии ГДЕ. База данных вынуждена сканировать всю таблицу целиком. Также влияние могут оказывать блокировки от других пользователей.
Как посчитать уникальные значения в колонке?
Для этого используется конструкция КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Поле) в тексте запроса. Это аналог SQL COUNT(DISTINCT Field).