Самый эффективный способ SELECT в кластерной таблице?

В моей программе мне нужно выполнить выборку в таблице кластеров CDPOS. Однако этот процесс очень неэффективен и, в зависимости от объема данных, может занять несколько минут или просто выйти из-под контроля. В проведенном мной тесте получение 35 идентификаторов заняло 1 минуту и ​​15 секунд, а во втором тесте с 90 идентификаторами время ожидания программы истекло.

Текущий процесс: сначала я выполняю выборку в таблице EKKO с заказами на покупку, отфильтрованными по дате, указанной пользователем. Затем я фильтрую идентификаторы, полученные из таблицы EKKO, в таблице CDHDR. Остальные идентификаторы из таблицы CDHDR затем используются при выборе в таблице CDPOS. Наконец, я использую проверку для фильтрации неключевых полей. Примечание. Я фильтрую по идентификаторам, поскольку в таблице CDPOS нет полей даты.

SELECT, в котором у меня возникают проблемы с производительностью:

IF lt_chunk_cdhdr is not INITIAL.


    SELECT OBJECTID
           CHANGENR
           FNAME
           CHNGIND
           VALUE_NEW
           VALUE_OLD
      FROM CDPOS
      INTO TABLE lt_temp_cdpos
      FOR ALL ENTRIES IN lt_chunk_cdhdr
      WHERE OBJECTID = lt_chunk_cdhdr-objectid.

    LOOP AT lt_temp_cdpos INTO ls_cdpos.
      CHECK ls_cdpos-fname = 'FRGKE' AND ls_cdpos-value_new = '2'
         OR ls_cdpos-fname = 'MWSKZ'
         OR ls_cdpos-fname = 'KEY' AND ls_cdpos-chngind = 'I'
         OR ls_cdpos-fname = 'BRTWR'
         OR ls_cdpos-fname = 'NETWR'
         OR ls_cdpos-fname = 'ZTERM'
         OR ls_cdpos-fname = 'INCO1'.

      APPEND ls_cdpos TO lt_cdpos.
    ENDLOOP.
ENDIF.

Я попытался повысить эффективность выбора, используя советы, которые нашел в Интернете. Один из этих советов заключался в том, чтобы при работе с кластерными таблицами использовать только ключевые поля в предложении WHERE, а затем использовать команду CHECK для повторной фильтрации полученных данных. Однако даже после следования этому совету эффективность все равно недостаточна. Если количество идентификаторов, подлежащих фильтрации, близко к 80, программа не завершает выполнение.

«Таблица кластеров: происходит прямо противоположное: из-за способа хранения этих таблиц предложение WHERE должно содержать только ключевые поля, а остальные поля следует проверять с помощью команды CHECK. База данных не может обрабатывать таблицы кластеров, поскольку она обрабатывает прозрачные Принуждение базы данных к распаковке и проверке полей (в случае выбора с неключевыми полями в предложении WHERE) в большинстве случаев менее эффективно, чем квалификация только с ключевыми полями и оставление ПРОВЕРКИ для неключевых полей. после того, как они будут возвращены». Ссылка (на португальском языке): https://marcolin.wordpress.com/2010/04/08/grandes-viloes-da- Performance-sap/

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

Вам необходимо выбрать все ключевые поля из CDHDR (OBJECTCLAS, OBJECTID и CHANGENR) и использовать эти поля в поле ДЛЯ ВСЕХ ВХОДОВ при выборе из CDPOS.

József Szikszai 04.07.2024 16:58

@JózsefSzikszai Я добавил в фильтр поле OBJECTCLAS, и кажется, он работает быстрее. Однако есть нюанс с полем CHANGENR. Прежде чем использовать его, я запускаю команду: DELETE ADJACENT DUPLICATES FROM lt_chunk_cdhdr COMPARING objectid. Я делаю это, чтобы уменьшить количество идентификаторов, извлекаемых командой SELECT из таблицы CDPOS, поскольку многие записи имеют одинаковый OBJECTID, но разные значения CHANGENR. Использование приведенной выше команды уменьшает количество искомых идентификаторов примерно на 70%.

Lucas Ag 04.07.2024 17:15

@JózsefSzikszai Это сработало, у меня было время протестировать это изменение, и невероятно, как добавление поля OBJECTCLAS в «где» меняет скорость обработки. Оно буквально увеличилось с 1 минуты 15 секунд до 3 секунд. Спасибо, это очень помогло.

Lucas Ag 04.07.2024 18:55
За пределами сигналов Angular: Сигналы и пользовательские стратегии рендеринга
За пределами сигналов Angular: Сигналы и пользовательские стратегии рендеринга
TL;DR: Angular Signals может облегчить отслеживание всех выражений в представлении (Component или EmbeddedView) и планирование пользовательских...
Sniper-CSS, избегайте неиспользуемых стилей
Sniper-CSS, избегайте неиспользуемых стилей
Это краткое руководство, в котором я хочу поделиться тем, как я перешел от 212 кБ CSS к 32,1 кБ (сокращение кода на 84,91%), по-прежнему используя...
1
3
115
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Мне удалось решить проблему с производительностью, добавив в фильтр поле OBJECTCLAS в дополнение к OBJECTID. Это изменение значительно сократило время обработки.

