Цели дня
- Научиться «дымовым» проверкам (минимальные, но информативные).
- Измерять время выполнения и размер результата.
- Снижать стоимость запроса: меньше колонок, меньше строк.
- Использовать
COUNT/EXISTSвместо «принести всё». - Понимать, как пагинировать без «OFFSET» — keyset-пагинация.
Дымовые проверки (smoke)
Минимальный тест даёт ответ: «жив» ли источник данных, корректны ли колонки и параметры.
Процедура SmokeQ(ТекстQ, П1 = Неопределено, П2 = Неопределено) Экспорт
// Забираем только одну строку — дёшево и информативно
ТекстПроба = "
|ВЫБРАТЬ ПЕРВЫЕ 1
| *
|ИЗ (" + ТекстQ + ") КАК Q";
Попытка
Таб = A1sQ.Unload(ТекстПроба, П1, П2);
A1sLog.Info("Day7", "Smoke OK: колонок=" + Таб.Колонки.Количество() + ", строк=" + Таб.Количество());
Исключение
A1sLog.Error("Day7", "Smoke FAIL: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Примечание: такой обёрткой удобно быстро проверять «сложные» запросы, не вытягивая весь объём.
Хронометраж (простая метрика времени)
Функция UnloadTimed(ТекстQ, П1 = Неопределено, П2 = Неопределено) Экспорт
Старт = ТекущаяДата();
Таб = A1sQ.Unload(ТекстQ, П1, П2);
Сек = РазностьДат(ТекущаяДата(), Старт, ВидРазницыДат.ВСекундах);
A1sLog.Info("Day7", "Unload: строк=" + Таб.Количество() + ", сек=" + Сек);
Возврат Таб;
КонецФункции
Функция ExecuteQTimed(ТекстQ, П1 = Неопределено, П2 = Неопределено) Экспорт
Старт = ТекущаяДата();
Рез = A1sQ.ExecuteQ(ТекстQ, П1, П2);
Сек = РазностьДат(ТекущаяДата(), Старт, ВидРазницыДат.ВСекундах);
A1sLog.Info("Day7", "ExecuteQ: сек=" + Сек);
Возврат Рез;
КонецФункции
Эти функции помогают быстро понять, что «тормозит», не меняя боевой код.
Минимизируйте колонки и строки
- Только нужные поля: ставьте явные алиасы (
КАК ID,КАК Nameи т. п.). - ПЕРВЫЕ &Лимит — особенно для экранов с превью.
- COUNT/EXISTS, когда нужен ответ «сколько/есть?» вместо выгрузки всех строк.
- Фильтры по ключу/дате ставьте в
ГДЕраньше всего (лучшая селективность).
Было: лишние поля и без лимита
ТекстQ = "
|ВЫБРАТЬ
| *
|ИЗ Справочник.Номенклатура КАК Ном
";
Таб = A1sQ.Unload(ТекстQ); // дорого, приносит всё
Стало: минимум полей + лимит
ТекстQ = "
|ВЫБРАТЬ ПЕРВЫЕ &Лимит
| Ном.Ссылка КАК ID,
| Ном.Наименование КАК Name
|ИЗ Справочник.Номенклатура КАК Ном
|УПОРЯДОЧИТЬ ПО Name
";
Таб = A1sQ.Unload(ТекстQ, 20);
Имена = Таб.ВыгрузитьКолонку("Name");
COUNT и EXISTS вместо «принести всё»
COUNT
ТекстQ = "
|ВЫБРАТЬ
| COUNT(Ном.Ссылка) КАК Cnt
|ИЗ Справочник.Номенклатура КАК Ном
|ГДЕ Ном.ЭтоГруппа = &ТолькоГруппы
";
Рез = A1sQ.ExecuteQ(ТекстQ, Ложь);
Вб = Рез.Выбрать();
Cnt = 0; Если Вб.Следующий() Тогда Cnt = Вб.Cnt; КонецЕсли;
A1sLog.Info("Day7", "Позиции (не группы): " + Cnt);
EXISTS (через «ПЕРВЫЕ 1»)
ТекстQ = "
|ВЫБРАТЬ ПЕРВЫЕ 1
| 1 КАК One
|ИЗ Справочник.Номенклатура КАК Ном
|ГДЕ Ном.Наименование = &Имя
";
Есть = A1sO.NotEmpty(ExecuteQFirstValue(ТекстQ, "Кабель ПВС"));
A1sLog.Info("Day7", "Существует точное имя: " + A1sS.AsString(Есть));
Смотри также готовые QT-шаблоны на Дне 6.
Поиск по маске: избегайте «%строка%»
ПОДОБНО &Маскас шаблоном"%текст%"отключает использование индекса.- Гораздо дешевле «начинается с»: маска вида
"Текст%"или условие НАЧИНАЕТСЯ С (если применимо).
Было: любой фрагмент
ТекстQ = "
|ВЫБРАТЬ ПЕРВЫЕ &Лимит
| Ном.Ссылка, Ном.Наименование
|ИЗ Справочник.Номенклатура КАК Ном
|ГДЕ Ном.Наименование ПОДОБНО &Маска
";
Таб = A1sQ.Unload(ТекстQ, 50, "%кабель%");
Стало: начинается с
ТекстQ = "
|ВЫБРАТЬ ПЕРВЫЕ &Лимит
| Ном.Ссылка, Ном.Наименование
|ИЗ Справочник.Номенклатура КАК Ном
|ГДЕ Ном.Наименование НАЧИНАЕТСЯ С &Префикс
|УПОРЯДОЧИТЬ ПО Ном.Наименование
";
Таб = A1sQ.Unload(ТекстQ, 50, "Кабель");
Пагинация без OFFSET: keyset-подход
Храним «последнюю пару» (LastName, LastID) и запрашиваем дальше по ключу, а не по смещению.
Функция НомПостранично(Лимит, LastName = "", LastID = Неопределено) Экспорт
Осн = "
|ВЫБРАТЬ ПЕРВЫЕ &Лимит
| Ном.Наименование КАК Name,
| Ном.Ссылка КАК ID
|ИЗ Справочник.Номенклатура КАК Ном
";
Если A1sO.NotEmpty(LastName) Тогда
Осн = Осн + "
|ГДЕ (Ном.Наименование > &LastName)
| ИЛИ (Ном.Наименование = &LastName И Ном.Ссылка > &LastID)
";
Осн = Осн + "
|УПОРЯДОЧИТЬ ПО Name, ID
";
Возврат A1sQ.Unload(Осн, Лимит, LastName, LastID);
Иначе
Осн = Осн + "
|УПОРЯДОЧИТЬ ПО Name, ID
";
Возврат A1sQ.Unload(Осн, Лимит);
КонецЕсли;
КонецФункции
Keyset стабилен: не «перескакивает» строки при одновременных изменениях данных.
Чек-лист оптимизаций
- □ Лишних колонок нет; везде читаемые алиасы.
- □ Для превью —
ПЕРВЫЕ &Лимит; для «есть/сколько» —EXISTS/COUNT. - □ Маска «начинается с» вместо «%внутри%», когда это возможно. li>
- □ Пагинация — keyset (по последнему ключу), а не смещение.
- □ Есть дымовой тест и замеры времени в логах.
Практика (15–30 минут)
- Обверните ваш «тяжёлый» запрос в
SmokeQиUnloadTimed— зафиксируйте секунды и объём. - Сократите колонки до необходимых; добавьте
ПЕРВЫЕ &Лимит— сравните время. - Замените «%маска%» на «НАЧИНАЕТСЯ С» там, где применимо.
- Сделайте страницу списка с keyset-пагинацией (по Name, ID).
- Все результаты логируйте через
A1sLog— получите объективные цифры.
Что дальше
День 8: Поиск и выбор данных — практические приёмы фильтрации и выборок для UI/отчётов.