(НЕ)много воды
Это пятничный пост, созданный шутки ради. В прошлый раз мы изучали "хардкод" про регистры бухгалтерии. Сегодня же будет простая тема. Вы не найдете здесь никакой полезной информации. Разве что ловкий ход при работе со SQL Server в конце статьи, но он вряд ли будет новостью для многих.
Сегодня мы на простой задаче рассмотрим как меняется код разработчика в зависимости от его опыта работы: как конкретно с платформой 1С:Предприятие, так и просто в роли разработчика. Опыт работы - это не размер стажа в трудовой книжке, а совокупность навыков, приобретенных в процессе решения задач. Поэтому стадии опыта разделим на такие уровни как:
- Начинающий (Junior)
- Уже с опытом (Middle)
- Ведущий разработчик (Senior)
- Эксперт (Expert)
- Огромный опыт (Oldschool)
Последний уровень особый и далее Вы узнаете почему.
Все что Вы увидите ниже будет в рамках кода встроенного языка платформы и немного SQL Server. Да, да! Мы чуть-чуть выйдем за рамки "желтой" экосистемы.
Задача
Вы же знаете, что за справочник "Номенклатура"? Не сомневаюсь! Он есть в большинстве прикладных решений на платформе 1С и содержит список номенклатурных позиций для ведения управленческого, регламентированного и любых других видов учета. В нем есть разнообразные настройки и большой объем связанного функционала (присоединение файлов, печатные формы и др.). Но мы, конечно же, обо всем этом говорить не будем.
Наша задача куда более простая - подсчитать количество элементов справочника "Номенклатура" в информационной базе. Что потом будет с полученным результатом подсчета нас не интересует. То есть фактически, мы реализуем функцию для решения этой задачи в общем модуле конфигурации. Все, никаких подвохов! Что же может такого случиться?
Вот и решения
Пойдем от простого к сложному с неожиданной концовкой.
Начинающий (Junior)
Для начинающего разработчика такое решение может показаться простым и правильным.
Функция ПолучитьКоличествоНоменклатуры() Экспорт
КоличествоЭлементов = 0;
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
КоличествоЭлементов = КоличествоЭлементов + 1;
КонецЦикла;
Возврат КоличествоЭлементов;
КонецФункции
Плюсы:
- Простое и понятное решение
Минусы:
- Низкая производительность
- Сложность в доработке / адаптации под новые требования
А Вы когда-нибудь такое делали? Я да! :)
Уже с опытом (Middle)
Спустя некоторое время разработчик открывает для себя запросы, и мы получаем следующий код.
// Количество элементов в справочнике "Номенклатура"
//
// Возвращаемое значение:
// - Число - количество найденных элементов.
//
Функция ПолучитьКоличествоНоменклатуры() Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КОЛИЧЕСТВО(Номенклатура.Ссылка) КАК КоличествоЭлементов
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
ВыборкаДетальныеЗаписи.Следующий();
КоличествоЭлементов = ВыборкаДетальныеЗаписи.КоличествоЭлементов;
Возврат КоличествоЭлементов;
КонецФункции
Плюсы:
- Эффективный подсчет количества записей запросом по сравнению с объектной моделью
- Легкая доработка / адаптация под новые требования
- Простое и понятное решение
Минусы:
- Производительность намного лучше предыдущего варианта, но все же для подсчета количества записей выполняется сканирование кластерного индекса. Частое выполнение такого запроса отрицательно сказывается на работе информационной системы.
Решение выглядит не плохо. Можно ли его улучшить?
Ведущий разработчик (Senior)
Спустя некоторое время разработчик получает еще порцию опыта, все больше втягивается в стандарты разработки, а также решает кэшировать количество записей для их эффективного получения.
#Область ПрограммныйИнтерфейс
// Количество элементов в справочнике "Номенклатура" из кэша
// в базе данных.
//
// Возвращаемое значение:
// - Число - количество найденных элементов.
//
Функция КоличествоНоменклатуры() Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| КэшПоказателейДляОтчетности.КоличествоЭлементов КАК КоличествоЭлементов
|ИЗ
| РегистрСведений.КэшПоказателейДляОтчетности.СрезПоследних(, ПоказательДляОтчетности = &ПоказательДляОтчетности) КАК КэшПоказателейДляОтчетности";
Запрос.УстановитьПараметр("ПоказательДляОтчетности", Справочники.ПоказателиДляОтчетности.КоличествоНоменклатуры);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
КоличествоЭлементов = 0;
Если ВыборкаДетальныеЗаписи.Следующий() Тогда
КоличествоЭлементов = ВыборкаДетальныеЗаписи.КоличествоЭлементов;
КонецЕсли;
Возврат КоличествоЭлементов;
КонецФункции
#КонецОбласти
Как мы видим, теперь значения кэшируются в периодическом регистре сведений "Кэш показателей для отчетности". Обновление значения происходит регламентным заданием "Обновить кэш количества номенклатуры" с произвольным расписанием.
Плюсы:
- Максимальная производительность при получении количества записей
- Расширенные возможности по хранению историчности значения
Минусы:
- По сравнению с предыдущими решениями код выглядит значительно сложнее
- Не смотря на кэширование, все же требуется сканирование кластерного индекса для подсчета количества записей
- Присутствует некоторая неточность в данных, т.к. требуется постоянное обновление кэша регламентным заданием с некоторым "лагом" по времени
Ради оптимизации решение было усложнено, но недостаток в виде сканирования кластерного индекса все же остается.
Эксперт (Expert)
Но что, если и это нас не устраивает и нам нужно еще оптимизировать получение количества записей. Нужно больше оптимизаций!
#Область ПрограммныйИнтерфейс
// Количество элементов в справочнике "Номенклатура" из собранной статистики СУБД.
//
// Возвращаемое значение:
// - Число - количество найденных элементов.
//
Функция КоличествоНоменклатуры() Экспорт
КоличествоЭлементов = Неопределено;
МетаданныеТаблицы = Метаданные.Справочники.Номенклатура;
МассивМетаданных = Новый Массив;
МассивМетаданных.Добавить(МетаданныеТаблицы);
ИнформацияОСтруктуреБазы = ПолучитьСтруктуруХраненияБазыДанных(МассивМетаданных, Истина);
НайденныеТаблицы = ИнформацияОСтруктуреБазы.НайтиСтроки(Новый Структура("Назначение", "Основная"));
Если НайденныеТаблицы.Количество() = 1 Тогда
ИнформацияОТаблице = НайденныеТаблицы.Получить(0);
ИмяТаблицыБазыДанных = ИнформацияОТаблице.ИмяТаблицыХранения;
СтрокаПодключения = СтрокаПодключенияКБазеДанных();
СоединениеСБазой = СоздатьСоединениеСБазой(СтрокаПодключения);
Если НЕ СоединениеСБазой = Неопределено Тогда
Попытка
ТекстЗапроса = ТекстЗапросаКоличествоЗаписейНоменклатуры();
ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{TableName}", ИмяТаблицыБазыДанных);
КомандаБД = Новый COMОбъект("ADODB.Command");
КомандаБД.ActiveConnection = СоединениеСБазой;
КомандаБД.CommandText = ТекстЗапроса;
КомандаБД.CommandTimeout = 60;
РезультатБД = КомандаБД.Execute();
Пока НЕ РезультатБД.eof() Цикл
КоличествоЭлементов = РезультатБД.Fields.Item("rows").Value;
РезультатБД.MoveNext();
КонецЦикла;
Исключение
КоличествоЭлементов = Неопределено;
КонецПопытки;
КомандаБД = Неопределено;
СоединениеСБазой = Неопределено;
КонецЕсли;
КонецЕсли;
Если КоличествоЭлементов = Неопределено Тогда
КоличествоЭлементов = КоличествоНоменклатурыСлужебный();
КонецЕсли;
Возврат КоличествоЭлементов;
КонецФункции
#КонецОбласти
На этот раз была применена радикальная оптимизация: для получения количества записей в таблице используется собранная статистика SQL Server, которую СУБД обновляет достаточно оперативно. Чтобы такой подход заработал, придется выполнять SQL-запрос напрямую к базе данных с помощью ADO. Подробнее о реализации смотрите в листинге выше.
Плюсы:
- Максимальная производительность при получении количества записей таблицы.
Минусы:
- Сложное решение в части разработки и сопровождения
- Могут быть незначительные отклонения значения от фактического количества записей на короткий промежуток времени
Фактически, это самое сложное решение как в части разработки, так и в части сопровождения, но зато с максимальной эффективностью.
Огромный опыт (Oldschool)
А теперь представим, что у разработчика огромный опыт с разными версиями платформы 1С, за плечами множество внедрений и успешных проектов. Какое решение в такой ситуации будет реализовано?
Функция КоличествоНоменклатуры() Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КОЛИЧЕСТВО(Номенклатура.Ссылка) КАК КоличествоЭлементов
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
ВыборкаДетальныеЗаписи.Следующий();
КоличествоЭлементов = ВыборкаДетальныеЗаписи.КоличествоЭлементов;
Возврат КоличествоЭлементов;
КонецФункции
Правильно - самое эффективное с точки зрения простоты разработки и скорости работы. Ведь все мы знаем, что предварительные оптимизации - зло.
Весело?
С пятницей, коллеги (если Вы читаете это в пятницу)! И хорошего дня в любом случае.
Мы рассмотрели пять решений одной простой задачи. Как мы видим, даже простейшая задача может быть усложнена в несколько раз и использовать нестандартные подходы при реализации. Чем больше опыта у разработчика, тем сложнее задачи он может решать. Но есть и другая сторона - простые задачи в таких случаях могут решаться слишком сложными способами, которые не всегда обоснованы.
Думаю, если проследите за изменением кода, то все встанет на свои места.
А как бы Вы решали подобную задачу?
Авторские разработки
-
Транслятор запросов 1С в SQL - инструмент для трансляции запросов платформы 1С в SQL, а также их диагностики.
-
Помощник работы с идентификаторами объектов - инструмент для расширенного анализа идентификаторов объектов.
-
Анализ производительности APDEX - отчет для просмотра и анализа замеров производительности в конфигурациях на базе БСП.
-
Путеводитель по истории релизов - отчет по истории выпуска релизов продуктов фирмы "1С" и анализа информации по обновлениям.
-
Просмотр и анализ структуры базы данных (отчет на СКД) - отчет для просмотра и анализа структуры базы данных с поддержкой файловых баз (ограниченный режим), а также баз на SQL Server и PostgreSQL.
-
Просмотр и анализ журнала регистрации (отчет на СКД) - отчет на базе системы компоновки данных (СКД) для просмотра записей журнала регистрации.
-
Обозреватель криптографии - отчет для просмотра доступных провайдеров и сертификатов криптографии на сервере и клиенте.
-
Пакетная выгрузка / загрузка внешних отчетов и обработок - пакетная выгрузка / загрузка внешних отчетов и обработок для массовый манипуляций с ними.
-
Мастер полнотекстового поиска - набор инструментов для работы с полнотекстовым индексом платформы 1С. Стандартные и расширенные возможности.
-
Командный интерпретатор для 1С - инструмент для выполнения команд CMD / PowerShell из 1С.