После добавления поля OBJECTCLAS выбор выглядел так:

IF lt_chunk_cdhdr is not INITIAL.

    SELECT OBJECTID
           CHANGENR
           FNAME
           CHNGIND
           VALUE_NEW
           VALUE_OLD
      FROM CDPOS
      INTO TABLE lt_temp_cdpos
      FOR ALL ENTRIES IN lt_chunk_cdhdr
      WHERE OBJECTID = lt_chunk_cdhdr-objectid
      AND   OBJECTCLAS = lt_chunk_cdhdr-OBJECTCLAS.

    LOOP AT lt_temp_cdpos INTO ls_cdpos.
      CHECK ls_cdpos-fname = 'FRGKE' AND ls_cdpos-value_new = '2'
         OR ls_cdpos-fname = 'MWSKZ'
         OR ls_cdpos-fname = 'KEY' AND ls_cdpos-chngind = 'I'
         OR ls_cdpos-fname = 'BRTWR'
         OR ls_cdpos-fname = 'NETWR'
         OR ls_cdpos-fname = 'ZTERM'
         OR ls_cdpos-fname = 'INCO1'.

      APPEND ls_cdpos TO lt_cdpos.
    ENDLOOP.
ENDIF.

В моем первоначальном подходе я использовал только поле OBJECTID для фильтрации оператора SELECT в таблице кластера CDPOS. Однако, включив также поле OBJECTCLAS, база данных смогла оптимизировать запрос гораздо эффективнее, что привело к значительному повышению производительности.

Раньше при фильтрации 35 идентификаторов время обработки программы составляло около 1 минуты 15 секунд, но теперь с изменением время сократилось до 3 секунд.

Таким образом, мы можем заключить, что для оптимальной производительности оператора SELECT в кластерной таблице необходимо фильтровать, используя только ключевые поля в предложении WHERE, и включать как можно больше ключевых полей. Кроме того, не рекомендуется фильтровать неключевые поля в инструкции SELECT; вместо этого используйте команду ПРОВЕРИТЬ, чтобы впоследствии отфильтровать эти поля.

В системе ABAP до версии ABAP 7.53 (*) «таблица кластера» — это «вещь» ABAP, которая не известна системе базы данных как таблица: данные таблицы кластера сжимаются ядром ABAP и сохраняются внутри его. «кластер таблиц», который представляет собой таблицу в базе данных (**).

Например, CDPOS — это таблица кластера, а CDCLS — ее кластер таблицы (см. скриншоты ниже). CDCLS также содержит таблицу кластеров PCDPOS (все ее таблицы кластеров можно увидеть через «список использования» кода транзакции SE11).

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

Ваш ABAP-запрос к таблице кластера будет преобразован в запрос к кластеру таблиц и отправлен в базу данных (***), поэтому при кодировании запроса вы должны применить к кластеру таблиц те же советы по повышению производительности, что и для других классических запросов. таблицы («Прозрачные таблицы» в SE11), т. е. вы должны фильтровать данные по ключевым столбцам кластера таблиц, в конечном итоге вы можете опустить любой из последних ключевых столбцов, но не первые, иначе это может быть довольно медленно. Остальные столбцы не важны для повышения производительности, поскольку они неизвестны базе данных.

CDCLS имеет этот первичный ключ:

  • MANDT (не требуется, поскольку неявно добавляется ядром ABAP)
  • OBJECTCLAS
  • OBJECTID
  • CHANGENR

Вот скриншоты кода транзакции SE11, которые показывают, является ли таблица кластерной (CDPOS), и информацию о ее кластере таблиц (CDCLS):




(*) Кластер таблиц - Глоссарий ABAP:

«До версии 7.53 таблица базы данных содержала данные нескольких кластерных таблиц. Таблицы кластеров больше не поддерживаются с версии 7.53, и все кластеры таблиц были удалены».

(**) Вот как данные хранятся в кластере таблиц, авторские права help.sap.com, извлечено 5 июля 2024 г.:

(***) Вы можете использовать код транзакции ST05, чтобы запустить трассировку SQL и посмотреть, как ваш SQL-запрос ABAP преобразуется в запрос к кластеру таблицы базы данных.

Интересно, спасибо за объяснение, почему таблица кластеров работает именно так.

Lucas Ag 10.07.2024 15:45

