gifts2017

Фильтруем и выводим "ТаблицуЗначений"

Опубликовал WWWolfy (WWWolfy) в раздел Программирование - Практика программирования

Демонстрация возможностей фильтрации "ТаблицЗначений" без ВК (альтернатива - 1с++ )
Фильтруем и выводим "ТаблицуЗначений"

Эта статья предназначена для демонстрации возможностей фильтрации "ТаблицЗначений" с помощью процедуры "ФильтрТабы" и для облегчения формирования кода при выводе таблиц со сложными группировками, при необходимости получить итоги до перебора строк запроса 1С или просто при фильтрации "ТаблицЗначений" по одной или нескольким колонкам.
Сам код процедуры "ФильтрТабы" (приведен в конце статьи) сложился на основе конкурса по быстрейшему удалению строк ТЗ, и в принципе может быть оптимизирован, хотя и так работает отлично.

Сразу перейду к примеру.
Допустим имеется таблица "тзнДанные" с колонками "Контрагент", "Товар" и "Сумма".
Нам необходимо вывести общую сумму по контрагенту до перебора товаров, а также все товары по контрагенту с суммами.
И так по каждому контрагенту.
 
 
1. Для начала отсортируем нашу таблицу чтобы потом уже не сортировать её, а только фильтровать. Сортировать обязательно надо в той последовательности, которой будем потом фильтровать её, т.е. сначала по "Контрагент", а потом уже как угодно. Если заранее не сортировать, то надо будет раскомментировать строчку в процедуре "ФильтрТабы".
тзнДанные.Сортировать("Контрагент, Товар");
2. Чтобы получить список контрагентов без повторений, мы выгрузим их в отдельный список (таблицу значений) и свернём (сортировать не надо, если вы это сделали в п.1) :

тзнКонтрагенты = СоздатьОбъект("ТаблицаЗначений");
тзнДанные.Выгрузить(тзнКонтрагенты, ,, "Контрагент");
тзнКонтрагенты.Свернуть( "Контрагент",);

3. Теперь пробежимся по всем контрагентам и выведем информацию:
 
тзнКонтрагенты.выбратьСтроки();
Пока тзнКонтрагенты.ПолучитьСтроку()=1 Цикл
  текКонтрагент = тзнКонтрагенты.Контрагент;
  тзнДанныеПоКонтрагенту = ФильтрТабы( тзнДанные, "Контрагент", текКонтрагент);

 4. Здесь мы получили таблицу "тзнДанныеПоКонтрагенту" с суммами и товарами по одному контрагенту (из нашего списка). Можно брать из неё итоговые суммы или перебирать товары, всё в наших руках.

 
