Очень медленный цикл itab с вложенными SELECT

Я объявил внутреннюю таблицу, например:

DATA: wa_collectoraction TYPE zcollectoraction,
  it_collectoraction LIKE STANDARD TABLE OF zcollectoraction.

Затем я заполняю таблицу:

SELECT bukrs kunnr yearmonth MAX( dat ) AS dat
FROM zcollectoraction
  INTO CORRESPONDING FIELDS OF TABLE it_collectoraction
WHERE bukrs IN so_bukrs AND
      kunnr IN so_kunnr AND
      dat   IN so_date
GROUP BY bukrs kunnr yearmonth.

и, наконец, у меня есть следующий цикл

LOOP AT it_collectoraction INTO wa_collectoraction.
PERFORM progress_bar USING 'Retrieving data...'(035)
                           sy-tabix
                           i_tab_lines.
"Get the MAX TIME for all lines in order to cover the case we have more than 1 line."
SELECT SINGLE * FROM zcollectoraction
    INTO CORRESPONDING FIELDS OF wa_collectoraction
  WHERE bukrs = wa_collectoraction-bukrs AND
        kunnr = wa_collectoraction-kunnr AND
        dat   = wa_collectoraction-dat   AND
        time  = ( SELECT MAX( time ) AS time
                    FROM zcollectoraction
                    WHERE bukrs = wa_collectoraction-bukrs AND
                          kunnr = wa_collectoraction-kunnr AND
                          dat   = wa_collectoraction-dat ).

MODIFY it_collectoraction FROM wa_collectoraction.
ENDLOOP.

Этот цикл выполняется 5 минут для 3000 записей. Может кто подскажет, что сделать, чтобы было быстрее?

заранее спасибо


person ekekakos    schedule 03.10.2017    source источник
comment
На какой версии NetWeaver вы работаете?   -  person    schedule 03.10.2017
comment
какой тип базы данных вы используете?   -  person András    schedule 03.10.2017
comment
Версия компонента: SAP ECC 6.0. СИСТЕМА БАЗЫ ДАННЫХ: Версии SAP DB6: 700, 710, 701, 702, 711, 720, 730   -  person ekekakos    schedule 03.10.2017
comment
Сама петля в порядке. Но какую обиду вы держите на свою базу данных, что так жестоко пытаетесь ее убить? ;-)   -  person vwegert    schedule 03.10.2017
comment
vwegert, что я пытаюсь сделать, я объясню ниже. Ну, позвольте мне сказать вам. В выборе я беру определенные поля максимальной даты месяца. Затем я выбираю все поля записи с максимальным временем, потому что есть большая вероятность иметь более 1 записи в качестве максимальной даты. Вы можете увидеть пример ниже. Спасибо   -  person ekekakos    schedule 03.10.2017


Ответы (4)


Лучшим инструментом для анализа производительности отдельного отчета является ST12, поэтому, если у вас есть возможность, отследите его.
Мы должны догадаться, что без трассировки самая большая проблема заключается либо в SELECT с подвыбором, либо в MODIFY.

1) SELECT в LOOP всегда медленные

Здесь вы фактически делаете два для каждой строки в it_collectoraction.

Попробуйте уменьшить количество SELECT

В зависимости от количества строк с одним и тем же dat может быть намного быстрее заменить SELECT в LOOP на SELECT с FOR ALL ENTRIES из zcollectoraction вне LOOP и найти MAX (время) на стороне ABAP.

Покрытие индекса

Кажется, все в порядке.

2) MODIFY работает медленно на СТАНДАРТНЫХ таблицах.

Вы должны просеять всю таблицу, чтобы найти соответствующую строку. Если вы определите it_collectoraction как SORTED, это будет намного быстрее. Если вы используете символ поля в LOOP, его можно вообще избежать.

Кодирование

Замените свой LOOP на это:

TYPES: BEGIN OF tty_coll_act,
        bukrs TYPE burks,
        kunnr TYPE kunnr,
        dat   TYPE dat,
        time  TYPE time,
      END OF tty_coll_act.

DATA: lt_coll_act TYPE TABLE OF tty_coll_act,
      ls_coll_act LIKE LINE OF lt_coll_act.

FIELD-SYMBOLS: <fs_collectoraction> LIKE LINE OF it_collectoraction.