Извините, я пока не могу использовать комментарии. Однако это 2 цента для @Lucas Ag. Конечно, это зависит от базы данных, но обычное практическое правило для хорошей производительности SQL-запроса заключается в предоставлении полей, подкрепленных индексом, начиная с первого поля (наиболее общего) и затем до последнего (наиболее общего). подробно). То есть дело не в том, чтобы просто предоставить как можно больше ключевых полей, а в правильном порядке. То есть в случае кластерных таблиц единственным имеющимся индексом является первичный ключ. Допустим, lt_chunk_cdhdr содержит все документы изменений для данного идентификатора класса/объекта. Рассмотрим приведенные ниже два запроса, оба из которых относятся к двум ключевым полям в предложении where:

    SELECT OBJECTID
       CHANGENR
       FNAME
       CHNGIND
       VALUE_NEW
       VALUE_OLD
    FROM CDPOS
    INTO TABLE lt_temp_cdpos
    FOR ALL ENTRIES IN lt_chunk_cdhdr
    WHERE OBJECTID = lt_chunk_cdhdr-objectid
    AND   OBJECTCLAS = lt_chunk_cdhdr-OBJECTCLAS.

    SELECT OBJECTID
       CHANGENR
       FNAME
       CHNGIND
       VALUE_NEW
       VALUE_OLD
    FROM CDPOS
    INTO TABLE lt_temp_cdpos
    FOR ALL ENTRIES IN lt_chunk_cdhdr
    WHERE OBJECTID = lt_chunk_cdhdr-objectid
    AND   CHANGENR = lt_chunk_cdhdr-CHANGENR.

Оба запроса имеют только два ключевых поля в предложении where, но запрос №2 будет (намного) медленнее. Однако для достижения максимальной производительности я бы рекомендовал заполнить все 3 ключевых поля — в любом случае они у вас уже есть в таблице lt_chunk_cdhdr. Причина: внутренний перевод for all entries в собственный запрос к БД и последствия этого. Например, если ваш объект относится к классу KRED (поставщик) с идентификатором «1234567890» и имеет 50 документов изменений (то есть 50 записей в CDHDR), ядро ​​ABAP может преобразовать его во что-то вроде:

    select <list of fields> from cdpos
    where mandtant = <your current system client>
    and objectclas = 'KRED' and objectid = '1234567890'  --record #1
    union all
    select <list of fields> from cdpos
    where mandtant = <your current system client>
    and objectclas = 'KRED' and objectid = '1234567890'  --record #2
    union all
    ...
    union all
    select <list of fields> from cdpos
    where mandtant = <your current system client>
    and objectclas = 'KRED' and objectid = '1234567890'  --record #50

Такое поведение наблюдалось в MS SQL. В Oracle же вместо OR стоит UNION ALL. Вышеупомянутое показывает следующее:

  • данные выбираются гораздо меньшими порциями, чем во внутренней таблице, что зависит от максимального размера оператора SQL и аналогичных ограничений БД;
  • если в запросе указаны только общие поля, БД придется вернуть один и тот же набор из 50 записей для каждого частичного ПК (ну, это наш пример с 50 документами изменений для данного объекта; конечно, оптимизатор БД довольно умен и выиграл так не делаю, но это лишняя нагрузка на БД).

Итог: самый эффективный способ select ... for all entries — указать в запросе полный ПК:

    SELECT ...
    FROM CDPOS
    INTO TABLE lt_temp_cdpos
    FOR ALL ENTRIES IN lt_chunk_cdhdr
    WHERE OBJECTCLAS = lt_chunk_cdhdr-CHANGENR
    AND   OBJECTID = lt_chunk_cdhdr-objectid
    AND   CHANGENR = lt_chunk_cdhdr-CHANGENR.

Надеюсь, это кому-то поможет.

Кстати, использование check в цикле весьма неэффективно. Если у вас есть только общие ключи — например, балансовая единица документа FI (таблица BSEG, поле BUKRS), а затем вы хотите выбрать только записи для определенного счета ГК, вам придется выбрать миллионы записей ради , например, 10 тыс. записей. Более того, цикл по таблице с условием check не будет таким же эффективным, как фильтрация на уровне ядра (которая в данном случае неявно выполняется тем же сервером приложений).

r04dRunErr 23.07.2024 09:50

Еще одна важная особенность CDHDR — это то, что поле CHANGENR содержит уникальные значения. Итак, если вы хотите выбрать записи из CDPOS, соответствующие определенным датам, просто отфильтруйте CDHDR по этим датам, а затем используйте полный ключ CDHDR для получения записей из CDPOS. Извините, продолжайте вспоминать все больше и больше вещей сразу после того, как я оставлю комментарий...

r04dRunErr 23.07.2024 09:54

Другие вопросы по теме

Похожие вопросы

Как ускорить интерполяцию для этого конкретного примера?
Каков самый быстрый способ расчета ежедневного баланса со сложными процентами в Pandas или Spark?
Почему INumber<T>.CreateX(int n) настолько медленный по сравнению с неявным преобразованием для чисел с плавающей запятой и двойной точности?
Есть ли название для этого типа структурированных данных и как его более эффективно использовать?
Преобразование списка ИЛИ массива в диапазон
Почему массив % 1 работает на 150 % медленнее для меньших чисел, чем для больших?
Левая часть выражения LIKE должна иметь значение varchar (фактически: varbinary). Какая альтернатива преобразованию varbinary в varchar?
Есть ли какая-либо польза от использования словарного понимания, если возможен эквивалентный dict(zip(a, b))?
Узнайте, какой поток/процесс потребляет процессор
Улучшение производительности простого триггера onEdit