Оглавление:
Про циклы в одну строку
Про быстрые массивы
Про длинные строки
Про типы данных. Динамическая настройка колонок табличного поля
Про типы данных. Расчет сумм по выделенным ячейкам табличного поля
Про хитрые запросы. Не стандартное использование итогов
Про хитрые запросы. Тэта-соединения
Про дерево значений
Если вы это всё уже знаете, то, пожалуйста, не кидайте в автора грязью. Танки грязи не боятся.

Но всё-таки я надеюсь вас чем-нибудь удивить.
Про циклы в одну строку
Первым номером нашей программы выступает код добавления (копирования) строк из одной таблицы значений в другую ТЗ или из одной табличной части в другую ТЧ. Ну, и т.д. Т.е. строки из обоих источников объединяются, совпадающие по имени колонки заполняются.
Для каждого СтрокаТЗ Из Таблица1 Цикл ЗаполнитьЗначенияСвойств(Таблица2.Добавить(), СтрокаТЗ) КонецЦикла;
Соответственно код добавления элементов из массива в массив будет такой.
Для каждого ЭлементМассива Из Массив1 Цикл Массив2.Добавить(ЭлементМассива) КонецЦикла;
Всего одна команда в цикле - что может быть проще?
Наверное, вы уже обратили внимание на запись циклов в одну строку. Так программный код выполняется несколько быстрее. Всё дело в том, что в строках есть дополнительные данные, которые используются, например, при отладке. Меньше строк - меньше дополнительных данных. Это прекрасно видно, если смотреть замер производительности в режиме отладки - цикл в строке Для из 10000 повторов записанный обычной структурой будет выполнен 10001 раз, а записанный в 1 строку будет выполнен 1 раз. Разумеется, что при этом результат выполнения программного кода будет абсолютно одинаковый, а вот время выполнения будет разное.
Также "тонким" моментом будет использование команды
ОбработкаПрерыванияПользователя()
. Если в цикле, записанном в 1 строку, используется данная команда, то вероятность срабатывания прерывания выполнения программы по Ctrl+Break становится не четким. Ну, может за исключением тех случаев, когда в цикле кроме этой других команд нет. 
Про быстрые массивы
Теперь посмотрим что-нибудь простенькое. Например, разбор строки с разделителями в массив элементов. В типовых конфигурациях есть функция
РазложитьСтрокуВМассивПодстрок()
. Предлагаю рассмотреть альтернативное решение. Вот код для строковых значений.// РазбираемаяСтрока - строка исходного текста
// Разделитель - разделитель элементов строки
ЗначениеИзСтрокиВнутр("{""#"",51e7a0d2-530b-11d4-b98a-008048da3034,{0,{""S"",""" + СтрЗаменить(СтрЗаменить(РазбираемаяСтрока, """", """"""), Разделитель, """},{""S"",""") + """}}}");
Вот код для числовых значений.
ЗначениеИзСтрокиВнутр("{""#"",51e7a0d2-530b-11d4-b98a-008048da3034,{0,{""N""," + СтрЗаменить(РазбираемаяСтрока, Разделитель, "},{""N"",") + "}}}");
Так, так, так. А вот довольно шустренький программный код для сворачивания элементов массива. Результат записывается в НовыйМассив.
НовыйМассив = Новый Массив; Соответствие = Новый Соответствие;
Для каждого ЭлементМассива Из Массив Цикл Соответствие.Вставить(ЭлементМассива) КонецЦикла;
Для каждого КлючИЗначение Из Соответствие Цикл НовыйМассив.Добавить(КлючИЗначение.Ключ) КонецЦикла;
Если вдруг понадобится отсортировать элементы в массиве, то быстро это можно сделать так:
СписокЗначений = Новый СписокЗначений;
СписокЗначений.ЗагрузитьЗначения(Массив);
СписокЗначений.СортироватьПоЗначению();
Массив = СписокЗначений.ВыгрузитьЗначения();
В некоторых случаях больше подойдет сортировка не по значению, а по представлению.
Про длинные строки
Получить строку из пробелов нужной длины можно собирая пробелы в цикле. Но можно использовать следующую конструкцию:
// Получаем строку пробелов длиной 10000 символов
СтрокаПробелов = СтрЗаменить(Формат(0, "ЧЦ=100; ЧН=; ЧВН=; ЧГ="), "0",
СтрЗаменить(Формат(0, "ЧЦ=100; ЧН=; ЧВН=; ЧГ="), "0", " "));
Использование трехуровневой буферизации может дать ускорение в десятки и даже тысячи раз. Код может выглядеть так:
Данные = "";
Результат = "";
Результат1 = "";
Результат2 = "";
// Получаем Данные и записываем в Результат
Пока ПолучитьДанные(Данные) Цикл
Результат2 = Результат2 + Данные;
Если СтрДлина(Результат2) > 100 Тогда
Результат1 = Результат1 + Результат2;
Результат2 = "";
Если СтрДлина(Результат1) > 10000 Тогда
Результат = Результат + Результат1;
Результат1 = "";
КонецЕсли;
КонецЕсли;
КонецЦикла;
Результат = Результат + Результат1 + Результат2;
Конечно, и здесь цикл можно записать в одну строку, но это не сделано только для сохранения читаемости программного кода в статье.
Про типы данных. Динамическая настройка колонок табличного поля
Иногда бывает нужно проверить тип данных какого-либо ревизита. Для типов данных без квалификаторов проверку типа данных легко сделать с помощью
ОписаниеТипов
. А вот для типов данных Число, Строка и Дата можно использовать другую конструкцию с использованием массива типов. Вот пример для динамической настройки табличного поля. В примере использованы оба способа сравнения типов.ТипЧисло = Тип("Число");
ОписаниеТипаБулево = Новый ОписаниеТипов("Булево");
мВыводитьПодвал = Ложь;
ТабличноеПоле.СоздатьКолонки();
КолонкиИсточника = ТабличноеПоле.Значение.Колонки;
// Отобразим булево как галочки, а отрицательные числа красным
Для каждого КолонкаТЧ Из ТабличноеПоле.Колонки Цикл
ИмяКолонкиТЧ = КолонкаТЧ.Имя;
ТипЗначенияКолонки = КолонкиИсточника[ИмяКолонкиТЧ].ТипЗначения;
Если ТипЗначенияКолонки = ОписаниеТипаБулево Тогда
КолонкаТЧ.ДанныеФлажка = КолонкаТЧ.Данные;
КолонкаТЧ.Данные = "";
ИначеЕсли ИмяКолонкиТЧ <> "НомерСтроки" Тогда
МассивТипов = ТипЗначенияКолонки.Типы();
Если МассивТипов.Количество() = 1 и МассивТипов[0] = ТипЧисло Тогда
КолонкаТЧ.ВыделятьОтрицательные = Истина;
КолонкаТЧ.ОтображатьИтогиВПодвале = Истина;
КолонкаТЧ.ГоризонтальноеПоложениеВПодвале = ГоризонтальноеПоложение.Право;
мВыводитьПодвал = Истина;
КонецЕсли;
КонецЕсли;
КонецЦикла;
ТабличноеПоле.Подвал = мВыводитьПодвал;
К вопросу "Почему не воспользоваться методом СодержитТип?". Метод СодержитТип не дает однозначного ответа на вопрос "Какой тип реквизита?". Он только подтверждает, что реквизит может быть, например, числом. А если реквизит составного типа?
Про типы данных. Расчет сумм по выделенным ячейкам табличного поля
В некоторых типовых конфигурациях 1С и публикациях в интернете используется очень удобный сервис "расчет сумм по выделенным ячейкам табличного поля". К сожалению все попавшие на глаза варианты реализации работают "неверно" при подсчете сумм по накладывающимся выделенным областям. При подсчете суммы из персекающихся ячеек учитываются несколько раз.
Предлагаю простенькую функцию, в которой данный "эффект" устранен.
Функция РасчетСуммыПоЯчейкам(ТабличноеПоле) Экспорт
Сумма = 0;
КоличествоСумм = 0;
СоответствиеЯчеек = Новый Соответствие;
ОписаниеТипов = Новый ОписаниеТипов("Число");
Для Каждого ВыделеннаяОбласть Из ТабличноеПоле.ВыделенныеОбласти Цикл
Для Индекс1 = ВыделеннаяОбласть.Лево По ВыделеннаяОбласть.Право Цикл
Для Индекс2 = ВыделеннаяОбласть.Верх По ВыделеннаяОбласть.Низ Цикл
Область = ТабличноеПоле.Область(Индекс2, Индекс1, Индекс2, Индекс1);
Значение = ОписаниеТипов.ПривестиЗначение(Область.Текст);
СоответствиеЯчеек.Вставить(Область.Имя, Значение);
КонецЦикла;
КонецЦикла;
КонецЦикла;
Для каждого Ячейка Из СоответствиеЯчеек Цикл
Значение = Ячейка.Значение;
Если Значение <> 0 Тогда
Сумма = Сумма + Значение;
КоличествоСумм = КоличествоСумм + 1;
КонецЕсли;
КонецЦикла;
КоличествоВыделено = СоответствиеЯчеек.Количество();
Если КоличествоВыделено > 1 Тогда
ТекстИтогов = " Выделено ячеек: " + КоличествоВыделено
+ " Просуммировано ячеек: " + КоличествоСумм
+ " Итого по выделенным ячейкам: " + Сумма;
Иначе
ТекстИтогов = "";
КонецЕсли;
Возврат ТекстИтогов;
КонецФункции // РасчетСуммыПоЯчейкам()
Использование метода
ПривестиЗначение
вместо встречающегося в типовых конфигурациях преобразования через Попытка
, дает в среднем ускорение на преобразовании типов в 4 раза. А использование отложенного вызова функции расчета суммы всего лишь на 0.2 секунды с легкостью решает проблему с задержками при выделении пользователем большого количества ячеек.Про хитрые запросы. Не стандартное использование итогов
А что сейчас? А сейчас запросы. Не все конечно, а несколько каких-нибудь простеньких и интересненьких. Например, построение запросом двухуровневого дерева структуры задолженности для формы элемента справочника Контрагенты. Исходим из того, что чем меньше команд, тем быстрее работает. Ну, обычно быстрее. Ведь 1С это все-таки интерпретатор.
Итак, строим запросом дерево по структуре задолженности как на картинке ниже, смотрим закладку Задолженность. На закладке добавлено табличное поле с деревом значений Задолженность.