SELECT bukrs kunnr dat time
    INTO TABLE lt_coll_act
    FROM zcollectoraction
    FOR ALL ENTRIES IN it_collectoraction
    WHERE bukrs = wa_collectoraction-bukrs AND
          kunnr = wa_collectoraction-kunnr AND
          dat   = wa_collectoraction-dat.

SORT lt_coll_act BY bukrs kunnr dat time DESCENDING.

LOOP AT it_collectoraction ASSIGNING <fs_collectoraction>.
" the READ TABLE finds the first matching row,
" it will be MAX(TIME) as TIME is sorted descending       
  READ TABLE lt_coll_act INTO ls_coll_act
      WITH KEY  bukrs = <fs_collectoraction>-bukrs
                kunnr = <fs_collectoraction>-kunnr
                dat   = <fs_collectoraction>-dat BINARY SEARCH.
  <fs_collectoraction> = ls_coll_act.
ENDLOOP.
person András    schedule 03.10.2017
comment
Это ключевые поля: - person ekekakos; 03.10.2017
comment
Это ключевые поля: MANDT-BUKRS-KUNNR-YEARMONTH-DAT-TIME Hera пример 200-1000-620-201708-20170819-20170819121212.356/////////////////////////// ///// 200-1000-620-201708-20170819-20170819121211.356/////////////////////// 200-1000-620-201708-20170810-20170810121200.356 /////////////////////// 200-1000-620-201708-20170810-20170810121100.356////////////////// /////// Итак, я хочу отобразить МАКСИМАЛЬНУЮ дату месяца, и если существует более 1 записи в максимальной дате, я хочу получить МАКСИМАЛЬНОЕ время. - person ekekakos; 03.10.2017
comment
Даже этот код слишком медленный. ‹code› ВЫБЕРИТЕ * ИЗ zcollectoraction КАК a В СООТВЕТСТВУЮЩИЕ ПОЛЯ ТАБЛИЦЫ it_collectoraction ГДЕ bukrs IN so_bukrs AND kunnr IN so_kunnr AND dat IN so_date AND time IN ( SELECT MAX( time ) AS time FROM zcollectoraction AS b ГДЕ b~bukrs EQ a~bukrs AND b~kunnr EQ a~kunnr AND b~dat IN so_date AND b~yearmonth EQ a~yearmonth ).‹/code› - person ekekakos; 03.10.2017
comment
Андрас, так как я не эксперт, как я это реализую? - person ekekakos; 03.10.2017
comment
Андрас, к сожалению, первый выбор занимает 1 минуту для 950 записей. Это намного быстрее, чем мой код 7000 рек против 9500 рек за 10 минут. Как вы думаете, исходя из вашего опыта, это хорошее время? Я не верю. Спасибо. - person ekekakos; 04.10.2017
comment
@ekekakos, если вы имеете в виду выбор с max (dat), 1 минута приемлема, если zcollectoraction содержит много миллионов строк. Group By не быстры на DB6 - person András; 04.10.2017
comment
Ввод lt_coll_act как HASHED с уникальным ключом также может помочь увеличить этот материал. - person Suncatcher; 04.10.2017
comment
И используйте параллельные курсоры. техника для перебора вложенных itabs. - person Suncatcher; 04.10.2017
comment
@Suncatcher HASHED не сработает. Комбинация только bukrs kunnr dat не уникальна, и мы не знаем время - person András; 04.10.2017
comment
@ Андрас, почему ты сделал такой вывод? Структуру zcollectoraction мы точно не знаем, поэтому можем утверждать, что она не уникальна только предположительно %) - person Suncatcher; 04.10.2017
comment
@Suncatcher 1) дубликаты ключей вызывают дамп, поэтому вам не следует использовать его, если вы не знаете наверняка. 2) OP использует MAX для time, что имеет смысл только в том случае, если их может быть более одного. - person András; 04.10.2017

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

Добавление запросов выбора внутри цикла всегда замедляет выполнение приложения, поскольку приложение должно выполнять запросы к базе данных для каждого цикла. Загрузка всей необходимой информации во внутреннюю таблицу и последующая обработка данных в приложении выполняются намного быстрее.

Дайте мне знать, если вам потребуется дополнительная информация по этому вопросу.

