Сергей Осипов (koeniger) где-то в 2001 Сервисные функции для сложных отчетов
Написание всевозможных отчетов и отчётиков для конфигураций V7 одно из основных
направлений работы прикладного программиста.
Часто заказчик требует сложные отчеты с итогами и группировками. Однажды мне надоело писать
бесконечные
ИтогоНачКол = ИтогоНачКол + НачКол;
Тогда я вспомнил, что настоящий программист это в первую очередь ленивый
программист, и написал библиотеку сервисных функций, позволяющую избавиться от этой рутины.
С помощью этой библиотеки писать сложные отчеты так же легко, как составлять SQL-запросы
(ну, я бы не назвал «лёгким и приятным» написание и отладку
многоэтажного вложенного SELECT'a ехидный комментарий редактора).
А если вы не знаете, что такое SQL-запрос, выражусь иначе раз и навсегда забудьте про
Итого.
Как это работает?
Пусть у нас есть задача сделать отчет вида:
|
НачОст |
Приход |
Расход |
КонОст |
Мясо |
ГруппаНачОст |
ГруппаПриход |
ГруппаРасход |
ГруппаКонОст |
баранина |
X |
X |
X |
X |
говядина |
X |
X |
X |
X |
Спиртное |
ГруппаНачОст |
ГруппаПриход |
ГруппаРасход |
ГруппаКонОст |
водка |
X |
X |
X |
X |
коньяк |
X |
X |
X |
X |
|
ИтогоНачОст |
ИтогоПриход |
ИтогоРасход |
ИтогоКонОст |
Обрабатывая запрос по остаткам, мы создаём одну или несколько таблиц значений, на основании
которых потом делаем печатную форму.
Пусть мы сохраняем результаты запроса в таблице Данные вида:
Группа |
Товар |
НачОст |
Приход |
Расход |
КонОст |
Чтобы не писать бесконечные СоздатьОбъект(), ДобавитьСтроку(), ДобавитьКолонку(),
мы используем сервисные функции:
Данные=ТЗНовая(); //Создаем пустую новую таблицу
Пока Запрос.Группировка("Товар")=1 Цикл
ТЗВнести(Данные,
"Группа",Запрос.Товар.Родитель,
"Товар",Запрос.Товар,
"НачОст",Запрос.НачОст,
"КонОст",Запрос.КонОст,
....
);
КонецЦикла;
Свернув таблицу по колонке группе Группа, мы получим таблицу Группы:
Группы.Загрузить(Данные);
Группы=Данные.Свернуть("Группа","");
Теперь мы индексируем таблицу Данные, чтобы в ней можно было получать
быстрые итоги:
ТЗИндексация(Данные);
А теперь, вместо того, чтобы накапливать промежуточные итоги, при выводе таблицы делаем
вот что:
Группы.ВыбратьСтроки();
Пока Группы.ПолучитьСтроку()=1 Цикл //Группировка по группам
//Определили остаток по группе
ГруппаНачОст=ТЗПоиск(Данные,"НачОст",1,"Группа",Группы.Группа);
...
Данные.ВыбратьСтроки();
Пока Данные.ПолучитьСтроку()=1 Цикл
//В данной группе по товарам
Если Данные.Группа<>Группы.Группа Тогда
Продолжить;
КонецЕсли;
//Определили остаток по группе и товару
НачОст=ТЗПоиск(Данные,
"НачОст",1,
"Группа",
Группы.Группа,
"Товар",
Данные.Товар);
...
КонецЦикла
КонецЦикла;
ИтогоНачОст=ТЗПоиск(Данные,"НачОст",1);
Здесь используется функция суммирования/поиска по нескольким колонкам.
1 означает, что нужно просуммировать все НачОст в таблице, где колонки
Группа и Товар равны соответствующим значениям, переданным в функцию через
параметры.
Тот кто хоть раз писал подобные отчеты, поймет, насколько это упрощает жизнь. Тем более, что
итог по группе в обычном случае можно вывести только в конце списка товаров группы, т.к.
сначала надо «пересчитать« все товары группы. Описанная методика позволяет вывести
итог по группе в любом месте.
О скорости и немного философии
Работает это достаточно быстро благодаря индексации таблицы значений. Индексация заключается
в том, что в таблице в пару каждой колонке хранится «индексная» колонка с таким
же именем, но начинающимся на _. В каждой
строчке этой колонки хранится номер той строки таблицы, в которой значение этой колонки
такое же, как и в текущей, или ноль, если таких значений больше нет.
До введения индексации отчет (полный перебор) работал 2 часа, теперь только 5 минут.
А вообще таблица значений в 1С достаточно мощный инструмент. В любой ячейке
можно хранить значение любого типа, в том числе и другую таблицу значений. Вы никогда не
пробовали создавать дерево в 1С? Делайте это в таблицах значений.
|