Ensure в цикле вместо EnsureBatch
критично по производительности
Каждый вызов Ensure делает как
минимум один запрос к БД (поиск по наименованию),
а при создании — ещё один (запись). При 200 позициях в списке это 200–400 запросов.
На большой базе это десятки секунд или таймаут.
// 200 позиций = 200–400 запросов!
Для Каждого Стр Из ДанныеExcel Цикл
Ном = A1sCatalogs.Ensure(
"Номенклатура",
Стр.Наименование,
A1sDS.Of("Родитель", Гр));
A1sDocs.AddRow(Dok, "Продукция",
A1sDS.Of("Номенклатура", Ном, ...));
КонецЦикла;
// Все имена одним батчем ДО цикла
Имена = Новый Массив;
Для Каждого Стр Из ДанныеExcel Цикл
Имена.Добавить(Стр.Наименование);
КонецЦикла;
A1sCatalogs.EnsureBatch(
"Номенклатура",
A1sAR.RemoveDuplicates(Имена),
A1sDS.Of("Родитель", Гр));
// Теперь LoadRows найдёт их в базе
A1sDocs.LoadRows(Dok, "Продукция", ДанныеExcel);
EnsureBatch, PickByName), потом цикл. Внутри цикла — только работа с
уже полученными ссылками. Это снижает число запросов к БД с N до 1.GetObject для чтения вместо Details
лишние запросы и память
GetObject()
открывает весь объект документа или справочника — загружает все реквизиты и все ТЧ.
Если нужно просто прочитать несколько полей — это избыточно.
Details()
делает один лёгкий запрос только за нужными полями.
ДокОбъект = ДокСсылка.GetObject();
Сообщить(ДокОбъект.Номер);
Сообщить(ДокОбъект.Дата);
Сообщить(ДокОбъект.Комментарий);
// Загружены ВСЕ ТЧ документа — зря
Инфо = A1sDocs.Details(
ДокСсылка,
"Комментарий");
Сообщить(Инфо.Number);
Сообщить(Инфо.Date);
Сообщить(Инфо.Комментарий);
// Один лёгкий запрос, нет ТЧ
Details для документов и Details для справочников. Для изменения —
GetObject + изменить + Записать или Update.
Поиск / ByName внутри цикла загрузки
критично по производительности
Если внутри цикла по строкам документа вызывать ByName, ByAttr или
Ensure
для каждой строки — получаем N запросов. Если ещё и список групп ищется внутри — N×M.
Решение: собрать все нужные ссылки до цикла через batch-функции.
Для Каждого Стр Из ДанныеExcel Цикл
// Запрос к БД на КАЖДУЮ строку!
ЕдИзм = A1sCatalogs.ByName(
"КлассификаторЕдиницИзмерения",
Стр.Единица);
Группа = A1sCatalogs.ByName(
"НоменклатурныеГруппы",
Стр.Группа);
// ещё 2 запроса на строку...
КонецЦикла;
// 2 запроса на весь список — до цикла
МапЕд = A1sCatalogs.PickByName(
"КлассификаторЕдиницИзмерения",
СписокЕдиниц); // Array→Map
МапГрупп = A1sCatalogs.PickByName(
"НоменклатурныеГруппы",
СписокГрупп);
Для Каждого Стр Из ДанныеExcel Цикл
ЕдИзм = МапЕд[Стр.Единица]; // O(1)
Группа = МапГрупп[Стр.Группа]; // O(1)
КонецЦикла;
Игнорирование Failed из PostAll / UpdateAll
скрытая потеря данных
PostAll, UnpostAll, UpdateAll и MoveBatch возвращают
структуру
{Success, Failed}.
Если не проверять Failed — часть операций молча провалится, документы останутся непроведёнными,
а вы узнаете об этом только при следующей выгрузке или жалобе пользователя.
// PostAll вернул результат — мы его выбросили
A1sDocs.PostAll(МассивДоков);
Сообщить("Готово");
// Сколько провалилось? Неизвестно
Итог = A1sDocs.PostAll(МассивДоков);
Сообщить("✓ " + Итог.Success.Количество());
Если Итог.Failed.Количество() > 0 Тогда
Сообщить("✗ Ошибки: "
+ Итог.Failed.Количество());
КонецЕсли;
{Success, Failed},
всегда добавляйте проверку: Если Итог.Failed.Количество() > 0 Тогда. Даже если просто пишете
в лог — это лучше, чем молчание.AddRow со строками вместо AddRowAuto
ошибка типов при записи
AddRow ожидает готовые ссылки. Если передать строку в поле типа
СправочникСсылка.Номенклатура — платформа выбросит исключение при записи документа
или молча запишет пустую ссылку. Для строковых данных используйте AddRowAuto.
// "Квартира 101" — строка, не ссылка
// При записи → ошибка "Несоответствие типов"
A1sDocs.AddRow(Dok, "Продукция",
A1sDS.Of(
"Номенклатура", "Квартира 101",
"Количество", 1));
// AddRowAuto: видит что "Номенклатура" — ссылочное поле
// вызывает EnsureByType → получает ссылку
A1sDocs.AddRowAuto(Dok, "Продукция",
A1sDS.Of(
"Номенклатура", "Квартира 101",
"Количество", 1));
AddRowAuto или LoadRows. Данные уже в виде ссылок (из запроса, из справочника) —
используйте AddRow или AddRows.
Ensure вместо EnsureUpdate при регулярной синхронизации
данные не обновляются
Ensure — «найди или создай». Нашёл → вернул ссылку и ничего не изменил.
Если сегодня прайс изменился — Ensure не обновит цену у существующего элемента.
Для регулярной синхронизации нужен EnsureUpdate — он всегда обновляет реквизиты.
// Квартира 101 уже есть в базе с ценой 3 500 000
// EnsureUpdate нужен, но используем Ensure
Для Каждого Поз Из НовыйПрайс Цикл
A1sCatalogs.Ensure(
"Номенклатура",
Поз.Наименование,
A1sDS.Of("Цена", Поз.Цена));
КонецЦикла;
// Цена не обновилась! Ensure нашёл элемент
// и вернул ссылку без изменений.
Для Каждого Поз Из НовыйПрайс Цикл
A1sCatalogs.EnsureUpdate(
"Номенклатура",
Поз.Наименование,
A1sDS.Of("Цена", Поз.Цена));
КонецЦикла;
// Нашёл → обновит Цену.
// Не нашёл → создаст с Ценой.
Хардкод имени справочника вместо EnsureByType
хрупкий код — ломается при смене модели
Если в универсальном загрузчике прибить строку "Номенклатура" — он перестанет работать
как только вы захотите использовать его с другим документом или ТЧ.
EnsureByType берёт имя справочника из метаданных реквизита — код остаётся универсальным.
// Работает только с "Номенклатура"
// Для другого документа — переписывать
Для Каждого Стр Из ДанныеJSON Цикл
Ном = A1sCatalogs.Ensure(
"Номенклатура",
Стр.item);
A1sDocs.AddRow(Dok, "Продукция",
A1sDS.Of("Номенклатура", Ном));
КонецЦикла;
// Работает с любым документом и ТЧ
// Имя справочника — из метаданных
ТипНом = A1sDocs.TabAttrType(
ИмяДокумента, ИмяТЧ, "Номенклатура");
Для Каждого Стр Из ДанныеJSON Цикл
Ном = A1sCatalogs.EnsureByType(
ТипНом, Стр.item);
A1sDocs.AddRow(Dok, ИмяТЧ,
A1sDS.Of("Номенклатура", Ном));
КонецЦикла;
Fluent на клиенте или в &НаКлиентеНаСервере
ошибка выполнения
Все четыре Fluent-обёртки создают объект обработки через Обработки.A1sDP_Docs.Создать().
Обработки видны только на сервере. На клиенте или в &НаКлиентеНаСервере
этот вызов упадёт с ошибкой «Обработка не найдена».
// &НаКлиенте или &НаКлиентеНаСервере
&НаКлиентеНаСервере
Процедура ОбработатьФорму()
// ОШИБКА: Обработки недоступны
Ссылка = A1sDocs.On("ВыпускПродукцииУслуг", ...)
.Post();
КонецПроцедуры
// Обернуть в серверный вызов
&НаСервере
Функция СоздатьВыпускНаСервере(Данные)
Возврат A1sDocs.On(
"ВыпускПродукцииУслуг", Данные)
.Post();
КонецФункции
// Или — обычные функции без Fluent
Dok = A1sDocs.Of(...); // тоже только &НаСервере!
A1sDS.Of с вложенной структурой вместо пар
лишний код, теряет смысл Of
Иногда создают структуру заранее, а потом передают её в Of как единственный аргумент.
Это работает, но теряет весь смысл Of — инлайн-создание из пар.
Если структура уже есть — передавайте её напрямую. Если нужны пары — используйте Of.
// Создаём структуру... чтобы передать в Of?
ДанныеШапки = Новый Структура;
ДанныеШапки.Вставить("Дата", '20260201');
ДанныеШапки.Вставить("Организация", Орг);
Dok = A1sDocs.Of(
"ВыпускПродукцииУслуг",
A1sDS.Of(ДанныеШапки));
// A1sDS.Of с одним аргументом — лишнее
// Если структура уже есть — просто передать
Dok = A1sDocs.Of(
"ВыпускПродукцииУслуг",
ДанныеШапки); // уже структура
// Если нет — инлайн через Of
Dok = A1sDocs.Of(
"ВыпускПродукцииУслуг",
A1sDS.Of(
"Дата", '20260201',
"Организация", Орг));
SelfTest — проверить что модули работают
Каждый модуль содержит SelfTest() — набор
встроенных тестов.
Они проверяют базовые операции без обращения к реальным данным базы.
Запускать при первой установке, после обновления библиотеки или при загадочных ошибках.
// ── Запустить SelfTest всех модулей ──────────────────────────────────────
// Поместить в обработку-проверку или запустить из консоли запросов
Модули = A1sDS.Of(
"A1sDS", A1sDS.SelfTest(),
"A1sAR", A1sAR.SelfTest(),
"A1sCatalogs", A1sCatalogs.SelfTest(),
"A1sDocs", A1sDocs.SelfTest());
Сообщить("════ SelfTest результаты ════");
Для Каждого КЗ Из Модули Цикл
Сообщить((КЗ.Значение ? "✓ " : "✗ ") + КЗ.Ключ);
КонецЦикла;
// Ожидаемый вывод:
// ✓ A1sDS
// ✓ A1sAR
// ✓ A1sCatalogs
// ✓ A1sDocs
// ── Один модуль отдельно ──────────────────────────────────────────────────
Ок = A1sDocs.SelfTest();
Если НЕ Ок Тогда
ВызватьИсключение "A1sDocs.SelfTest: проверка не пройдена";
КонецЕсли;
// ── Что проверяют SelfTest ────────────────────────────────────────────────
// A1sDS: Of, OfKeys, HasKey, IsEmpty, Equals, Merge, Defaults...
// A1sAR: Of, OfN, Contains, IsEmpty, RemoveDuplicates, Sort...
// A1sCatalogs: IsCatalog, IsRef, GetObject, GetRef...
// A1sDocs: IsDocument, IsRef, IsObject, GetObject, GetRef, MetaCount...
// ════════════════════════════════════════════════════════════════════════════
// ЧЕК-ЛИСТ при загадочной ошибке
// ════════════════════════════════════════════════════════════════════════════
// 1. Проверить что модули работают (SelfTest выше)
// 2. Проверить что ТЧ не пустая перед Post
Если A1sDocs.RowCount(Dok, "Продукция") = 0 Тогда
Сообщить("ВНИМАНИЕ: пустая ТЧ");
КонецЕсли;
// 3. Проверить что обязательные реквизиты шапки заполнены
Инфо = A1sDocs.Details(Dok, "Организация, Склад");
Если НЕ ЗначениеЗаполнено(Инфо.Организация) Тогда
Сообщить("ВНИМАНИЕ: не заполнена Организация");
КонецЕсли;
// 4. Проверить существование справочников до загрузки строк
ТестСсылка = A1sCatalogs.ByName("Номенклатура", "Квартира 101");
Если НЕ ЗначениеЗаполнено(ТестСсылка) Тогда
Сообщить("'Квартира 101' не найдена в справочнике");
КонецЕсли;
// 5. Проверить что код работает на сервере
// (добавить &НаСервере к процедуре если нет)
// 6. Проверить IsPosted у существующих документов
Если НЕ A1sDocs.IsPosted(ДокСсылка) Тогда
Сообщить("Документ не проведён: " + ДокСсылка);
КонецЕсли;
| # | Антипаттерн | Эффект | Исправление | Критичность |
|---|---|---|---|---|
| АП-1 | Ensure в цикле | N запросов к БД вместо 1 | EnsureBatch до цикла | Высокая |
| АП-2 | GetObject для чтения | Лишний тяжёлый запрос | Details(ExtraAttrs) | Средняя |
| АП-3 | Поиск внутри цикла | N×M запросов | PickByName батчем до цикла | Высокая |
| АП-4 | Игнор Failed | Скрытые непроведённые | Проверять .Failed.Количество() | Высокая |
| АП-5 | AddRow со строками | Ошибка типов при записи | AddRowAuto / LoadRows | Высокая |
| АП-6 | Ensure при синхронизации | Данные не обновляются | EnsureUpdate | Высокая |
| АП-7 | Хардкод справочника | Хрупкий код | EnsureByType + TabAttrType | Средняя |
| АП-8 | Fluent на клиенте | Ошибка «Обработка не найдена» | Только &НаСервере | Высокая |
| АП-9 | Of с промежуточной структурой | Лишний код без смысла | Передать структуру напрямую | Низкая |