Обработка элементов ссылочных типов порциями

11.02.19

Разработка - Запросы

Небольшое дополнение к рекомендации от 1С по оптимизации использования оперативной памяти (https://its.1c.ru/db/v8std#content:2149184374:hdoc).

В рекомендации от 1С Оптимизация использования оперативной памяти предлагается получать данные порциями при потенциально неограниченных выборках.

В качестве примера приводится такой участок кода:

ВсеОбработано = Ложь;
Пока Истина Цикл
	Запрос = Новый Запрос;
	Запрос.Текст = 
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	Номенклатура.Ссылка КАК Ссылка,
	|	Номенклатура.Наименование КАК Наименование,
	|	Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	<условие выборки необработанных записей>";
	
	РезультатЗапроса = Запрос.Выполнить();
	ВсеОбработано = РезультатЗапроса.Пустой();
	Если ВсеОбработано Тогда
		Прервать;
	КонецЕсли;
	
	// Обход порции результата запроса
	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		// Обработка элемента выборки
		// ...
	КонецЦикла;
	
КонецЦикла;

Но зачастую бывает сложно подобрать это самое "условие выборки необработанных записей".

Я предлагаю простое решение для выборки ссылочных данных: использовать в качестве указателя на порцию данных ссылку.

Для этого необходимо внести следующие изменения:

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

Получим следующий код:

ВсеОбработано = Ложь;
ПоследняяСсылка = Справочники.Номенклатура.ПустаяСсылка();
Пока Истина Цикл
	Запрос = Новый Запрос;
	Запрос.Текст = 
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	Номенклатура.Ссылка КАК Ссылка,
	|	Номенклатура.Наименование КАК Наименование,
	|	Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	Номенклатура.Ссылка > &ПоследняяСсылка
	|	И <условия отбора требуемые для прикладной задачи>
	|
	|УПОРЯДОЧИТЬ ПО
	|	Ссылка";
	Запрос.УстановитьПараметр("ПоследняяСсылка",ПоследняяСсылка);	
	РезультатЗапроса = Запрос.Выполнить();
	ВсеОбработано = РезультатЗапроса.Пустой();
	Если ВсеОбработано Тогда
		Прервать;
	КонецЕсли;
	
	// Обход порции результата запроса
	ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
	Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
		ПоследняяСсылка = ВыборкаДетальныеЗаписи.Ссылка;
		// Обработка элемента выборки
		// ...
	КонецЦикла;
	
КонецЦикла;

Предложенное решение лежит на поверхности, но мне в голову пришло не сразу, так что, надеюсь, может быть полезным :)

П.С. Я намеренно не стал проводить рефакторинг кода с ИТС, чтобы добавленный код был более заметен. На мой взгляд создавать запрос стоит перед циклом, а не внутри него и нет необходимости создавать переменную ВсеОбработано.

Код после рефакторинга
 

См. также

Infostart Toolkit: Инструменты разработчика 1С 8.3 на управляемых формах

Инструментарий разработчика Роли и права Запросы СКД Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    129550    700    390    

749

Как посмотреть итоговый запрос в отчете СКД

Запросы СКД Система компоновки данных Россия Бесплатно (free)

Часто при разработке отчетов в СКД возникает ситуация, когда не совсем понятно, почему отчет выводит не те данные, которые нужны, либо не выводит вовсе. Возникает потребность увидеть конечный запрос, который формирует СКД. Как это сделать, рассмотрим в этой статье.

15.05.2024    2715    implecs_team    4    

33

Пропорциональное распределение в запросе с использованием АвтоНомерЗаписи()

Запросы Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Часто поступают задачи по произвольному распределению общих сумм. После распределения иногда пропадают копейки. Суть решения добавить АвтоНомерЗаписи() в ВТ распределения, и далее используя функции МАКСИМУМ или МИНИМУМ можем положить разницу копеек в первую или последнюю строку знаменателя распределения.

11.04.2024    2609    andrey_sag    10    

32

Для чего используют конструкцию запроса "ГДЕ ЛОЖЬ" в СКД на примере конфигурации 1С:ERP

Запросы СКД Платформа 1С v8.3 Запросы Система компоновки данных 1С:ERP Управление предприятием 2 Бесплатно (free)

В типовых конфигурациях разработчики компании 1С иногда используют в отчетах, построенных на СКД, такую конструкцию, как "ГДЕ ЛОЖЬ". Такая конструкция говорит о том, что данные в запросе не будут получены совсем. Для чего же нужен тогда запрос?

13.02.2024    6295    KawaNoNeko    23    

26

Набор-объект для СКД по тексту или запросу

Запросы СКД Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Абонемент ($m)

Есть список полей в виде текста, или запрос - закидываем в набор СКД.