Получаем в детальных строках договоры контрагентов, а в итоговых строках организации. Приведенный ниже в качестве примера программный код будет работать в УТ и УПП. Для остальных конфигураций код запроса нужно немного подправить.
Запрос = Новый Запрос(
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| ДоговорыКонтрагентов.Организация КАК Организация,
| ДоговорыКонтрагентов.Ссылка КАК СтруктураДоговоров,
| -ЕСТЬNULL(ВзаиморасчетыОстатки.СуммаВзаиморасчетовОстаток, 0) КАК Сумма
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(
| , Контрагент = &Контрагент) КАК ВзаиморасчетыОстатки
| ПО ДоговорыКонтрагентов.Ссылка = ВзаиморасчетыОстатки.ДоговорКонтрагента
|ГДЕ
| ДоговорыКонтрагентов.Владелец = &Контрагент
|
|УПОРЯДОЧИТЬ ПО
| Организация,
| СтруктураДоговоров
|ИТОГИ
| Организация КАК СтруктураДоговоров,
| СУММА(Сумма)
|ПО
| Организация");
Запрос.УстановитьПараметр("Контрагент", Ссылка);
Задолженность = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
Получилось простенько и симпатично.
В итогах Организация используется без агрегатной функции - это возможно потому, что по Организации выполняется группировка.
Про хитрые запросы. Тэта-соединения
Кажется тема с запросами получилась интересной. Тогда ещё один примерчик, который можно подержать в руках - изменение курсов валют с группировкой по месяцам.

Запрос = Новый Запрос(
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| КурсыВалют.Период КАК Период1,
| МАКСИМУМ(ЕСТЬNULL(КурсыВалют1.Период, КурсыВалют.Период)) КАК Период2
|ПОМЕСТИТЬ Соединения
|ИЗ
| РегистрСведений.КурсыВалют КАК КурсыВалют
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют1
| ПО КурсыВалют.Валюта = КурсыВалют1.Валюта
| И КурсыВалют.Период > КурсыВалют1.Период
| И (КурсыВалют1.Валюта = &Валюта)
|ГДЕ
| КурсыВалют.Валюта = &Валюта
|
|СГРУППИРОВАТЬ ПО
| КурсыВалют.Период
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗРЕШЕННЫЕ
| КурсыВалют.Период КАК Период,
| НАЧАЛОПЕРИОДА(КурсыВалют.Период, МЕСЯЦ) КАК ПериодДляИтогов,
| КурсыВалют.Курс,
| КурсыВалют.Курс - КурсыВалют1.Курс КАК ИзменениеКурса
|ИЗ
| РегистрСведений.КурсыВалют КАК КурсыВалют
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Соединения КАК Соединения
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют1
| ПО Соединения.Период2 = КурсыВалют1.Период
| И (КурсыВалют1.Валюта = &Валюта)
| ПО КурсыВалют.Период = Соединения.Период1
| И (КурсыВалют.Валюта = &Валюта)
|ИТОГИ
| ПериодДляИтогов КАК Период,
| СУММА(ИзменениеКурса)
|ПО
| ПериодДляИтогов");
Запрос.УстановитьПараметр("Валюта", Валюта);
КурсыВалют = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
Аналогично можно построить и другие запросы, в которых отслеживается изменение значений показателей, например, изменение цен.
Про дерево значений
Краткое описание исходных условий для этого варианта. Работаем с деревом значений имеющим 2 уровня. Для первоначального отображения формируется только первый уровень строк дерева и добавляется по одной пустой строке на втором уровне для отображения плюсиков возле строк. Первый уровень свернут. Далее, при разворачивании пользователем строки первого уровня, пустая строка второго уровня заменяется на необходимый набор данных. Необходимый набор данных получаем, например, запросом в виде таблицы значений.
Что мы получаем при использовании такого варианта работы с деревом? Быстрое первоначальное отображение дерева для пользователя. Что ещё? Немного больше кода при работе с деревом.
Вот тут мы и попляшем немного вокруг вопроса: "Как поместить необходимый набор данных в строки второго уровня?" Есть варианты.
Наиболее очевидный вариант описан в начале этой статьи в части "Про циклы в одну строку". Но он не единственный. Предлагаю рассмотреть ещё один кусочек кода, который намного производительнее при большом количестве строк в необходимом наборе данных.
Процедура ДеревоЗначенийПередРазворачиванием(Элемент, СтрокаДереваЗначений, Отказ)
Если СтрокаДереваЗначений.УровеньРазвернут Тогда
Возврат;
КонецЕсли;
СтрокиГруппировки = СтрокаДереваЗначений.Строки;
СтрокиГруппировки.Очистить();
ТаблицаЗначений = ПолучитьНаборНеобходимыхДанных(СтрокаДереваЗначений);
Если ТаблицаЗначений.Колонки.Количество() * 2 > ТаблицаЗначений.Количество() Тогда
Для каждого СтрокаТаблицыЗначений Из ТаблицаЗначений Цикл
ЗаполнитьЗначенияСвойств(СтрокиГруппировки.Добавить(), СтрокаТаблицыЗначений);
КонецЦикла;
Иначе
Для Индекс = 1 По ТаблицаЗначений.Количество() Цикл
СтрокиГруппировки.Добавить();
КонецЦикла;
Для каждого Колонка Из ТаблицаЗначений.Колонки Цикл
СтрокиГруппировки.ЗагрузитьКолонку(ТаблицаЗначений.ВыгрузитьКолонку(Колонка.Имя), Колонка.Имя);
КонецЦикла;
КонецЕсли;
СтрокаДереваЗначений.УровеньРазвернут = Истина;
КонецПроцедуры // ДеревоЗначенийПередРазворачиванием()
Циклы также можно оформить в одну строку, но для сохранения читаемости кода этого не сделано. На контрольном примере предложенный вариант заполнения был производительнее в 4 раза. Конечно же всё зависит от исходных данных и на других данных результаты могут быть другие, но о том, что 1С всё таки интерпретатор не стоит забывать. Правило "Чем меньше команд, тем быстрее работает программа" остается актуальным и по сей день.
Оригинал полной статьи с готовыми файлами-примерами можно найти необходимо зарегистрироваться для просмотра ссылки