Как обработать больше 99 999 строк в документе 1С и исправить запрос с ВЫБРАТЬ ПЕРВЫЕ?

Программист 1С v8.3 (Обычные формы) Бухгалтерский учет
← На главную

При проведении инвентаризации в крупных организациях или при переносе данных часто возникает ситуация, когда число позиций основных средств в одной выборке превышает сотню тысяч строк. При попытке записать такой массив в табличную часть документа разработчики сталкиваются с техническим ограничением платформы 1С — не более 99 999 строк.

Разберем, почему возникает эта ошибка, в чем заключается коварство конструкции ВЫБРАТЬ ПЕРВЫЕ без сортировки и какими способами можно правильно обработать такой объем данных.

Почему возникает ошибка при разбиении запроса через ВЫБРАТЬ ПЕРВЫЕ

Рассмотрим распространенную ошибку, когда разработчик пытается скрестить две части выборки, отсекая первые 99 999 записей с помощью подзапроса вида ГДЕ НЕ ОС В (ВЫБРАТЬ ПЕРВЫЕ 99999 ...).

На практике такой подход приводит к тому, что во вторую часть попадут совсем не те элементы, которые ожидает программист. Происходит это по следующим причинам:

  1. Отсутствие гарантии порядка строк: СУБД выдает результаты запроса в произвольном порядке, если не указано явное предложение УПОРЯДОЧИТЬ ПО. Без сортировки первый и второй запросы вернут совершенно разные последовательности записей.
  2. Ошибки синтаксиса во вложенных запросах: Платформа 1С выдает ошибку вида «Во вложенных запросах нельзя применять операцию УПОРЯДОЧИТЬ ПО без использования конструкции ПЕРВЫЕ», если сортировка добавлена некорректно или по неуникальным полям.
  3. Низкая производительность: Условие НЕ В (...) с вложенной виртуальной таблицей остатков на массивах в десятки тысяч элементов заставляет SQL-сервер выполнять тяжелое сканирование вместо использования индексов, что приводит к задержкам и зависаниям базы.

Ограничение платформы 1С на 99 999 строк

Ограничение в 99 999 строк связано со служебным реквизитом НомерСтроки табличных частей документов, который исторически имеет фиксированную длину. Попытка добавить 100 000-ю строку вызывает системную ошибку сохранения объекта.

Кроме того, размещение 100 000 и более элементов на форме документа создает колоссальную нагрузку на оперативную память сервера приложений и клиента, вплоть до аварийного завершения работы платформы.

Решение 1: Автоматическое разбиение на несколько документов (Рекомендуемое)

Наиболее надежный и правильный с точки зрения архитектуры 1С способ — разбивка большого массива данных на несколько документов инвентаризации. Для этого создается внешняя обработка, которая заполняет документы порциями по 80 000 – 90 000 строк.

Проанализируем пример логики формирования документов в цикле:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЕПСБУОстатки.Счет КАК Счет,
    |	ЕПСБУОстатки.Субконто2 КАК ОС,
    |	ЕПСБУОстатки.Субконто3 КАК ЦМО,
    |	ЕПСБУОстатки.СуммаОстаток КАК СтоимостьБалансовая
    |ИЗ
    |	РегистрБухгалтерии.ЕПСБУ.Остатки(&ДатаЗапроса, Счет В ИЕРАРХИИ (&Счет101), , Учреждение = &Учреждение) КАК ЕПСБУОстатки";

Запрос.УстановитьПараметр("ДатаЗапроса", ДатаИнвентаризации);
Запрос.УстановитьПараметр("Счет101", Счет101);
Запрос.УстановитьПараметр("Учреждение", Учреждение);

Выборка = Запрос.Выполнить().Выбрать();

СчетчикСтрок = 0;
ЛимитСтрок = 90000; // Безопасный порог для одного документа

ДокументОбъект = Неопределено;

Пока Выборка.Следующий() Цикл
    Если ДокументОбъект = Неопределено Или СчетчикСтрок >= ЛимитСтрок Тогда
        Если ДокументОбъект <> Неопределено Тогда
            ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
        КонецЕсли;
        
        ДокументОбъект = Документы.ИнвентаризацияОС.СоздатьДокумент();
        ДокументОбъект.Дата = ДатаИнвентаризации;
        ДокументОбъект.Учреждение = Учреждение;
        СчетчикСтрок = 0;
    КонецЕсли;
    
    НоваяСтрока = ДокументОбъект.ОС.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
    СчетчикСтрок = СчетчикСтрок + 1;
КонецЦикла;

Если ДокументОбъект <> Неопределено И СчетчикСтрок > 0 Тогда
    ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;

Решение 2: Постраничная выборка (Keyset Pagination) по уникальному ключу

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

Рассмотрим схему построения запроса с фильтрацией по границе ключа:


ВЫБРАТЬ ПЕРВЫЕ 90000
	ЕПСБУОстатки.Счет КАК Счет,
	ЕПСБУОстатки.Субконто2 КАК ОС,
	ВЫРАЗИТЬ(ЕПСБУОстатки.Субконто2 КАК Справочник.ОсновныеСредства).Код КАК ОСКод,
	ЕПСБУОстатки.СуммаОстаток КАК СтоимостьБалансовая
ИЗ
	РегистрБухгалтерии.ЕПСБУ.Остатки(&ДатаЗапроса, Счет В ИЕРАРХИИ (&Счет101), , Учреждение = &Учреждение) КАК ЕПСБУОстатки
ГДЕ
	ВЫРАЗИТЬ(ЕПСБУОстатки.Субконто2 КАК Справочник.ОсновныеСредства).Код > &ПоследнийКод
УПОРЯДОЧИТЬ ПО
	ОСКод ВОЗР

Выясним главные правила использования этого метода:

  1. Обязательно указываем конструкцию УПОРЯДОЧИТЬ ПО по уникальному полю (код справочника, Ссылка и т.д.).
  2. Перед выполнением запроса для первой порции передаем в параметр &ПоследнийКод пустую строку "".
  3. Для выборки следующей порции берем код ОСКод из последней строки предыдущей выборки и передаем его в параметр &ПоследнийКод.

Решение 3: Оптимизация параметров виртуальных таблиц

Обратим внимание на условие, часто встречающееся в текстах запросов:


И ВЫБОР
	КОГДА &БалансОтбор = НЕОПРЕДЕЛЕНО
		ТОГДА ИСТИНА
	ИНАЧЕ Баланс = &БалансОтбор
КОНЕЦ

Важно: Использование логических условий ВЫБОР КОГДА внутри параметров виртуальных таблиц блокирует эффективное использование индексов СУБД. Оптимизатор запросов SQL не может заранее построить план выполнения и переходит к полному сканированию остатков.

Правильнее формировать текст запроса динамически в программном коде перед выполнением. Если параметр задан — подставляем прямое условие Баланс = &БалансОтбор, если не задан — полностью исключаем эту строчку из условий виртуальной таблицы.

← На главную