1 стартмани

31.01.2024    2284    2    Yashazz    0    

31

Запрос 1С copilot

Инструментарий разработчика Запросы Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Бесплатно (free)

Пишем на человеческом языке, что нам надо, и получаем текст запроса на языке 1С. Используются большие языковые модели (LLM GPT) от OpenAI или Яндекс на выбор.

15.01.2024    7099    39    mkalimulin    32    

53

PrintWizard: поддержка представлений ЗУП в конструкторе

Инструментарий разработчика Запросы Платформа 1С v8.3 Бесплатно (free)

Одной из интересных задач, стоящих в процессе разработки, была поддержка механизма представлений в ЗУП. Но не просто возможность исполнения запросов с ними. Основная проблема была в том, чтобы с ними было удобно работать, а именно: создавать, модифицировать и отлаживать. Кратко о том, что в итоге получилось...

14.12.2023    2014    vandalsvq    7    

29

Консоль запросов УФ 8.3.2.24.12 (мод от Dr.Zombi)

Инструментарий разработчика Запросы Платформа 1С v8.3 Управляемые формы Запросы Россия Абонемент ($m)

Работа с запросом и СКД, Полная поддержка пакетных запросов, временных таблиц. Главное скорость отладки запроса и данных, а красота вторична.

1 стартмани

07.12.2023    3478    52    DrZombi    54    

21
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. A_Max 19 11.02.19 14:55 Сейчас в теме
Это конечно хорошо, но ВНЕЗАПНО может появиться объект с УИД меньше последнего обработанного.
Почему:
1. Никто вам не обещал и гарантирет того, что уиды генерируются последовательно.
2. Обмены

Поэтому и создаются регистры сведений ОчередьХХХХХХХХ
Артано; dsdred; Irwin; +3 Ответить
2. triviumfan 94 11.02.19 18:11 Сейчас в теме
(1) такие вещи делают в монопольном режиме имхо
Plotks2017; +1 1 Ответить
7. lunjio 67 11.02.19 20:53 Сейчас в теме
(1) Речь просто о необходимости порционно обрабатывать информацию, в случае что вы описываете, есть ещё необходимость последовательности, ну хорошо, в этом случае используем МоментВремени() ссылки и вопрос решен. Регистры же, как правило создают для отложенной обработки, в статье же в первом предложении говорится, что порционно при неограниченно больших данных.
8. Eleepod 60 12.02.19 00:27 Сейчас в теме
(1)Согласен, в случае если во время обработки в системе могут появится новые данные они могут быть не обработаны. Данный вариант стоит рассматривать как альтернативу запросу, который получает все данные за одну выборку.
dsdred; Plotks2017; +2 Ответить
23. markers 275 14.02.19 04:51 Сейчас в теме
(8) А что изменится если запросить сразу все? При запросе всей информации данный риск даже выше. И данный способ настолько прост и от этого хорош! При этом не надо добавлять реквизиты и пр.
11. Darklight 32 12.02.19 13:15 Сейчас в теме
(1)Просто так такие UID не появляются. У них есть стандарт генерирования (у 1С он свой UUID, у мелкомягких свой - GUID) - и он хронологический. Вариантов появления младших UID только два:
1. Обмен данных с другой системой, формирующих UID на другом компьютере, выполненный параллельно с обработкой
2. UID задаётся вручную строкой не по тем же правилам его формирования - опять таки каким-то параллельным процессом

Для данного примера вероятен только п.1. (п.2. почти невероятен - но кто его знает, что там пишу криворукие программисты на местах)
Поэтому, можно на время обработки остановить обмен. Это вообще важное замечание для ряда подобных обработок - ведь появление пропущенной ссылки тут не самое страшное, что может произойти, когда в данные вклинится параллельный процесс.
Ну или, хотя бы блокировку данных надо наложить (увы - в данном примере - на всю таблицу)!

