[РЕШЕНО] Запрос в цикле, как правильно построить?

Тема в разделе "Конфигурирование на платформе "1С:Предприятие 8"", создана пользователем Катюфка, 27 янв 2017.

  1. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    Здравствуйте.
    8.3 управляемые формы.
    Есть табличная часть Протокол, в которой поля, которые нас интересуют: НомерУчастника, Участник, ВремяЧистое, Круг. Записи ведутся последовательно, то есть у Иванова, например, будет запись времени первого круга, потом второго и так далее.
    Необходимо для каждой категории определить участников, занявших 3 первые места. То есть у участника должно быть максимальное количество кругов и минимальное время.
    В данный момент проверка сделана перебором табличной части. Но такой подход очень зависит от количества участников и количества кругов. Процедура вызывается во время соревнований, поэтому время ее работы критично. На слабеньком ноутбуке это может быть несколько десятых секунды, что вместе с остальными процедурами уже заметно на глаз.
    Пытаюсь сделать то же самое запросом. Но не понимаю, как обойтись без цикла его вызова. И цикл по результату тоже явно нерационален. В общем, программист из меня никудышный. :) Помогите, пожалуйста. Пока время выполнения в цикле по табличной части меньше, чем с запросом.

    Код:
    &НаСервере
    Процедура ОбновитьЛидеров()
        Объект.Лидеры.Очистить();
        Запрос = Новый Запрос;
        Запрос.Текст =
            "ВЫБРАТЬ 
            |    Протокол.НомерУчастника,
            |    Протокол.Участник,
            |    Протокол.Категория,
            |    Протокол.ВремяЧистое,
            |    Протокол.Круг
            |ПОМЕСТИТЬ Таб
            |ИЗ
            |    &Протокол КАК Протокол
            |ГДЕ
            |    Протокол.Категория = &Категория;
            |ВЫБРАТЬ
            |    МАКСИМУМ(Таб.Круг) КАК Круг,
            |    МАКСИМУМ(Таб.ВремяЧистое) КАК ВремяЧистое,
            |    Таб.НомерУчастника,
            |    Таб.Участник,
            |    Таб.Категория
            |ИЗ
            |    Таб КАК Таб
            |СГРУППИРОВАТЬ ПО
            |    Таб.НомерУчастника,
            |    Таб.Участник,
            |    Таб.Категория
            |"
            ;
        Запрос.Параметры.Вставить("Протокол",Объект.Протокол.Выгрузить());
        Для сч = 0 По Объект.Категории.Количество()-1 Цикл
            Запрос.Параметры.Вставить("Категория",Объект.Категории[сч].Категория);
            Результат = Запрос.Выполнить().Выгрузить();
            Результат.Сортировать("Круг Убыв, ВремяЧистое");
            Место = 0;
            Для Каждого Стр Из Результат Цикл
                Место = Место + 1;
                Если Место = 4 Тогда
                    Прервать;
                КонецЕсли;
                НоваяСтр = Объект.Лидеры.Добавить();
                НоваяСтр.Место = Место;
                НоваяСтр.НомерУчастника = Стр.НомерУчастника;
                НоваяСтр.Участник = Стр.Участник;
                НоваяСтр.Категория = Стр.Категория;
                НоваяСтр.Круг = Стр.Круг;
                НоваяСтр.ВремяЧистое = Стр.ВремяЧистое;
            КонецЦикла;
        КонецЦикла;
    КонецПроцедуры
    
  2. nbIpKuH_BaH9I
    Offline

    nbIpKuH_BaH9I Модераторы Команда форума Модератор

    Регистрация:
    16 сен 2009
    Сообщения:
    7.816
    Симпатии:
    498
    Баллы:
    104
    В Вашем случае будет самым оптимальным вариантом написать один запрос с ИТОГАМИ по Категории. Обойти его и сделать тоже самое без запроса в цикле и цикла в цикле.
    Т.е. не нужно делать отбора по категории. Если запись есть в базе, то лучше не используйте Объект.Протокол.Выгрузить(), а сразу берите данные из базы с отбором по ссылке.
  3. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    А как сразу взять, если мы находимся в том же документе, как обратиться в запросе к табличной части? У меня по-другому не получилось.
  4. Yuriy_Alexandrovich
    Offline

    Yuriy_Alexandrovich Профессионал в 1С Команда форума

    Регистрация:
    15 сен 2011
    Сообщения:
    1.349
    Симпатии:
    87
    Баллы:
    54
    Можно и без запроса, оперируя выгрузкой табличной части в таблицу значений
    1. Свертка по "участник"
    2. Создание таблицы значений из табличной части с отбором по "участник", метод "Выгрузить(<Оставляем пустым> , <СтруктураОтбораСтрок>)"
    3. Сортировка полученной таблицы значений методом "Сортировать", так чтобы строки удовлетворяющие требуемым условиям были первыми
    4. Получение первой (индекс 0) записи таблицы после сортировки
    --- Объединение сообщений, 27 янв 2017 ---
    Если документ не записан то не получите,
    нужно будет выгружать текущую таб. часть в таблицу значений и ее уже грузить во временную таблицу через параметр в запросе
  5. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    Примерно так и работает. Но по теории запросом должно быть быстрее. При условии, конечно, что запрос составлен правильно.
  6. 1с-ник
    Offline

    1с-ник Профессионал в 1С Заблокирован

    Регистрация:
    5 окт 2014
    Сообщения:
    998
    Симпатии:
    162
    Баллы:
    104
    Честно не особо понимаю, что требуется получить (либо так описано), и есть ли ссылка на документ или её ещё нет.
  7. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    Открыт документ. В нем несколько табличных частей: Категории, Протокол, Лидеры. Протокол заполняется. Во время его заполнения, идет проверка, не сменились ли лидеры, т.е. после каждой новой строки в Протокол, вызывается процедура ОбновитьЛидеров(). Требуется для каждой из категорий получить 3 лучших результата. Категории, Круги - в Протоколе вперемешку, не упорядочены.
  8. 1с-ник
    Offline

    1с-ник Профессионал в 1С Заблокирован

    Регистрация:
    5 окт 2014
    Сообщения:
    998
    Симпатии:
    162
    Баллы:
    104
    Могу сказать, что решение такой задачи одним лишь запросом не приведет к её оптимизации.
  9. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    Но ведь как-то можно упорядочить запросом по Категориям? В этих категориях упорядочить по убыванию Круг, а потом по возрастанию ВремяЧистое? Потом отобрать только первые 3. В теории я это себе представляю, а на практике ничего не получается.
  10. nbIpKuH_BaH9I
    Offline

    nbIpKuH_BaH9I Модераторы Команда форума Модератор

    Регистрация:
    16 сен 2009
    Сообщения:
    7.816
    Симпатии:
    498
    Баллы:
    104
    Чем не устроил мой вариант?


    Отправлено с моего iPhone используя Tapatalk
  11. Gfdtk
    Offline

    Gfdtk Опытный в 1С

    Регистрация:
    28 апр 2008
    Сообщения:
    168
    Симпатии:
    0
    Баллы:
    26
    Вместо
    Код:
            |СГРУППИРОВАТЬ ПО
            |    Таб.НомерУчастника,
            |    Таб.Участник,
            |    Таб.Категория
    
    Используйте
    Код:
            |ИТОГИ ПО
            |    Таб.Категория, //если отбор по конкретной категории, то, имхо, категорию нужно вверх вынести или вообще убрать
            |    Таб.НомерУчастника,
            |    Таб.Участник
    
    
    Перебор результата будет примерно таким
    Код:
    РезультатЗапроса = Запрос.Выполнить();
    ВыборкаКатегория = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "Категория");
    Пока ВыборкаКатегория.Следующий() Цикл
        ВыборкаНомерУчастника = ВыборкаКатегория.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "НомерУчастника");
        Пока ВыборкаНомерУчастника.Следующий() Цикл
             ВыборкаУчастник = ВыборкаНомерУчастника.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, Участник);
             Пока ВыборкаУчастник.Следующий() Цикл
                  Выборка = ВыборкаУчастник.Выбрать(); //Нужен чтобы выбрать поля по котором нет итогов, иначе они будут пустые
                  Пока Выборка.Следующий() Цикл
                      //код обработки
                  КонецЦикла;
             КонецЦикла;
        КонецЦикла;
    КонецЦикла;
    
    Я бы еще уменьшил количество группировок итогов, Например убрал бы НомерУчастника из итогов
  12. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    Мне бы сам код посмотреть. Вот у Gfdtk - это оно?
    Спасибо. Попробую.
  13. Yuriy_Alexandrovich
    Offline

    Yuriy_Alexandrovich Профессионал в 1С Команда форума

    Регистрация:
    15 сен 2011
    Сообщения:
    1.349
    Симпатии:
    87
    Баллы:
    54
    Код:
    &НаСервере
    Процедура ОбновитьЛидеров()
       
       Объект.Лидеры.Очистить();
       Запрос = Новый Запрос;
       Запрос.Текст = "
       |//* Исходные данные
       |ВЫБРАТЬ
       |   Протокол.НомерУчастника,
       |   Протокол.Участник,
       |   Протокол.Категория,
       |   Протокол.ВремяЧистое,
       |   Протокол.Круг
       |ПОМЕСТИТЬ ВТ_ИсходныеДанные
       |ИЗ
       |   &Протокол КАК Протокол
       |;
       |//* Результаты каждого участника, срез по кругу, аналогично срезу последних :)
       |ВЫБРАТЬ
       |   РезультатПоКругу.НомерУчастника     КАК НомерУчастника,
       |   РезультатПоКругу.Участник       КАК Участник,
       |   РезультатПоКругу.Категория       КАК Категория,
       |   РезультатПоКругу.Круг         КАК Круг,
       |   ИсходныеДанныеДетали.ВремяЧистое   КАК ВремяЧистое
       |ИЗ
       |(   ВЫБРАТЬ
       |     ИсходныеДанные.НомерУчастника   КАК НомерУчастника,
       |     ИсходныеДанные.Участник       КАК Участник,
       |     ИсходныеДанные.Категория     КАК Категория,
       |     МАКСИМУМ(ИсходныеДанные.Круг)   КАК Круг
       |   ИЗ
       |     ВТ_ИсходныеДанные КАК ИсходныеДанные
       |   СГРУППИРОВАТЬ ПО
       |     ИсходныеДанные.НомерУчастника,
       |     ИсходныеДанные.Участник,
       |     ИсходныеДанные.Категория
       |) КАК РезультатПоКругу
       |   ЛЕВОЕ СОЕДИНЕНИЕ
       |     ВТ_ИсходныеДанные КАК ИсходныеДанныеДетали
       |   ПО ИсходныеДанныеДетали.Категория = РезультатПоКругу.Категория
       |     И ИсходныеДанныеДетали.Участник = РезультатПоКругу.Участник
       |     И ИсходныеДанныеДетали.НомерУчастника = РезультатПоКругу.НомерУчастника
       |     И ИсходныеДанныеДетали.Круг = РезультатПоКругу.Круг
       |
       |УПОРЯДОЧИТЬ ПО
       |   Категория,
       |   Круг УБЫВ,
       |   ВремяЧистое ВОЗР
       |
       |ИТОГИ
       |   МАКСИМУМ(Круг),
       |   МИНИМУМ(ВремяЧистое)
       |ПО
       |   Категория
       |";
       
       Запрос.Параметры.Вставить("Протокол", Объект.Протокол.Выгрузить());
       
       РезультатКакДерево = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
       // обходим дерево
       Для Каждого СтрокаВерхнегоУровня Из РезультатКакДерево.Строки Цикл // строки по категориям
         
         // берем первые 3-записи, сортировка в запросе крайне важна !
         Для Место = 1 По 3 Цикл
           
           // подчиненные строки, строки нижнего уровня, детали
           СтрокаДанныхРезультата = СтрокаВерхнегоУровня.Строки.Получить(Место - 1);
           
           НоваяСтр = Объект.Лидеры.Добавить();
           // поскольку наименование полей совпадает, то можем сделать так
           ЗаполнитьЗначенияСвойств(НоваяСтр, СтрокаДанныхРезультата);
           НоваяСтр.Место = Место;
           
         КонецЦикла;
         
       КонецЦикла;
    
    КонецПроцедуры
    
  14. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    Yuriy_Alexandrovich, Спасибо!

    PS В результатах вашего запроса НомерУчастника и Участник почему-то Null, а мне это важно. И ошибка по индексам. Но есть над чем подумать. Все равно очень пригодится.
    Последнее редактирование: 27 янв 2017
  15. shurikvz
    Offline

    shurikvz Модераторы Команда форума Модератор

    Регистрация:
    1 окт 2009
    Сообщения:
    8.543
    Симпатии:
    343
    Баллы:
    104
    Катюфка
    1) каково среднее количество строк в обрабатываемой ТЧ?
    2) база я так понимаю файловая?
    3) в варианте из #1 можно в запросе просто ТОП 3 сделать
    4) отладчик (замер производительности) включить, расшифровка строк по времени выполнения какова?
  16. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    1) Раньше было порядка 180, задержка была незаметна, сейчас может быть и 500.
    2) Файловая.
    3) То есть все циклы остаются?
    4) Попробуем. Дело в том, что на моем компьютере процедуры работают быстро. А вот на реальном ноутбуке во время соревнований стали жаловаться на заметную задержку. Обновить технику, к сожалению, пока не представляется возможным.
  17. nbIpKuH_BaH9I
    Offline

    nbIpKuH_BaH9I Модераторы Команда форума Модератор

    Регистрация:
    16 сен 2009
    Сообщения:
    7.816
    Симпатии:
    498
    Баллы:
    104
    В этом сообщении Вам код привели. Что не получается?
  18. shurikvz
    Offline

    shurikvz Модераторы Команда форума Модератор

    Регистрация:
    1 окт 2009
    Сообщения:
    8.543
    Симпатии:
    343
    Баллы:
    104
    Это было "к слову". Кроме того в том коде сортировать бы сразу в запросе надо. И в таблицу зачем выгружать, это лишние действия (время).

    Честно говоря - я не совсем понимаю к чему здесь вообще запрос использовать. Выборки данных из таблиц не производится.
    Сказать что однозначно прироста производительности при переходе на запрос не будет - не скажу, но не знаю, стоит ли игра свеч.


    Можно увидеть код, который сейчас получает данные эти (который без запроса)?
    Возможно просто он не оптимально написан.
    Тут по сути ведь вообще ничего не надо. Сортировка табличной части и один проход циклом по ней.

    Как-то так (прям здесь писал, не проверял)
    Код:
    Объект.Лидеры.Очистить();
    ВременнаяТаблица = Объект.Протокол.Выгрузить();
    ВременнаяТаблица.Сортировать("Категория, Круг Убыв, ВремяЧистое");
    
    ПредыдущаяКатегория = Неопределено; Место = 0;
    Для каждого СтрокаТаблицы Из ВременнаяТаблица Цикл
    
    Если ПредыдущаяКатегория <> СтрокаТаблицы.Категория Тогда
    ПредыдущаяКатегория = СтрокаТаблицы.Категория;
    Место = 0;
    КонецЕсли;
    
    Место = Место + 1;
    Если Место > 3 Тогда Продолжить; КонецЕсли;
    
    НоваяСтр = Объект.Лидеры.Добавить();
    НоваяСтр.Место = Место;
    НоваяСтр.НомерУчастника = СтрокаТаблицы.НомерУчастника;
    НоваяСтр.Участник = СтрокаТаблицы.Участник;
    НоваяСтр.Категория = СтрокаТаблицы.Категория;
    НоваяСтр.Круг = СтрокаТаблицы.Круг;
    НоваяСтр.ВремяЧистое = СтрокаТаблицы.ВремяЧистое;
    
    КонецЦикла;
    
    1с-ник нравится это.
  19. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    Да, вполне возможно, что лучше оптимизировать уже имеющийся. Тут два прохода, но это нужно, чтобы убрать предыдущие круги у каждого участника. У Вас, кстати, это не учтено. Получится, что в первой тройке окажется 2 Иванова, вместо Иванов, Петров, Сидоров.
    Пока работает так:

    Код:
    &НаСервере
    Процедура ОбновитьЛидеров()
        Объект.Лидеры.Очистить();
        Объект.Лидеры.Загрузить(Объект.Протокол.Выгрузить());
        Объект.Лидеры.Сортировать("НомерУчастника Возр,Круг Убыв,ВремяЧистое Убыв");
        Номер = Неопределено;
        Для Каждого Строка Из Объект.Лидеры Цикл
            Если (Строка.НомерУчастника = Номер)
            Или (Строка.НомерУчастника = 0)   
            Тогда
                Строка.СтрУдалить = 1;
            Иначе
                Номер = Строка.НомерУчастника;
            КонецЕсли;
        КонецЦикла;
        УдалитьПомеченные();
        Объект.Лидеры.Сортировать("Категория Убыв,Круг Убыв,ВремяЧистое Возр");
        Категория = "";
        Место = 0;
        Для Каждого Строка Из Объект.Лидеры Цикл
            Место = Место + 1;
            Если Строка.Категория = Категория Тогда
                Если Место > 3 Тогда
                    Строка.СтрУдалить = 1;
                КонецЕсли;
            Иначе
                Категория = Строка.Категория;
                Место = 1;
            КонецЕсли;
            Строка.Место = Место;
        КонецЦикла;
        УдалитьПомеченные();
    КонецПроцедуры
    
    &НаСервере
    Процедура УдалитьПомеченные()
        СтруктураОтбора = Новый Структура("СтрУдалить", 1);
        МассивУдаляемыхСтрок = Объект.Лидеры.НайтиСтроки(СтруктураОтбора);
        Для Каждого ЭлементМассива Из МассивУдаляемыхСтрок Цикл
          Объект.Лидеры.Удалить(ЭлементМассива);
      КонецЦикла;   
    КонецПроцедуры
    
    Код явно избыточный и зависимый от количества строк.
  20. TopicStarter Overlay
    Катюфка
    Offline

    Катюфка Профессионал в 1С

    Регистрация:
    3 июн 2013
    Сообщения:
    518
    Симпатии:
    83
    Баллы:
    54
    Я пытаюсь и с ним разобраться, но пока сплошные ошибки. Не умею я работать с запросами. Можете привести полный код процедуры?
    --- Объединение сообщений, 29 янв 2017 ---
    Если это поможет, есть еще таблица Регистрация, в которой есть НомерУчастника, Участник, Категория. Там только участники. Хорошо бы взять эту таблицу и добавить к ней максимальный круг со временем каждого участника.

Поделиться этой страницей