всегоПоКонтрагенту = тзнДанныеПоКонтрагенту.Итог("Сумма");
  Сообщить( "Всего по Контрагенту """+текКонтрагент+""" = "+всегоПоКонтрагенту );

  тзнДанныеПоКонтрагенту.выбратьСтроки();
  Пока тзнДанныеПоКонтрагенту.ПолучитьСтроку()=1 Цикл
    текТовар = тзнДанныеПоКонтрагенту.Товар;
    текСуммаТовар = тзнДанныеПоКонтрагенту.Сумма;
    Сообщить( "Сумма по товару """+текТовар+""" = "+текСуммаТовар );
  КонецЦикла; //по товарам

КонецЦикла; //по контрагентам

Собственно вот и всё! Можно выводить таблицы, или просто обрабатывать данные, доработав этот шаблон до необходимого вам функционала.
С вертикальными группировками будет немного по другому, но не намного сложнее. Надо будет заранее сформировать список "столбцов" и выводить всегда все(!) столбцы, даже если они не попались в этой горизонтальной группировке.


Ниже приведёны код примера (без комментариев) и код процедуры "ФильтрТабы".
--------------------------------------------------------------------------------
тзнДанные.Сортировать("Контрагент, Товар");

тзнКонтрагенты = СоздатьОбъект("ТаблицаЗначений");
тзнДанные.Выгрузить(тзнКонтрагенты, ,, "Контрагент");
тзнКонтрагенты.Свернуть( "Контрагент",);

тзнКонтрагенты.выбратьСтроки();
Пока тзнКонтрагенты.ПолучитьСтроку()=1 Цикл
  текКонтрагент = тзнКонтрагенты.Контрагент;
  тзнДанныеПоКонтрагенту = ФильтрТабы( тзнДанные, "Контрагент", текКонтрагент);

  всегоПоКонтрагенту = тзнДанныеПоКонтрагенту.Итог("Сумма");
  Сообщить( "Всего по Контрагенту """+текКонтрагент+""" = "+всегоПоКонтрагенту );

  тзнДанныеПоКонтрагенту.выбратьСтроки();
  Пока тзнДанныеПоКонтрагенту.ПолучитьСтроку()=1 Цикл
    текТовар = тзнДанныеПоКонтрагенту.Товар;
    текСуммаТовар = тзнДанныеПоКонтрагенту.Сумма;
    Сообщить( "Сумма по товару """+текТовар+""" = "+текСуммаТовар );
  КонецЦикла; //по товарам

КонецЦикла; //по контрагентам


См. также

Подписаться Добавить вознаграждение
Комментарии
1. Чум (Чум) 16.06.06 06:38
Тупой перебор строк таблицы значений с проверкой услвия?
А "метод рупора" уже отменили?
2. aldakin 26.06.06 22:52
Тупой не тупой, но всегда удивляли авторы из нашей страны. На пальцах можно перебрать, кто писать умеет, а не пропускает через переводчик хелп программы, а после необработанный текст дает в тираж. Иностранцы пишут проще, въезжаешь сразу. Такие статьи необходимы, своего пользователя найдут. Поддержим автора.
3. Доржи Цыденов (support) 28.06.06 16:21
Подсветка кода с помощью новых тэгов [ code ] [ /code ]
4. AlexQC (alexqc) 29.08.06 10:44
5. Олег (lov-kot) 21.09.06 02:18
Если ТЗ получили из Запрос.Выгрузить - то зачем все это?
Если иначе - все правильно, так и надо. +1
6. kairat primbetov (karat60) 07.09.06 20:58
7. WWWolfy (WWWolfy) 21.09.06 10:56
2(lov-kot) Конечно же это предназначено для сложных случаев, например когда данные надо группировать в разных последовательностях и когда сами данные сливаются из нескольких (иногда разных) источников.
8. Евгений Мартыненков (JohnyDeath) 21.09.06 11:08
Я примерно такими же способами у себя страдал. Представляете, если кол-во строк около 10000? И ещё один столбец или даже два... ;)
Потом я увидел в 1с++ ИндексированнуюТаблицу и сразу же влюбился... Посмотрите только на это: http://www.1cpp.ru/docum/IndexedTable.html
9. WWWolfy (WWWolfy) 21.09.06 13:42
2(JohnyDeath) Представляю. Только читаться отчёт в 10000 строк будет очень плохо как с экрана так и на бумаге, да и сформировать таблицу (теми же БухИтогами) на 10000 строк выйдет в разы дольше чем выводить их вышеописанным способом. Так что проблемы не вижу.
Про "1с++ ИндексированнуюТаблицу" знаю давно, но не каждый клиент будет ставить себе 1с++, а тут всё стандартными средствами.
10. WWWolfy (WWWolfy) 21.09.06 14:17
Кстати попробовал 10000 строк разнородной инфы на примере выше. Получилось примерно 3 сек.
11. Евгений Мартыненков (JohnyDeath) 22.09.06 10:56
2(WWWolfy) Я нисколько не хотел оскорбить тебя или твой метод. Просто предложил альтернативу причём не дурную альтернативу.
А насчет чтения отчета в 10000 строк: дык не факт, что выводиться будут все 10000!
12. WWWolfy (WWWolfy) 22.09.06 14:45
2(JohnyDeath) А я нисколько и не оскорбился. 1С++ сам применял ни раз (в основном прямые запросы) и высказывал респект его авторам. Но ТЗ предпочитаю фильтровать именно своим способом, потому он подходит везде.
13. Гость 25.09.06 12:09
Можно избавиться от цикла поиска конечной строки, если передавать в функцию ФильтрТабы не только ТЕКУЩЕЕ значФильтра, но и СЛЕДУЮЩЕЕ из тзнКонтрагенты..
тогда:

если тзнДанные.НайтиЗначение(значФильтраNEXT,КонСтрока,имяФильтра) = 1 Тогда
КонСтрока = КонСтрока - 1;
конецесли;

надо только предусмотреть ветвление для последней строки тзнКонтрагенты, когда КонСтрока = тзнДанные.КоличествоСтрок();

Думаю, будет работать еще быстрее..
14. WWWolfy (WWWolfy) 25.09.06 15:14
Можно, но думаю не сильно повлияет на скорость. Мерять надо..
И универсальность функции потеряется.
15. Tarasenkov (tarasenkov) 23.01.08 21:40
Можно значительно ускорить работу при многократном вызове фильтра,
заменив создание таблицы на "ЗначениеИзСтрокиВнутр".

[code]Перем ШаблонТз; // Объявим в начале модуля

// Немного изменим код
Функция ФильтрТабы( тзнДанные, имяФильтра, значФильтра)
//тзнДанные.Сортировать(имяФильтра);//не нужно если уже отсортировано
//тзнРезульт =СоздатьОбъект("ТаблицаЗначений");
тзнРезульт = ЗначениеИзСтрокиВнутр(ШаблонТз);
...
// В теле модуля, в самом конце добавим
ШаблонТз = ЗначениеВСтрокуВнутр(СоздатьОбъект("ТаблицаЗначений"));[code/]

Тест на 38000 вызовов - различие в 15 раз.
СоздатьОбъект("ТаблицаЗначений") - 8.5 секунды
ЗначениеИзСтрокиВнутр(ШаблонТз) - 0.5 секунды

За счет такой оптимизации в реальной задаче удалось сократить время в два раза.
16. Tarasenkov (tarasenkov) 23.01.08 21:41
*Фикс
Можно значительно ускорить работу при многократном вызове фильтра,
заменив создание таблицы на "ЗначениеИзСтрокиВнутр".

Код
Перем ШаблонТз; // Объявим в начале модуля

// Немного изменим код 
Функция ФильтрТабы( тзнДанные, имяФильтра, значФильтра)
//тзнДанные.Сортировать(имяФильтра);//не нужно если уже отсортировано
//тзнРезульт =СоздатьОбъект("ТаблицаЗначений");
тзнРезульт = ЗначениеИзСтрокиВнутр(ШаблонТз);
...
// В теле модуля, в самом конце добавим
ШаблонТз = ЗначениеВСтрокуВнутр(СоздатьОбъект("ТаблицаЗначений"));
Показать полностью


Тест на 38000 вызовов - различие в 15 раз.
СоздатьОбъект("ТаблицаЗначений") - 8.5 секунды
ЗначениеИзСтрокиВнутр(ШаблонТз) - 0.5 секунды

За счет такой оптимизации в реальной задаче удалось сократить время в два раза.
17. Евгений Мартыненков (JohnyDeath) 24.01.08 09:57
+(16) у замечательного человека Кости aka 33lab в его конфе "СКАТ-профессионал" подсмотрел такую вот маленькую глобальную процедуру:
Код
Функция _Новый(стрОбъект) Экспорт
   Если стрОбъект="СписокЗначений" Тогда Возврат ЗначениеИзСтрокиВнутр("{""VL"",{}}"); КонецЕсли;
   Если стрОбъект="ТаблицаЗначений" Тогда
      Возврат ЗначениеИзСтрокиВнутр("{""VT"",""1"",{""0"",{{"""",""0"",""0"",""0"","""",""2""}}}}");
   КонецЕсли;
КонецФункции
Показать полностью
tarasenkov; +1 Ответить
18. NoMAx (NoMax) 19.09.08 11:12
Огромное спасибо.
Сэкономило время и позволило выкинуть, лишний мусор
при группировке из тз.
И самое замечательное что все реализуется штатными 1С средствами