Да, кстати, замечу, что если вообще не заморачиваться - и выбрать все Ссылки разом а потом их обрабатывать - то это так же ни гарантирует, что в процессе обработки не появятся новые Ссылки (в любой "хронологии"), которые останутся не обработанными - проблема не повторяемого чтения (или наоборот - будут удалены - проблема фантомов). Вот для этого и существуют блокировки данных!
Ali1976; dsdred; Krio2; acanta; +4 Ответить
3. lemz 123 11.02.19 18:32 Сейчас в теме
Хочется убивать конечно уже за использование запроса в цикле. Ограничение выборки хотя бы по ссылке имеет здравое зерно
6. lunjio 67 11.02.19 19:38 Сейчас в теме
(3) Запросы в цикле не всегда зло, есть таблица 10 гб размером, сервак просто помирает при попытке выбрать из нее, как сделать как ни порционно и в цикле ? Убивать хочется за тупую архитектуру и кривую логику, когда люди вместо того чтобы попытаться найти фунционал или взять удачный пример из типовых или других решений, придумывают велосипеды, да причем очень кустарные.
NoRazum; Артано; Plotks2017; +3 Ответить
10. Shmell 537 12.02.19 11:29 Сейчас в теме
(3) такой подход очень оправдан. При этом у Вас не засоряется кэш объектов вследствии использования ПолучитьОбъект(), и немаловажный момент - что Вы можете параллелить обработку порций объектов разными фоновыми заданиями.
Артано; Plotks2017; +2 Ответить
14. Darklight 32 12.02.19 13:28 Сейчас в теме
(3)Не понимаю к чему Вы так предрались. Запросы в цикле - это не есть абсолютное зло, особенно когда это цикли порционной обработки. При работе с Big Data так вообще без этого НИКАК не обойтись!

Вот создавать каждый раз запрос в цикле - не очень красиво, но для данного примера - абсолютно не критично!
Plotks2017; for_sale; +2 Ответить
4. acanta 11.02.19 18:35 Сейчас в теме
То есть сравнение на больше меньше работает по Гуиду как по строке?
for_sale; +1 Ответить
9. Eleepod 60 12.02.19 01:11 Сейчас в теме
(4) Примерно, но только не в том представлении как оно выводится методом УникальныйИдентификатор. Например ГУИД dee6e178-55bc-11d9-848a-00112f435cbd при ошибке "объект не найден" будет выведен как 848a00112f435cbd11d955bcdee6e178, т.е. вид A-B-C-D-E превращается в DECBA. Думаю в таком виде они и сравниваются.
18. kuzyara 2022 13.02.19 07:41 Сейчас в теме
(9) гуиды имеют тип binary(16) и сравниваются побитово как бинарные данные: binary bit-for-bit comparison behavior
Функция ПолучитьGUIDПоУникальномуИдентификатору(UUID1)
    UUID=ВРЕГ(UUID1);
    ч1 = Сред(UUID,20,4);
    ч2 = Сред(UUID,25,12);
    ч3 = Сред(UUID,15,4);
    ч4 = Сред(UUID,10,4);
    ч5 = Сред(UUID,1,8);
    Возврат "0x" + ч1 + ч2 + ч3 + ч4 + ч5;
КонецФункции
Показать

А если кто-то как Darklight думает что гуиды формируются хронологически - почитайте статью Как формируется GUID?

Гуиды выдаются пулом по 32 штуки для каждого сеанса отдельно, за исключением random based uuid получаемых через Новый УникальныйИдентификатор()
5. lunjio 67 11.02.19 19:36 Сейчас в теме
Боже мой, уже весь мир давно использует, посмотрите процедуры корректировки стоимости списания в УПП, ПЕРВЫЕ 3000 ГДЕ &ПоследнийОбъектОчереди = НЕОПРЕДЕЛЕНО ИЛИ Объект.Ссылка > &ПоследнийОбъектОчереди УПОРЯДОЧИТЬ ПО Объект.Ссылка.
dsdred; Irwin; Shmell; +3 Ответить
12. Darklight 32 12.02.19 13:18 Сейчас в теме
(5)Думаю не весь мир. Решение очевидно понятное (если ссылки сортируются, значит они могут сравниваться), да неприглядное ;-) не привычно вот таки воспринимать ссылки большинству!
13. Darklight 32 12.02.19 13:25 Сейчас в теме

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

Зачем так людей пугаете. Конечно же можно - если поля сортировки будут ниже поля упорядочивания по ссылеке - то никаких проблем (но это бывает редко - когда ссылка это и есть ключевой обрабатываемый объект)
А вот, если поля для сортировки стоят раньше ссылки - то тогда их так же придётся включить в контроллируемые (по классическому подходу) - и пример несколько усложнится, потеряв своё изначальное изящество - но будет работать. Хоршо бы и такой пример привести тоже.



Так же следует учитывать, что сортировка по ссылке не означает сортировку по моменту времени.