person anslem arnolda    schedule 04.10.2017
comment
Я тоже так подумал и создал таблицу, заполнил ее данными выбора (30500 записей) и заменил настоящую таблицу, но забыл, что не могу использовать оператор select с itab. А также я не знаю, как получить МАКСИМАЛЬНОЕ время с помощью инструкции Read. Я буду очень обязан, если вы можете мне помочь. Спасибо. - person ekekakos; 04.10.2017
comment
Добавьте два оператора select из цикла. Сначала напишите выбор, чтобы получить МАКСИМАЛЬНОЕ ВРЕМЯ для другой внутренней таблицы (itab1) с помощью вашей исходной внутренней таблицы с для всех записей (it_collectoraction). Затем внутри вашего цикла напишите оператор чтения и получите время (MAX TIME) из itab1 в рабочую область (wa1), которая содержит время. Затем напишите еще один оператор чтения, чтобы получить значения из it_collectoraction в другую рабочую область (wa2), передав значение времени из (wa1), которое является максимальным значением времени. Это та же логика, которую вы написали изначально, которая гораздо более оптимизирована. - person anslem arnolda; 04.10.2017
comment
Обратитесь к образцу кода, предоставленному Андрашем, это правильно - person anslem arnolda; 04.10.2017

Прежде всего, я хочу поблагодарить всех вас за вашу помощь. Я изменяю логику выбора, используя внутреннюю таблицу со всеми записями из dbtab в соответствии с данными выбора пользователя. Итак, код стал следующим:

DATA: wa_collectoraction TYPE zcollectoraction,
  it_collectoraction TYPE TABLE OF zcollectoraction,
  itsort_collectoraction TYPE HASHED TABLE OF zcollectoraction
      WITH UNIQUE KEY mandt bukrs kunnr yearmonth dat time.

FIELD-SYMBOLS: <fs_collectoraction> LIKE LINE OF it_collectoraction.

SELECT bukrs kunnr yearmonth MAX( dat ) AS dat
  FROM zcollectoraction
    INTO CORRESPONDING FIELDS OF TABLE it_collectoraction
  WHERE bukrs IN so_bukrs AND
        kunnr IN so_kunnr AND
        dat   IN so_date
  GROUP BY bukrs kunnr yearmonth.

" Keep the total records which will be inserted.
i_tab_lines = sy-dbcnt.

SELECT * INTO TABLE itsort_collectoraction
  FROM zcollectoraction
  WHERE bukrs IN so_bukrs AND
      kunnr IN so_kunnr AND
      dat   IN so_date.

SORT itsort_collectoraction
            BY mandt bukrs kunnr yearmonth dat time DESCENDING.

LOOP AT it_collectoraction ASSIGNING <fs_collectoraction>.
  PERFORM progress_bar USING 'Retrieving data...'(035)
                             sy-tabix
                             i_tab_lines.

  READ TABLE itsort_collectoraction INTO wa_collectoraction
      WITH KEY bukrs = <fs_collectoraction>-bukrs
                kunnr = <fs_collectoraction>-kunnr
                yearmonth = <fs_collectoraction>-yearmonth
                dat   = <fs_collectoraction>-dat.
  <fs_collectoraction> = wa_collectoraction.
ENDLOOP.

Этот код запускает 43000 записей за 1 минуту. Единственная проблема в том, что после первых 10000-15000 записей процесс замедляется. Я не знаю, есть ли какая-либо команда для очистки чего-либо. Я не знаю, что очистить.

Еще раз большое спасибо всем вам. С уважением Элиас

PS. В первые 10 секунд он обрабатывает 14 000 записей. За 1 минуту обработано 38 500, а за 1 минуту и ​​50 секунд завершено 54 500 записей. У меня создается впечатление, что он выполняет что-то, что замедляет процесс. ЕСТЬ ИДЕИ?

