В рекомендации от 1С Оптимизация использования оперативной памяти предлагается получать данные порциями при потенциально неограниченных выборках.
В качестве примера приводится такой участок кода:
ВсеОбработано = Ложь;
Пока Истина Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| <условие выборки необработанных записей>";
РезультатЗапроса = Запрос.Выполнить();
ВсеОбработано = РезультатЗапроса.Пустой();
Если ВсеОбработано Тогда
Прервать;
КонецЕсли;
// Обход порции результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Обработка элемента выборки
// ...
КонецЦикла;
КонецЦикла;
Но зачастую бывает сложно подобрать это самое "условие выборки необработанных записей".
Я предлагаю простое решение для выборки ссылочных данных: использовать в качестве указателя на порцию данных ссылку.
Для этого необходимо внести следующие изменения:
- Перед погружением в цикл запросов получаем пустую ссылку (она всегда меньше любой непустой ссылки) и записываем ее в переменную указывающую на последнюю полученную ссылку (ПоследняяСсылка).
- В запросе упорядочиваем выборку по ссылке (не включая автоупорядочивание, т.к. это приведет к сортировке по представлению) и добавляем отбор по ссылке (Ссылка > &ПоследняяСсылка).
- На каждом проходе выборки обновляем переменную ПоследняяСсылка, присваивая ей текущую обрабатываемую ссылку.
Получим следующий код:
ВсеОбработано = Ложь;
ПоследняяСсылка = Справочники.Номенклатура.ПустаяСсылка();
Пока Истина Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка > &ПоследняяСсылка
| И <условия отбора требуемые для прикладной задачи>
|
|УПОРЯДОЧИТЬ ПО
| Ссылка";
Запрос.УстановитьПараметр("ПоследняяСсылка",ПоследняяСсылка);
РезультатЗапроса = Запрос.Выполнить();
ВсеОбработано = РезультатЗапроса.Пустой();
Если ВсеОбработано Тогда
Прервать;
КонецЕсли;
// Обход порции результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ПоследняяСсылка = ВыборкаДетальныеЗаписи.Ссылка;
// Обработка элемента выборки
// ...
КонецЦикла;
КонецЦикла;
Предложенное решение лежит на поверхности, но мне в голову пришло не сразу, так что, надеюсь, может быть полезным 🙂
П.С. Я намеренно не стал проводить рефакторинг кода с ИТС, чтобы добавленный код был более заметен. На мой взгляд создавать запрос стоит перед циклом, а не внутри него и нет необходимости создавать переменную ВсеОбработано.
Это конечно хорошо, но ВНЕЗАПНО может появиться объект с УИД меньше последнего обработанного.
Почему:
1. Никто вам не обещал и гарантирет того, что уиды генерируются последовательно.
2. Обмены
Поэтому и создаются регистры сведений ОчередьХХХХХХХХ
(1) такие вещи делают в монопольном режиме имхо
То есть сравнение на больше меньше работает по Гуиду как по строке?
Боже мой, уже весь мир давно использует, посмотрите процедуры корректировки стоимости списания в УПП, ПЕРВЫЕ 3000 ГДЕ &ПоследнийОбъектОчереди = НЕОПРЕДЕЛЕНО ИЛИ Объект.Ссылка > &ПоследнийОбъектОчереди УПОРЯДОЧИТЬ ПО Объект.Ссылка.
(3) Запросы в цикле не всегда зло, есть таблица 10 гб размером, сервак просто помирает при попытке выбрать из нее, как сделать как ни порционно и в цикле ? Убивать хочется за тупую архитектуру и кривую логику, когда люди вместо того чтобы попытаться найти фунционал или взять удачный пример из типовых или других решений, придумывают велосипеды, да причем очень кустарные.
(1) Речь просто о необходимости порционно обрабатывать информацию, в случае что вы описываете, есть ещё необходимость последовательности, ну хорошо, в этом случае используем МоментВремени() ссылки и вопрос решен. Регистры же, как правило создают для отложенной обработки, в статье же в первом предложении говорится, что порционно при неограниченно больших данных.
(1)Согласен, в случае если во время обработки в системе могут появится новые данные они могут быть не обработаны. Данный вариант стоит рассматривать как альтернативу запросу, который получает все данные за одну выборку.
(4) Примерно, но только не в том представлении как оно выводится методом УникальныйИдентификатор. Например ГУИД dee6e178-55bc-11d9-848a-00112f435cbd при ошибке «объект не найден» будет выведен как 848a00112f435cbd11d955bcdee6e178, т.е. вид A-B-C-D-E превращается в DECBA. Думаю в таком виде они и сравниваются.
(3) такой подход очень оправдан. При этом у Вас не засоряется кэш объектов вследствии использования ПолучитьОбъект(), и немаловажный момент — что Вы можете параллелить обработку порций объектов разными фоновыми заданиями.
(1)Просто так такие UID не появляются. У них есть стандарт генерирования (у 1С он свой UUID, у мелкомягких свой — GUID) — и он хронологический. Вариантов появления младших UID только два:
1. Обмен данных с другой системой, формирующих UID на другом компьютере, выполненный параллельно с обработкой
2. UID задаётся вручную строкой не по тем же правилам его формирования — опять таки каким-то параллельным процессом
Для данного примера вероятен только п.1. (п.2. почти невероятен — но кто его знает, что там пишу криворукие программисты на местах)
Поэтому, можно на время обработки остановить обмен. Это вообще важное замечание для ряда подобных обработок — ведь появление пропущенной ссылки тут не самое страшное, что может произойти, когда в данные вклинится параллельный процесс.
Ну или, хотя бы блокировку данных надо наложить (увы — в данном примере — на всю таблицу)!
Да, кстати, замечу, что если вообще не заморачиваться — и выбрать все Ссылки разом а потом их обрабатывать — то это так же ни гарантирует, что в процессе обработки не появятся новые Ссылки (в любой «хронологии»), которые останутся не обработанными — проблема не повторяемого чтения (или наоборот — будут удалены — проблема фантомов). Вот для этого и существуют блокировки данных!
(5)Думаю не весь мир. Решение очевидно понятное (если ссылки сортируются, значит они могут сравниваться), да неприглядное 😉 не привычно вот таки воспринимать ссылки большинству!
в данном случае нельзя сортировать данные по другим полям, кроме ссылки, так что если нужно обрабатывать данные в определенной последовательности, то такой метод не подойдет.
Зачем так людей пугаете. Конечно же можно — если поля сортировки будут ниже поля упорядочивания по ссылеке — то никаких проблем (но это бывает редко — когда ссылка это и есть ключевой обрабатываемый объект)
А вот, если поля для сортировки стоят раньше ссылки — то тогда их так же придётся включить в контроллируемые (по классическому подходу) — и пример несколько усложнится, потеряв своё изначальное изящество — но будет работать. Хоршо бы и такой пример привести тоже.
Так же следует учитывать, что сортировка по ссылке не означает сортировку по моменту времени.
Если речь про момент времени документов — то да, Ссылка <> МоментВремени — но там как раз Ссылку лучше заменить на МоментВремени — он так же уникален (и не повторяем) как и Ссылка.
Если же имелся в виду какой-то другой момент времени — то надо уточнить — какой и в чём проблема!
(3)Не понимаю к чему Вы так предрались. Запросы в цикле — это не есть абсолютное зло, особенно когда это цикли порционной обработки. При работе с Big Data так вообще без этого НИКАК не обойтись!
Вот создавать каждый раз запрос в цикле — не очень красиво, но для данного примера — абсолютно не критично!
Единственный трындец, который может наступить, это допустим когда мы не прямую таблицу типа обрабатываем, допустим справочника номенклатуры или документа, а какой-нибудь регистр сведений и за такое поле принимаем измерение у которого может пустая ссылка в измерении и таких ссылок больше, чем обрабатываемая порция, вот тогда да, конец света )
(15)У Вас пример, немного из другой оперы, хотя автор в теме статьи не ограничил её только обработкой источников, являющихся только объектными (аля справочников и документов). Да, там где обрабатываемый объект не является ключевым (как ссылка) для выборки — эта техника не сгодится. Но ничего особо сложного — делаете структуру со свойствами — где все ключевые измерения и сохраняеете текующую позицию туда. В запросе по ним всем сортируете, а условие будет на «ИЛИ» по этим измерениям (и так же на «>»), а в цикле обновляете их все из данных выборки в структуре через ЗаполнитьЗначенияСвойств — как -то так — вроде ничего особенного — код сложнее где-то на (10 + количество ключевых полей) процентов.
(13) Похоже про сортировку я действительно не прав. А насчет ссылок и моментов времени хотел предостеречь от заблуждения что сортировка по ссылке равна сортировке по моменту времени. Уберу пока эти моменты. Спасибо за комментарии!
(9) гуиды имеют тип binary(16) и сравниваются побитово как бинарные данные:binary bit-for-bit comparison behavior
Показать
А если кто-то как Darklight думает что гуиды формируются хронологически — почитайте статьюКак формируется GUID?
Гуиды выдаются пулом по 32 штуки для каждого сеанса отдельно, за исключением random based uuid получаемых через Новый УникальныйИдентификатор()
Предлагаю ещё немного рефакторинга:
Показать
Если есть проблема с новыми ссылками, то почему бы не складывать уже обработанные в массив и проверять вхождение при обработки каждой порции?
Показать
Если предыдущий вариант со складированием ссылок обработанных объектов в массив не подходит, то можно попробовать ещё один
особо извращенскийспособ: переделать запрос на НЕ вхождение порции в таблицу изменений справочника. Но здесь имеются подводные камни, которые нужно проверять: наложение общей транзакции, отдельный план обмена с ручной регистрацией и ещё куча других вопросов.если немного подумать то АВТОНОМЕРЗАПИСИ() решает задачу разбиения выборки на порции
размер порции 1000, 10 000,… задаем параметром и дальше простая математика в SQL без шаманских пассов получает порционную выборку
(8) А что изменится если запросить сразу все? При запросе всей информации данный риск даже выше. И данный способ настолько прост и от этого хорош! При этом не надо добавлять реквизиты и пр.
Картинка зачотная!!! пацсталом. ссылочные элементы. порциями. аххах..
Старый способ…
https://infostart.ru/public/893304/
уже описывал в статье
А вообще способ «декоративный» — не надежный!
https://infostart.ru/public/893304/
Не дураки придумали ПланОбмена и номера сообщений — этот вариант тоже показан в статье