Если речь про момент времени документов - то да, Ссылка <> МоментВремени - но там как раз Ссылку лучше заменить на МоментВремени - он так же уникален (и не повторяем) как и Ссылка.
Если же имелся в виду какой-то другой момент времени - то надо уточнить - какой и в чём проблема!
17. Eleepod 60 12.02.19 22:45 Сейчас в теме
(13) Похоже про сортировку я действительно не прав. А насчет ссылок и моментов времени хотел предостеречь от заблуждения что сортировка по ссылке равна сортировке по моменту времени. Уберу пока эти моменты. Спасибо за комментарии!
15. lunjio 67 12.02.19 13:37 Сейчас в теме
Единственный трындец, который может наступить, это допустим когда мы не прямую таблицу типа обрабатываем, допустим справочника номенклатуры или документа, а какой-нибудь регистр сведений и за такое поле принимаем измерение у которого может пустая ссылка в измерении и таких ссылок больше, чем обрабатываемая порция, вот тогда да, конец света )
16. Darklight 32 12.02.19 17:57 Сейчас в теме
(15)У Вас пример, немного из другой оперы, хотя автор в теме статьи не ограничил её только обработкой источников, являющихся только объектными (аля справочников и документов). Да, там где обрабатываемый объект не является ключевым (как ссылка) для выборки - эта техника не сгодится. Но ничего особо сложного - делаете структуру со свойствами - где все ключевые измерения и сохраняеете текующую позицию туда. В запросе по ним всем сортируете, а условие будет на "ИЛИ" по этим измерениям (и так же на ">"), а в цикле обновляете их все из данных выборки в структуре через ЗаполнитьЗначенияСвойств - как -то так - вроде ничего особенного - код сложнее где-то на (10 + количество ключевых полей) процентов.
19. Glebis 13 13.02.19 09:29 Сейчас в теме
Предлагаю ещё немного рефакторинга:
 	Запрос = Новый Запрос;
	Запрос.Текст = 
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	Номенклатура.Ссылка КАК Ссылка,
	|	Номенклатура.Наименование КАК Наименование,
	|	Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	Номенклатура.Ссылка > &ПоследняяСсылка
	|	И <условие выборки необработанных записей>
	|
	|УПОРЯДОЧИТЬ ПО
	|	Ссылка";
	Запрос.УстановитьПараметр("ПоследняяСсылка", Справочники.Номенклатура.ПустаяСсылка());
	РезультатЗапроса = Запрос.Выполнить();
	Пока НЕ РезультатЗапроса.Пустой() Цикл
		// Обход порции результата запроса
		ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
		Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
			// Обработка элемента выборки
			// ...
		КонецЦикла;
		Запрос.УстановитьПараметр("ПоследняяСсылка", ВыборкаДетальныеЗаписи.Ссылка);
		РезультатЗапроса = Запрос.Выполнить();
	КонецЦикла;
Показать
20. Glebis 13 13.02.19 09:43 Сейчас в теме
Если есть проблема с новыми ссылками, то почему бы не складывать уже обработанные в массив и проверять вхождение при обработки каждой порции?

Запрос = Новый Запрос;
	Запрос.Текст = 
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	Номенклатура.Ссылка КАК Ссылка,
	|	Номенклатура.Наименование КАК Наименование,
	|	Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	НЕ Номенклатура.Ссылка В (&МассивОбработанныхСсылок)
	|	И <условие выборки необработанных записей";
	МассивОбработанныхСсылок = Новый Массив;
	Запрос.УстановитьПараметр("МассивОбработанныхСсылок", МассивОбработанныхСсылок);
	РезультатЗапроса = Запрос.Выполнить();
	Пока НЕ РезультатЗапроса.Пустой() Цикл
		// Обход порции результата запроса
		ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
		Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
			МассивОбработанныхСсылок.Добавить(ВыборкаДетальныеЗаписи.Ссылка);
			// Обработка элемента выборки
			// ...
		КонецЦикла;
		Запрос.УстановитьПараметр("МассивОбработанныхСсылок", МассивОбработанныхСсылок);
		РезультатЗапроса = Запрос.Выполнить();
	КонецЦикла;
Показать
21. Glebis 13 13.02.19 11:03 Сейчас в теме
Если предыдущий вариант со складированием ссылок обработанных объектов в массив не подходит, то можно попробовать ещё один особо извращенский способ: переделать запрос на НЕ вхождение порции в таблицу изменений справочника. Но здесь имеются подводные камни, которые нужно проверять: наложение общей транзакции, отдельный план обмена с ручной регистрацией и ещё куча других вопросов.
22. VmvLer 13.02.19 14:23 Сейчас в теме
если немного подумать то АВТОНОМЕРЗАПИСИ() решает задачу разбиения выборки на порции

размер порции 1000, 10 000,... задаем параметром и дальше простая математика в SQL без шаманских пассов получает порционную выборку
24. user970589 11 14.02.19 10:05 Сейчас в теме
Картинка зачотная!!! пацсталом. ссылочные элементы. порциями. аххах..
25. dsdred 3345 17.02.19 08:40 Сейчас в теме
Старый способ...
уже описывал в статье https://infostart.ru/public/893304/

А вообще способ "декоративный" - не надежный!
Не дураки придумали ПланОбмена и номера сообщений - этот вариант тоже показан в статье https://infostart.ru/public/893304/
Оставьте свое сообщение