Обработка элементов ссылочных типов порциями
Программирование - Практика программирования
В рекомендации от 1С Оптимизация использования оперативной памяти предлагается получать данные порциями при потенциально неограниченных выборках.
В качестве примера приводится такой участок кода:
ВсеОбработано = Ложь;
Пока Истина Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| <условие выборки необработанных записей>";
РезультатЗапроса = Запрос.Выполнить();
ВсеОбработано = РезультатЗапроса.Пустой();
Если ВсеОбработано Тогда
Прервать;
КонецЕсли;
// Обход порции результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Обработка элемента выборки
// ...
КонецЦикла;
КонецЦикла;
Но зачастую бывает сложно подобрать это самое "условие выборки необработанных записей".
Я предлагаю простое решение для выборки ссылочных данных: использовать в качестве указателя на порцию данных ссылку.
Для этого необходимо внести следующие изменения:
- Перед погружением в цикл запросов получаем пустую ссылку (она всегда меньше любой непустой ссылки) и записываем ее в переменную указывающую на последнюю полученную ссылку (ПоследняяСсылка).
- В запросе упорядочиваем выборку по ссылке (не включая автоупорядочивание, т.к. это приведет к сортировке по представлению) и добавляем отбор по ссылке (Ссылка > &ПоследняяСсылка).
- На каждом проходе выборки обновляем переменную ПоследняяСсылка, присваивая ей текущую обрабатываемую ссылку.
Получим следующий код:
ВсеОбработано = Ложь;
ПоследняяСсылка = Справочники.Номенклатура.ПустаяСсылка();
Пока Истина Цикл
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка > &ПоследняяСсылка
| И <условия отбора требуемые для прикладной задачи>
|
|УПОРЯДОЧИТЬ ПО
| Ссылка";
Запрос.УстановитьПараметр("ПоследняяСсылка",ПоследняяСсылка);
РезультатЗапроса = Запрос.Выполнить();
ВсеОбработано = РезультатЗапроса.Пустой();
Если ВсеОбработано Тогда
Прервать;
КонецЕсли;
// Обход порции результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ПоследняяСсылка = ВыборкаДетальныеЗаписи.Ссылка;
// Обработка элемента выборки
// ...
КонецЦикла;
КонецЦикла;
Предложенное решение лежит на поверхности, но мне в голову пришло не сразу, так что, надеюсь, может быть полезным :)
П.С. Я намеренно не стал проводить рефакторинг кода с ИТС, чтобы добавленный код был более заметен. На мой взгляд создавать запрос стоит перед циклом, а не внутри него и нет необходимости создавать переменную ВсеОбработано.
ПоследняяСсылка = Справочники.Номенклатура.ПустаяСсылка();
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка > &ПоследняяСсылка
| И <условие выборки необработанных записей>
|
|УПОРЯДОЧИТЬ ПО
| Ссылка";
Пока Истина Цикл
Запрос.УстановитьПараметр("ПоследняяСсылка",ПоследняяСсылка);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Прервать;
КонецЕсли;
// Обход порции результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
ПоследняяСсылка = ВыборкаДетальныеЗаписи.Ссылка;
// Обработка элемента выборки
// ...
КонецЦикла;
КонецЦикла;
См. также
Специальные предложения
Почему:
1. Никто вам не обещал и гарантирет того, что уиды генерируются последовательно.
2. Обмены
Поэтому и создаются регистры сведений ОчередьХХХХХХХХ
1. Обмен данных с другой системой, формирующих UID на другом компьютере, выполненный параллельно с обработкой
2. UID задаётся вручную строкой не по тем же правилам его формирования - опять таки каким-то параллельным процессом
Для данного примера вероятен только п.1. (п.2. почти невероятен - но кто его знает, что там пишу криворукие программисты на местах)
Поэтому, можно на время обработки остановить обмен. Это вообще важное замечание для ряда подобных обработок - ведь появление пропущенной ссылки тут не самое страшное, что может произойти, когда в данные вклинится параллельный процесс.
Ну или, хотя бы блокировку данных надо наложить (увы - в данном примере - на всю таблицу)!
Да, кстати, замечу, что если вообще не заморачиваться - и выбрать все Ссылки разом а потом их обрабатывать - то это так же ни гарантирует, что в процессе обработки не появятся новые Ссылки (в любой "хронологии"), которые останутся не обработанными - проблема не повторяемого чтения (или наоборот - будут удалены - проблема фантомов). Вот для этого и существуют блокировки данных!
Вот создавать каждый раз запрос в цикле - не очень красиво, но для данного примера - абсолютно не критично!
Функция Получить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 думает что гуиды формируются хронологически - почитайте статью
Гуиды выдаются пулом по 32 штуки для каждого сеанса отдельно, за исключением random based uuid получаемых через Новый УникальныйИдентификатор()
в данном случае нельзя сортировать данные по другим полям, кроме ссылки, так что если нужно обрабатывать данные в определенной последовательности, то такой метод не подойдет.
Зачем так людей пугаете. Конечно же можно - если поля сортировки будут ниже поля упорядочивания по ссылеке - то никаких проблем (но это бывает редко - когда ссылка это и есть ключевой обрабатываемый объект)
А вот, если поля для сортировки стоят раньше ссылки - то тогда их так же придётся включить в контроллируемые (по классическому подходу) - и пример несколько усложнится, потеряв своё изначальное изящество - но будет работать. Хоршо бы и такой пример привести тоже.
Так же следует учитывать, что сортировка по ссылке не означает сортировку по моменту времени.
Если речь про момент времени документов - то да, Ссылка <> МоментВремени - но там как раз Ссылку лучше заменить на МоментВремени - он так же уникален (и не повторяем) как и Ссылка.
Если же имелся в виду какой-то другой момент времени - то надо уточнить - какой и в чём проблема!
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка > &ПоследняяСсылка
| И <условие выборки необработанных записей>
|
|УПОРЯДОЧИТЬ ПО
| Ссылка";
Запрос.УстановитьПараметр("ПоследняяСсылка", Справочники.Номенклатура.ПустаяСсылка());
РезультатЗапроса = Запрос.Выполнить();
Пока НЕ РезультатЗапроса.Пустой() Цикл
// Обход порции результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
// Обработка элемента выборки
// ...
КонецЦикла;
Запрос.УстановитьПараметр("ПоследняяСсылка", ВыборкаДетальныеЗаписи.Ссылка);
РезультатЗапроса = Запрос.Выполнить();
КонецЦикла;
ПоказатьЗапрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1000
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.Ссылка В (&МассивОбработанныхСсылок)
| И <условие выборки необработанных записей";
МассивОбработанныхСсылок = Новый Массив;
Запрос.УстановитьПараметр("МассивОбработанныхСсылок", МассивОбработанныхСсылок);
РезультатЗапроса = Запрос.Выполнить();
Пока НЕ РезультатЗапроса.Пустой() Цикл
// Обход порции результата запроса
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
МассивОбработанныхСсылок.Добавить(ВыборкаДетальныеЗаписи.Ссылка);
// Обработка элемента выборки
// ...
КонецЦикла;
Запрос.УстановитьПараметр("МассивОбработанныхСсылок", МассивОбработанныхСсылок);
РезультатЗапроса = Запрос.Выполнить();
КонецЦикла;
Показатьуже описывал в статье
А вообще способ "декоративный" - не надежный!
Не дураки придумали ПланОбмена и номера сообщений - этот вариант тоже показан в статье

Просмотры 2155
Загрузки 0
Комментарии 25
Создание 11.02.19 10:40
Обновление 11.02.19 10:40
№ Публикации 1000333
Рубрики Практика программирования
Кому Программист
Тип файла Нет файла
Платформа Платформа 1С v8.x (все механизмы)
Конфигурация Не имеет значения
Операционная система Не имеет значения
Страна Не имеет значения
Отрасль Не имеет значения
Налоги Не имеет значения
Вид учета Не имеет значения
Раздел учета Не имеет значения
Доступ к файлу Бесплатно (free)
Код открыт Да

