В моей программе мне нужно выполнить выборку в таблице кластеров 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 днем. Однако в зависимости от месяца и количества заказов на покупку это все равно может привести к тайм-ауту.
@JózsefSzikszai Я добавил в фильтр поле OBJECTCLAS, и кажется, он работает быстрее. Однако есть нюанс с полем CHANGENR. Прежде чем использовать его, я запускаю команду: DELETE ADJACENT DUPLICATES FROM lt_chunk_cdhdr COMPARING objectid.
Я делаю это, чтобы уменьшить количество идентификаторов, извлекаемых командой SELECT из таблицы CDPOS, поскольку многие записи имеют одинаковый OBJECTID, но разные значения CHANGENR. Использование приведенной выше команды уменьшает количество искомых идентификаторов примерно на 70%.
@JózsefSzikszai Это сработало, у меня было время протестировать это изменение, и невероятно, как добавление поля OBJECTCLAS в «где» меняет скорость обработки. Оно буквально увеличилось с 1 минуты 15 секунд до 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 преобразуется в запрос к кластеру таблицы базы данных.
Интересно, спасибо за объяснение, почему таблица кластеров работает именно так.
Извините, я пока не могу использовать комментарии. Однако это 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
. Вышеупомянутое показывает следующее:
Итог: самый эффективный способ 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
не будет таким же эффективным, как фильтрация на уровне ядра (которая в данном случае неявно выполняется тем же сервером приложений).
Еще одна важная особенность CDHDR
— это то, что поле CHANGENR
содержит уникальные значения. Итак, если вы хотите выбрать записи из CDPOS
, соответствующие определенным датам, просто отфильтруйте CDHDR
по этим датам, а затем используйте полный ключ CDHDR
для получения записей из CDPOS
. Извините, продолжайте вспоминать все больше и больше вещей сразу после того, как я оставлю комментарий...
Вам необходимо выбрать все ключевые поля из CDHDR (OBJECTCLAS, OBJECTID и CHANGENR) и использовать эти поля в поле ДЛЯ ВСЕХ ВХОДОВ при выборе из CDPOS.