person ekekakos    schedule 04.10.2017
comment
Поскольку вы дважды работаете с одной и той же таблицей zcollectoraction, вы должны иметь возможность перенести логику в один оператор SELECT. Изучите подзапросы и GROUP BY, если это необходимо. Каков ожидаемый результат? Просто значение MAX (время) для каждой строки? - person Mikael G; 04.10.2017
comment
Использование FOR ALL ENTRIES вместо повторения операторов IN, вероятно, значительно уменьшит количество секунд SELECT. - person András; 04.10.2017
comment
Микаэль, если вы понимаете причину этого вопроса, вы увидите, что я сделал много выборок в dbtab zcollectoraction, что вызвало большие задержки. Поэтому я решил использовать хешированную вкладку со всеми записями и полями базы данных в соответствии с критериями выбора. Эта таблица содержит 12 полей, и у каждого клиента есть много строк в месяц. Они хотят иметь отчет, который будет отображать последний день месяца для каждого клиента. Если последний день месяца имеет более 1 строки, им нужен последний день с максимальным временем. С максимальным выбором я получаю последний день месяца, а затем в цикле получаю максимальное время записи. - person ekekakos; 04.10.2017
comment
ХОРОШО. Затем я бы сказал, что вы можете решить свое требование с помощью одного SQL-запроса без какой-либо обработки внутренних таблиц ABAP. GROUP BY и HAVING решат эту проблему. - person Mikael G; 05.10.2017
comment
Убедитесь, что вы очистили рабочую область перед оператором чтения. - person anslem arnolda; 06.10.2017
comment
Таблицы @ekekakos HASHED работают быстро, только если вы используете все ключевые поля в READ TABLE. Вы этого не сделаете, поэтому он работает так же плохо, как и стандартный стол. - person András; 09.09.2018
comment
Вам не хватает БИНАРНОГО ПОИСКА в ТАБЛИЦЕ ЧТЕНИЯ - person András; 26.03.2019

Я немного опоздал на вечеринку, но в вашем первом посте я вижу, что вы просто хотите прочитать последние (максимальные (дата) и максимальные (время)) записи из одной таблицы на букры и куннры?

Используйте один выбор, чтобы получить содержимое таблицы. Выбирать только по ключевым полям или индексам: предполагаю, что дата не ключевое поле, а букры и куннры:

SELECT bukrs kunnr yearmonth dat time
FROM zcollectoraction
  INTO CORRESPONDING FIELDS OF TABLE it_collectoraction
WHERE bukrs IN so_bukrs AND
      kunnr IN so_kunnr 
.

Удалить неключевые поля из itab:

DELETE it_collectoraction WHERE dat NOT IN so_date.

Отсортируйте его по дате и времени по убыванию, чтобы последняя запись была первой комбинацией букв букр и куннр.

SORT it_collectoraction BY bukrs kunnr date DESCENDING time DESCENDING.

Удалить все соседние (= все с одинаковым ключом сравнения после первого) записи для bukrs и kunnr

DELETE ADJACENT DUPLICATES FROM it_collectoraction COMPARING bukrs kunnr.
person futu    schedule 05.10.2017
comment
Зачем вам удалять условие dat из выбора? БД делает это быстрее, и вам нужно перемещать меньше строк. Я вижу это часто, но это очень плохо для производительности. - person András; 09.09.2018
comment
На самом деле это не так уж плохо для производительности, потому что для sql важны только проиндексированные поля. Удаление потом на сервере приложений в памяти тоже быстро. Люди часто используют его, потому что оптимизатор базы данных иногда использует неправильный индекс или объединяет несколько индексов, которые не являются полностью определенными, что приводит к очень плохой производительности. Если крайне важно, чтобы использовался один конкретный индекс, это обычный способ обеспечить это. - person futu; 03.04.2019
comment
Это часто, поэтому я стараюсь с этим бороться, когда это возможно. Если у вас нет нужного индекса, создайте его. Если у вас есть правильный индекс, но оптимизатор его выбирает, воспользуйтесь подсказкой. Это очень плохая практика, и ее следует прекратить. - person András; 03.04.2019
comment
В общем, индексы дороги для всей системы, поэтому в большинстве случаев запрещено создавать новые индексы для каждого отдельного запроса. Использование подсказок было бы альтернативой, но с точки зрения обслуживания это также может рассматриваться как очень плохая практика, потому что они подрывают уровень абстракции openSQL для базы данных. Кроме того, насколько мне известно, они не отслеживаются в системе. В конце концов, разработчику приходится взвешивать недостатки. И, конечно, вы правы, в случае оператора добавление даты к условию WHERE было бы лучшим подходом :) - person futu; 03.04.2019