Сергей Осипов (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С? Делайте это в таблицах значений. |