62
«Ну и запросы у вас, сказала база данных и повисла». Данный документ подготовлен после общения на форуме www.sapforum.biz, так сказать спасибо камрадам: №1, Darth, Tav_48, Bdmalex и.. если кого забыл, обращайтесь, впишем так же ваши имена… Отдельное персональное спасибо и пиво, виски, капучино при встрече, по желанию Dmitriy-ю и Удав-у, внесших много ценных замечаний по документу. Комментарии и замечания приветствуются… можно на форуме, можно на адрес [email protected]. 1. Выбор данных для обработки. ........................................................................................ 2 2. Использование агрегатных функций. ............................................................................. 5 3. Ограничение выборки в условиях WHERE. ................................................................... 7 4. Проверка наличия значения в таблице. ....................................................................... 11 5. Буферизация чтения данных ........................................................................................ 12 6. Объединение таблиц в запросах. ................................................................................. 13 7. COMMIT WORK. ............................................................................................................. 19 8. Определение внутренних таблиц. ................................................................................ 25 8.1. Организация внутренних таблиц. ........................................................................... 25 8.2. Индексы внутренних таблиц. .................................................................................. 28 8.3. Работа с внутренними таблицами. ......................................................................... 30 9. Работа с внутренними таблицами: LOOP, READ, DELETE и т.д. ............................... 37 10. Тестирование программ. ............................................................................................. 44 10.1. Быстрая проверка времени выполнения запросов. ........................................... 44 10.2. SLIN – Расширенная проверка программы. ........................................................ 45 10.3. SCI / SCII – SAP Code Inspector. ........................................................................... 55 В общем как не крутись, а писать на ABAP приходится, это какие-то отчеты/обработки/экзиты, которые гудят долго, жрут, как память, так и процессор и конечном итоге имеем недовольных пользователей и медленную систему. Хотя конечно, сейчас есть много консультантов функциональщиков, которые ничего не писали и не собираются, это их выбор и мы его уважаем, хотя возможно не одобряем. Данная статья предназначена как раз для тех, кто пишет на ABAP или хочет научиться писать правильно, т. е. поговорим об оптимизации или как писать быстрые программы. Кстати, не обязательно писать программы, иногда, достаточно написать небольшую проверку с одним оператором SELECT в экзите, чтобы тормознуть работу всей системы в разы. Я не претендую на истину, все же некоторые вопросы оптимизации очень специфичны и если у меня в тесте, что-то работает быстрее, то это не значит, что у вас это будет так же быстро. Хотя в целом, большинство операций описанных ниже, будут корректными в большинстве случаев для всех систем. В данном случае под оптимизацией будем понимать оптимизацию написания кода на языке ABAP, а не способы выбора данных из таблиц системы. Конечно же, второй тип оптимизации зачастую будет быстрее, т. е. если вы знаете, в какой таблице находятся нужные вам данные, в требуемом разрезе, то выбрать их будет быстрее, чем сделать тоже, но обработав десяток таблиц, но это песня из другой оперы и она очень будет индивидуальна в каждой функциональности. Часть информации, из описанного ниже, есть в справке к системе и «зубры» и так знают, как писать правильно, но вот молодЕжь такие перлы выдает, что иногда становится страшно... Страница 1 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Оптимизация ABAP v1-2

Embed Size (px)

Citation preview

Page 1: Оптимизация ABAP v1-2

«Ну и запросы у вас, сказала база данных и повисла».

Данный документ подготовлен после общения на форуме www.sapforum.biz, так сказать спасибо камрадам: №1, Darth, Tav_48, Bdmalex и.. если кого забыл, обращайтесь, впишем так же ваши имена… Отдельное персональное спасибо и пиво, виски, капучино при встрече, по желанию Dmitriy-ю и Удав-у, внесших много ценных замечаний по документу.

Комментарии и замечания приветствуются… можно на форуме, можно на адрес [email protected].

1. Выбор данных для обработки. ........................................................................................ 2 2. Использование агрегатных функций. ............................................................................. 5 3. Ограничение выборки в условиях WHERE. ................................................................... 7 4. Проверка наличия значения в таблице. ....................................................................... 11 5. Буферизация чтения данных ........................................................................................ 12 6. Объединение таблиц в запросах. ................................................................................. 13 7. COMMIT WORK. ............................................................................................................. 19 8. Определение внутренних таблиц. ................................................................................ 25

8.1. Организация внутренних таблиц. ........................................................................... 25 8.2. Индексы внутренних таблиц. .................................................................................. 28 8.3. Работа с внутренними таблицами. ......................................................................... 30

9. Работа с внутренними таблицами: LOOP, READ, DELETE и т.д. ............................... 37 10. Тестирование программ. ............................................................................................. 44

10.1. Быстрая проверка времени выполнения запросов. ........................................... 44 10.2. SLIN – Расширенная проверка программы. ........................................................ 45 10.3. SCI / SCII – SAP Code Inspector. ........................................................................... 55

В общем как не крутись, а писать на ABAP приходится, это какие-то отчеты/обработки/экзиты, которые гудят долго, жрут, как память, так и процессор и конечном итоге имеем недовольных пользователей и медленную систему. Хотя конечно, сейчас есть много консультантов функциональщиков, которые ничего не писали и не собираются, это их выбор и мы его уважаем, хотя возможно не одобряем. Данная статья предназначена как раз для тех, кто пишет на ABAP или хочет научиться писать правильно, т. е. поговорим об оптимизации или как писать быстрые программы. Кстати, не обязательно писать программы, иногда, достаточно написать небольшую проверку с одним оператором SELECT в экзите, чтобы тормознуть работу всей системы в разы.

Я не претендую на истину, все же некоторые вопросы оптимизации очень специфичны и если у меня в тесте, что-то работает быстрее, то это не значит, что у вас это будет так же быстро. Хотя в целом, большинство операций описанных ниже, будут корректными в большинстве случаев для всех систем.

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

Часть информации, из описанного ниже, есть в справке к системе и «зубры» и так знают, как писать правильно, но вот молодЕжь такие перлы выдает, что иногда становится страшно...

Страница 1 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 2: Оптимизация ABAP v1-2

хотя опять же, если вспомнить прошлый век и свой первый отчет, то как говориться, все имели право на ошибку, ну и сам такой был.

Для понимания описаний, желательны хотя бы минимальные знания языка ABAP, хотя бы в рамках обзора книги «Разработка приложений SAP R/3" авторы Рюдигер Кречмер и Вольфганг Вейс .

1. Выбор данных для обработки.

Время диалоговых процессов в системе, как известно, ограничено. Стандартно диалоговых процесс в системе может длиться не более 600 секунд и дальше ваша задача будет завершена принудительно, правда на практике, администраторы системы, это время обычно увеличивают, но и мы можем тоже, кое-что докрутить в своих запросах, чтобы и они работали чуть быстрее. И так есть таблица MKPF — заголовки документов материала. Варианта выбора и обработки значений есть два, первый в цикле оператора SELECT. ENDSELECT., второй выбор во внутреннюю таблицу и затем обработка данных в цикле оператора LOOP AT <внутренняя таблица>. ENDLOOP. Так же у этих операторов есть различные варианты их вызова.

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

DATA: lt_mkpf LIKE mkpf OCCURS 1 WITH HEADER LINE.

Варианты выборки данных.

Если структура, куда выбирают данные, совпадает со структурой из выбираемой таблицы, то можно выбрать данные наиболее быстрым способом

ВремВып: 1.355.738 микросек. a1) SELECT * INTO TABLE lt_mkpf FROM mkpf.

При реальных запросах, не все поля находящиеся в таблице MKPF, нужны во внутренней таблице LT_MKPF, тогда такую структуру надо объявить с перечислением только используемых полей. Если это так сделано, тогда нужно использовать расширение операции INTO в виде «INTO CORRESPONDING FIELDS OF» т. е., система сама разложит результат, сделав правильное соответствие выбираемых полей из таблицы, полям в структуре. При этом если выбираются все поля, то время выполнения различается в районе 0,1%, но при этом всегда время выполнения первого запроса меньше чем второго.

ВремВып: 1.369.663 микросек. b1) SELECT * INTO CORRESPONDING FIELDS OF TABLE lt_mkpf

FROM mkpf.

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

Как уже отмечалось выше, более предпочтительнее, выглядит данный запрос, если нам нужны не все поля из таблицы, например, хотим выбрать следующие поля: MBLNR — номер документа, MJAHR — год документа, XBLNR — ссылочный номер, BKTXT — краткий текст заголовка документа. Тут уже время отличается и существенно.

Страница 2 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 3: Оптимизация ABAP v1-2

ВремВып: 360.492 микросек. c1) SELECT MBLNR MJAHR XBLNR BKTXT

INTO CORRESPONDING FIELDS OF TABLE lt_mkpfFROM mkpf.

Как видим скорость выборки стала в среднем в 4 раза быстрее. Однако если сделать следующий вариант вызова, когда внутренняя таблица содержит только необходимый набор полей, то, как видим, операция «INTO TABLE» будет быстрее, в среднем от 15% до 20%

DATA: BEGIN OF lt_mkpf_short OCCURS 0, mblnr LIKE mkpf-mblnr, mjahr LIKE mkpf-mjahr, xblnr LIKE mkpf-xblnr, bktxt LIKE mkpf-bktxt,END OF lt_mkpf_short.

ВремВып: 389.617 микросек. d1) SELECT mblnr mjahr xblnr bktxt

INTO CORRESPONDING FIELDS OF TABLE lt_mkpf_shortFROM mkpf.

ВремВып: 314.952 микросек. e1) SELECT mblnr mjahr xblnr bktxt

INTO TABLE lt_mkpf_shortFROM mkpf.

Так же на скорость выборки, в случае перечисления выбираемых полей, влияет их порядок. Если порядок следования полей внутренней таблицы такой же, как и в таблице базы данных, то скорость выбора будет быстрее, при этом, если вы выбираете все поля. Например, требуется выбрать все поля из таблицы MKPF.

DATA: lt_mkpf LIKE mkpf OCCURS 1 WITH HEADER LINE.

ВремВып: 1.323.437 микросек. f1) SELECT MBLNR MJAHR VGART BLART BLAUM BLDAT BUDAT

CPUDT CPUTM AEDAT USNAM TCODE XBLNR BKTXTFRATH FRBNR WEVER XABLN AWSYS BLA2D TCODE2BFWMS EXNUM SPE_BUDAT_UHR SPE_BUDAT_ZONELE_VBELN SPE_LOGSYS SPE_MDNUM_EWM GTS_CUSREF_NOKNUMV

INTO CORRESPONDING FIELDS OF TABLE lt_mkpfFROM mkpf.

ВремВып: 1.349.799 микросек. g1) SELECT * INTO TABLE lt_mkpf FROM mkpf.

Как видим перечисление всех полей с оператором «CORRESPONDING FIELDS», работает чуть быстрее, чем операция «INTO TABLE», разница не существенна, но опять, же если выжимать максимум, то следует задуматься. Однако если вы вдруг перепутаете порядок следования полей, то получите вместо ускорения запроса, наоборот его замедление.

ВремВып: 1.348.756 микросек. h1) SELECT CPUDT CPUTM AEDAT USNAM TCODE XBLNR BKTXT MBLNR MJAHR VGART BLART BLAUM BLDAT BUDAT BFWMS EXNUM SPE_BUDAT_UHR SPE_BUDAT_ZONE

FRATH FRBNR WEVER XABLN AWSYS BLA2D TCODE2KNUMV

Страница 3 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 4: Оптимизация ABAP v1-2

LE_VBELN SPE_LOGSYS SPE_MDNUM_EWM GTS_CUSREF_NOINTO CORRESPONDING FIELDS OF TABLE lt_mkpfFROM mkpf.

На разных выборках время выполнения запроса H1, фактически вышло на скорость выполнения запроса G1.

Описанные выше комбинации работают быстро, однако грузят все данные в память, которая, не смотря на прогресс, имеет тенденцию заканчиваться в самый не подходящий момент. Поэтому иногда приходится жертвовать производительностью, но при этом оптимизировать память. Для этого данные обрабатываются по ходу их считывания в конструкции SELECT <>. ENDSELECT.

ВремВып: 1.590.766 микросек. I1) SELECT * INTO lt_mkpf FROM mkpf.

ENDSELECT.

ВремВып: 1.611.237 микросек. k1) SELECT * INTO CORRESPONDING FIELDS OF lt_mkpf FROM mkpf.

ENDSELECT.

Как видим время выполнения данного запроса больше, в среднем у меня колебания были от 10% до 15%. Но вот по памяти, такая конструкция вне конкуренции. Конечно же, время выполнения будет быстрее, если перечислить в запросе только поля, которые нам нужны.

ВремВып: 561.934 микросек. j1) SELECT MBLNR

MJAHR VGART BLART

INTO CORRESPONDING FIELDS OF lt_mkpf FROM mkpf.

ENDSELECT.

Как видим скорость выполнения запроса J1 в среднем в три раза выше, чем запросов I1. По поводу перечисления полей и порядка их следования, правила такие же, как и для запросов F1 и H1, т. е. порядок следования полей важен.

Выводы (скорость): Наиболее быстрым, будет вариант выбора данных, во внутреннюю таблицу используя запрос вида E1, при этом внутренняя таблица должна содержать только те поля, которые нам необходимы для работы. Порядок следования полей, желательно должен совпадать с порядком следования полей из выбираемой таблицы.

Выводы (память): Если у вас хватает памяти, то конструкция выбора всех данных в память с последующей обработкой будет предпочтительнее, чем их построчная обработка. Для оптимизации использования памяти, выбирайте и определяйте в структурах только те поля, которые вам нужны для обработки. Из практики, мне только раз встретилась система, где памяти было действительно не то чтобы мало, но данных было, очень много, поэтому пришлось оптимизировать память, жертвуя скоростью выполнения запроса.

Страница 4 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 5: Оптимизация ABAP v1-2

2. Использование агрегатных функций. Агрегатные функции это использование таких конструкций как SUM, COUNT, MAX и т. д. в запросах. Однозначно сказать, что будет быстрее, т. е. выбрать данные во внутреннюю таблицу и посчитать все самому или же попросить, чтобы за нас все посчитала база данных сложно. Так же, наблюдается зависимость работы данных функции от типа базы данных, на которой работает система. Данные тесты проводились для базы данных DB6, рисунок 1: Database.png. Большая вероятность, что для других баз данных результаты будут отличными, от полученных в данном описании.

Рисунок 1: Database.png

В общем виде ситуация следующая, есть запрос вида

SELECT SUM( menge ) FROM mkpf WHERE xauto = 'X' AND meins = 'ST'.

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

DATA: l_menge LIKE mseg-menge.DATA: BEGIN OF lt_mseg OCCURS 0, menge LIKE mseg-menge,END OF lt_mseg.

ВремВып: 1.764.975 микросек. a2) SELECT SUM( menge ) INTO l_menge FROM mseg

WHERE xauto = 'X' AND meins = 'ST'.

ВремВып: 1.524.474 микросек. b2) SELECT menge INTO TABLE lt_mseg

FROM mseg WHERE xauto = 'X' AND meins = 'ST'.

CLEAR: l_menge.LOOP AT lt_mseg.

l_menge = l_menge + lt_mseg-menge.ENDLOOP.

В среднем время выполнения второго запроса B2, оказывается быстрее, чем использование агрегатной функции, хотя конечно написать кода приходится больше и выглядит он не так красиво как в запросе A2. В среднем после более чем десяти прогонов, время выполнения запроса B2 было быстрее от 7% до 15%. В среднем было около 10% выигрыш в скорости работы.

Примечание: Об оптимизации циклов по внутренним таблицам, будет рассказано ниже. Использование такой оптимизации, даст еще прирост производительности для запроса B2.

С функцией COUNT результат еще интереснее

DATA: l_menge LIKE mseg-menge, l_count LIKE sy-tabix.

Страница 5 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 6: Оптимизация ABAP v1-2

DATA: BEGIN OF lt_mseg OCCURS 0, menge LIKE mseg-menge,END OF lt_mseg.

ВремВып: 2.880.489 микросек. c2) SELECT COUNT( * ) INTO l_count FROM mseg

WHERE xauto = 'X' AND meins = 'ST'.

ВремВып: 2.152.414 микросек. d2) SELECT menge INTO TABLE lt_mseg

FROM mseg WHERE xauto = 'X' AND meins = 'ST'.

CLEAR: l_count.LOOP AT lt_mseg. l_count = l_count + 1.ENDLOOP.

По факту, скорость выполнения запроса D2 меньше, чем выполнения C2. При этом после 50 прогонов в паре случаев было, что D2 почти был равен по скорости запросу С2. Среднее значение процента, по сумме прогонов получилось в районе от 10% до 15% в скорости выполнения.

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

DATA: l_menge LIKE mseg-menge,DATA: BEGIN OF lt_mseg OCCURS 0, menge LIKE mseg-menge,END OF lt_mseg.

ВремВып: 2.394.439 микросек.e2) SELECT menge INTO TABLE lt_mseg

FROM mseg WHERE xauto = 'X' AND meins = 'ST'.SORT lt_mseg BY menge DESCENDING.READ TABLE lt_mseg INDEX 1.

ВремВып: 2.332.711 микросек.f2) SELECT MAX( menge ) INTO l_menge FROM mseg

WHERE xauto = 'X' AND meins = 'ST'.

ВремВып: 2.335.711 микросек. G2) SELECT menge INTO TABLE lt_mseg

FROM mseg WHERE xauto = 'X' AND meins = 'ST'ORDER BY menge DESCENDING.READ TABLE lt_mseg INDEX 1.

При этом если в запрос E2 добавить сортировку, используя конструкцию ORDER BY, т. е. исключаем локальную сортировку после запроса, пример запроса G2, то скорость практически была такой же, как и запроса F2. Индексов на поле, по которому выполнялась сортировка, не было. В целом скорость выполнения между запросами E2 и F2, колебалась в районе от 3% до 5%.

Примечание: При проверке работы агрегатных функций для базы данных Oracle 9, по таблицам COSS/COCP/COEP, функция суммирования работала быстрее, чем локальная выгрузка во внутренние таблицы с последующей обработкой в цикле по внутренней таблице. При проверке суммирования, GROUP BY был по индексу.

Страница 6 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 7: Оптимизация ABAP v1-2

Выводы (скорость): Так как работа агрегированных функций очень специфична в зависимости от используемой базы данных, то сделайте тестирование работы этих функций на своей системе, чтобы определить наиболее быстрый вариант. Для текущей используемой системы рекомендации оказались следующими, для функций COUNT и SUM по возможности нужно было использовать локальную обработку, с чтением данных во внутренние таблицы и последующим расчетом, после запросов, а для функций MIN и MAX, наоборот, лучше оказалось выполнять обработку, используя их.

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

3. Ограничение выборки в условиях WHERE.

Стандартная рекомендация, если вы можете ограничить выборку по каким-то полям, то сделайте это. Скорость обработки, будет выше, чем проход по всей таблице, без каких либо ограничений, с последующим выбором нужных данных из внутренней таблицы. Как минимум, выигрыш будет на количестве пересылаемых данных от сервера к клиенту, в том случае если по полям ограничений в условии WHERE, нет никаких индексов, и выполняется полный проход по всем записям таблицы. Конечно, более логичным будет, при формировании условия отбора, обратить внимание на наличие индексов к таблице. После чего сформировать запрос так, чтобы оптимизатор не ломал голову, какой индекс использовать, т. е. при перечислении полей в условии WHERE, используйте порядок такой же, как есть в подходящем индексе, хотя данное утверждение спорно и зависит от типа используемой базы данных и статистики индекса, если подходящих индексов несколько. Из классики:

ВремВып: 1.835.003 микросек. a3) SELECT * FROM SBOOK INTO SBOOK_WA.

CHECK: SBOOK_WA-CARRID = 'LH' AND SBOOK_WA-CONNID = '0400'.

ENDSELECT.

ВремВып: 72.706 микросек. b3) SELECT * FROM SBOOK INTO SBOOK_WA

WHERE CARRID = 'LH' ANDCONNID = '0400'.

ENDSELECT.

Как видим, запросы с ограничением условия в WHERE, работают быстрее, в разы. Кстати, известный факт при использовании довеска «FOR ALL ENTRIES IN», не забываем делать проверку на наличие данных в таблице, используемой в условии. Потому что, если данных там нет, то будет выполнен полный проход по таблице выборки, а это плачевно отражается на скорости выбора данных, даже если вам нужно получить все записи.

DATA: BEGIN OF lt_mseg OCCURS 0, menge LIKE mseg-menge,END OF lt_mseg.DATA: BEGIN OF lt_meins OCCURS 0, meins LIKE mseg-meins,END OF lt_meins.

ВремВып: 2.190.261 микросек.

Страница 7 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 8: Оптимизация ABAP v1-2

с3) CLEAR: lt_meins[].SELECT menge INTO TABLE lt_msegFROM msegFOR ALL ENTRIES IN lt_meinsWHERE meins = lt_meins-meins.

ВремВып: 1.748.061 микросек.d3) SELECT menge INTO TABLE lt_mseg

FROM mseg.

Как видим, простой проход по таблице выполняется быстрее, чем тот же самый проход, но с пустой таблицей в условии FOR ALL ENTRIES. Поэтому отслеживайте, чтобы таблица в таком условии не была пустой. Если в ходе работы программы у вас может возникать такая ситуация, то возможно более правильным будет сделать два запроса в программе, в зависимости от заполнения таблицы lt_meins.

При задании ограничения по полям очень важно, чтобы в системе были подходящие индексы, иначе при запросе, система будет выполнять полный проход по таблице. Наличие индексов к таблицам можно посмотреть в транзакции SE11. В режиме просмотра данных таблицы, на панели инструментов есть кнопка «Индексы», рисунок 2: SE11-4.png.

Рисунок 2: SE11-4.pngКроме показанных в списке индексов, где первая колонка это имя индекса, всегда существует еще так называемый индекс первичного ключа с именем «0», в этот индекс входят все поля, которые отмечены в таблице как ключевые. В данном случае, для таблицы MKPF, существует только один дополнительный индекс, построенный по дате проводки документов материала. Для просмотра данных индекса сделайте двойной клик в строке.

Во время построения запроса, оптимизатор базы данных, анализирует поля в условии WHERE и старается найти подходящий индекс, если такой не найден, то будет выполнен полный перебор записей таблицы, что конечно же катастрофически скажется на скорости выбора данных. Для большинства баз данных, порядок перечисления полей в условии WHERE не важен, т.е. если есть индекс по полям 1 и 2, а в ограничении порядок полей стоит

Страница 8 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 9: Оптимизация ABAP v1-2

2 и 1, то оптимизатор теоретически найдет правильный индекс и будет его применять. Для базы DB2 это правило работает. Так же оптимизатор старается подобрать индекс, если есть не полная совместимость по используемым полям, т.е. например индекс содержит только некоторые из полей WHERE или же, наоборот, в индексе содержится больше полей, чем в запросе. При наличии нескольких подходящих индексов, система будет использовать тот индекс, у которого лучшая статистика. Статистику индекса умеют собирать системные администраторы базы данных. В общем виде статистика, в терминах системы «Estimated Costs» это число, значение которого рассчитывается в зависимости от величины различных (неодинаковых) записей в таблице, чем оно меньше, тем более лучшим считается индекс.

По возможности следует избегать оператора NOT в условиях, так как при использовании такой конструкции поля определенные через NOT не обрабатываются как пригодные для индекса. Поэтому нужно стараться избегать операции отрицания. Так же это относится и к операциям не равно – «<>», такие поля так же пропускаются индексом. Для примера использовалась таблица MSEG с анализом использования индексов в транзакции ST05. В системе создан завод U100 и для этого завода созданы два склада с кодами 0001 и 0002. Простым запросом выбираем документы, проведенные по одному или другому складу. Например, выберем документы, проведенные по складу 0001.

Данные для работы:data: lt_mseg like mseg occurs 1 with header line.

ВремВып: 1.660 микросек.e3) SELECT * INTO TABLE lt_mseg

FROM mseg WHERE werks = '1000' AND lgort = '0001'.

ВремВып: 16.215 микросек.f3) SELECT * INTO TABLE lt_mseg

FROM mseg WHERE werks = '1000' AND lgort <> '0002'.

Как видим простое указание, выбирать документы, где завод не равен 0002, т.е. только документы с заводом 0001, приводит к значительному увеличению времени работы запроса. Поверьте что завода там действительно только два. Причина кроется в выборе индексов для запросов E3 и F3. Для запроса E3 система использовала созданный индекс по заводу и складу с именем MSEG~TST, рисунок 3: ST05-TST-0.png.

Страница 9 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 10: Оптимизация ABAP v1-2

Рисунок 3: ST05-TST-0.png

Для запроса F3, этот индекс уже не использовался, как видим, был взят индекс MSEG~M, причем в этом индексе, использовалось, только одно поле с кодом завода, данные кода склада <> 0002, были вообще пропущены, рисунок 4: ST05-TST-2.png, т.е. по факту оптимизатор выбирал индекс исходя их поля «Завод», и совсем не интересовался полем «Склад».

Рисунок 4: ST05-TST-2.png

Исходя из построения плана запроса, можно так же сделать следующий вывод, если для завода будет создано, например, много складов, например завод 2200 в IDES содержит более 30 складов и стоит задача выбрать все документы за исключением склада 0001, то более правильно, будет перечислить в условии все склады в используя команду IN, за исключением склада 0001, чем указать в условии значение «не равно складу 0001».

ВремВып: 274 микросек.

Страница 10 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 11: Оптимизация ABAP v1-2

g3) select * into table lt_msegfrom mseg where werks = '2200' and

lgort IN ('0002', '0003', '0004', '0088', '0131', <Все склады>, 'WL41', 'WL42').

ВремВып: 9.981 микросек.h3) select * into table lt_mseg

from mseg where werks = '2200' and lgort <> '0001'.

Как видим скорость в выполнении запросов существенно различается, а все потому, что для запроса H3, использовался не тот индекс, по аналогии с рисунком 4: ST05-TST-2.png. Хотя конечно писать запрос G3 дольше, но как пример можно использовать переменную типа RANGES выбрать в нее нужные все склады завода 2200 и удалить лишние, например 0001.

Выводы (скорость): Если возможно, то при любых запросах к базе данных, стараемся ограничивать объем выбираемых данных, используя конструкцию WHERE, при этом особое внимание обращаем на наличие индексов по полям, участвующим в ограничениях выбора, так как это значительно ускоряет обработку данных. Не используйте в ограничениях WHERE операции отрицания NOT или не равно («<>»), раскладывайте эти конструкции на более понятные для оптимизатора варианты равно или операцию IN. Иначе, при выборе, система будет выполнять полный проход по таблице данных или будет использовать не корректные для данной ситуации индексы.

Выводы (память): При выборе данных во внутренние таблицы, ограничение WHERE, всегда позволяет получить из базы меньший объем данных и соответственно получить меньшие затраты по используемой памяти, в случае выбора данных во внутренние таблицы. Поэтому, даже если у вас нет подходящих индексов, но есть ограничение по памяти, использование ограничений в WHERE желательно, т.е. не имеет смысла загружать лишние данные во внутренние таблицы, для их последующего исключения из обработки локально.

4. Проверка наличия значения в таблице.

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

ВремВып: 764 микросек. a4) SELECT * FROM SBOOK INTO SBOOK_WA

WHERE CARRID = 'LH'.EXIT.

ENDSELECT.

ВремВып: 103 микросек. b4) SELECT * FROM SBOOK INTO SBOOK_WA

UP TO 1 ROWSWHERE CARRID = 'LH'.ENDSELECT.

По факту, в среднем, скорость работы отличается от 6 до 8 раз, так что не забываем такой оператор как UP TO <x> ROWS и уже ни в коем случае не используем для проверки наличия агрегатную функцию COUNT( * ), результат будет совсем грустным.

ВремВып: 1.424 микросек.

Страница 11 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 12: Оптимизация ABAP v1-2

c4) DATA l_count like sy-tabix.SELECT COUNT( * ) INTO l_countFROM SBOOK UP TO 1 ROWS

WHERE CARRID = 'LH'.

ВремВып: 103 микросек. d4) SELECT SINGLE * FROM sbook INTO sbook_wa

WHERE carrid = 'LH' .

Еще один способ проверить наличие значения в таблице это использовать конструкция SELECT SINGLE. Скорость выполнения запроса D4, будет аналогичная запросу B4. С точки зрения интерфейса к базе данных, оба запроса B4 и D4, выдадут одинаковую инструкцию на уровень базы данных. Однако, если использовать расширенную проверку программы, транзакция SLIN, то будет сгенерировано следующее предупреждение:

In "SELECT SINGLE ...", the WHERE condition for the key field "BOOKID" does nottest for equality. Therefore, the single record in question may not be unique.

Суть предупреждения, сводится к тому, что для запросов вида SELECT SINGLE, система предполагает всегда задание уникального ключа в условии отбора WHERE, в данном же случае, ограничения условия WHERE в запросе, не обеспечивают такой уникальности, то система сообщает, что может существовать более одной записи для запроса. Так как это предупреждение, выдается только при расширенной проверке программы, то это уже дело разработчика, обращать на него внимание или нет.

Выводы (скорость): Для проверки наличия значений в таблице, используем конструкцию с UP TO <X> ROWS. Кстати, документация по разработке SAP, так же рекомендует данную инструкцию.

Выводы (память): Для оптимизации памяти, если мы проверяем только наличии записи в базе данных, вместо выбора всех полей, лучше указать одно поле, на пример CARRID. Так же, такая конструкция, влияет и на скорость обработки, при этом замечена следующая закономерность, время запроса B4 уменьшается на 6-7 микросекунд, а запроса D4 на 10-11 микросекунд, т.е. время запроса D4 поле 50 тестовых прогонов было всегда меньше, хотя 4-5 микросекунд, величина практически не ощутимая.

5. Буферизация чтения данных

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

ВремВып: 83 микросек. а5) SELECT SINGLE * FROM T100 INTO T100_WA

BYPASSING BUFFERWHERE SPRSL = 'D' AND

ARBGB = '00' ANDMSGNR = '999'.

ВремВып: 5 микросек. b5) SELECT SINGLE * FROM T100 INTO T100_WA

WHERE SPRSL = 'D' ANDARBGB = '00' AND

Страница 12 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 13: Оптимизация ABAP v1-2

MSGNR = '999'.

Выводы (скорость): Если не знаем зачем, то никогда не отключаем буферизацию чтения данных. Для некоторых таблиц базы, вы можете обратить внимание, что операции чтения одинаковы, не зависимо от наличия директивы BYPASSING BUFFER, это связано с техническими параметрами настройки таблиц участвующих в запросе, для просмотра технических параметров таблиц, можно воспользоваться транзакцией SE13, рисунок 3: SE13.png. Как видим для таблицы MSEG буферизация запрещена, а для таблицы T100, буферизация разрешена, поэтому для таблицы MSEG директива BYPASSING BUFFER не актуальна.

Выводы (память): С точки зрения работы с системой вы можете влиять на буферизацию таблиц. SAP допускает такую модификацию технических параметров настройки, без запроса ключа на модификацию объекта. В общем итоге таблицы с включенной буферизацией используют специальную область памяти «table buffer», которая имеет свой ограниченный размер. При чтении данных, для таблиц с включенной буферизацией, в общем виде система, проверят наличие записи в буфере, и только в случае ее отсутствия, производит чтение с диска. Если данные в таблице из категории, «часто изменяемые», то при включенной буферизации запросы на чтение могут выбирать уже не актуальные данные. Для своих Z-таблиц использовать буферизацию или нет решать вам, базируясь на логике использования таблиц в разрабатываемых приложениях, чаще всего в приложении буферизируются таблицы справочников. Для таблиц SAP, изменять тип буферизации крайне не желательно. Последствия могут быть катастрофическими.

Рисунок 5: SE13.png

6. Объединение таблиц в запросах.

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

ВремВып: 6.292 микросек.a6) SELECT * FROM SPFLI INTO SPFLI_WA.

SELECT * FROM SFLIGHT INTO SFLIGHT_WAWHERE CARRID = SPFLI_WA-CARRID AND

CONNID = SPFLI_WA-CONNID.

Страница 13 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 14: Оптимизация ABAP v1-2

ENDSELECT.ENDSELECT.

ВремВып: 7.673 микросек. b6) SELECT * INTO WA

FROM SPFLI AS P INNER JOIN SFLIGHT AS F ON

P~CARRID = F~CARRID ANDP~CONNID = F~CONNID.

ENDSELECT.

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

ВремВып: 28.083.839 микросек.c6) TABLES: mkpf, mseg.

SELECT * FROM mkpf. SELECT * FROM mseg

WHERE mblnr = mkpf-mblnr ANDmjahr = mkpf-mjahr.

ENDSELECT.ENDSELECT.

ВремВып: 16.145.212 микросек. d6) DATA: lt_mseg LIKE mseg.

SELECT * INTO CORRESPONDING FIELDS OF lt_msegFROM mkpf AS mINNER JOIN mseg AS s ON

m~mblnr = s~mblnr ANDm~mjahr = s~mjahr.

ENDSELECT.

Как видим, скорость выполнения уже будет быстрее, однако в целом, при объединении таблиц, всегда нужно смотреть на объемы данных, которые в них находятся. Так же, по возможности, вам требуется ограничивать выбираемые данные, причем ограничение будет тем качественнее, чем на более высоком уровне будет сделано, т. е. ограничение по таблице MKPF более предпочтительно, чем на уровень ниже по MSEG.

Выводы (скорость): В данном случае мне сложно что-то советовать, потому что это уже тема, связанная с хранимыми данными, а в начале статьи было сказано, что о такой оптимизации постараемся особо не говорить. В целом, как видим, объединение с чтением во внутреннюю таблицу или хотя бы во внутреннюю переменную выполняются быстрее, что следует из темы 1, но в данном случае подключается зависимость скорости от количества данных находящихся в таблицах.

При выполнении соединения, важным условием является наличие индексов по полям соединения таблиц. В большинстве случае, при наличии нужных индексов, оптимизатор базы данных правильно выбирает необходимые индексы и делает соединение. Однако, в некоторых случаях может быть ситуация когда нужно явно указать использование индекса, для этого в запросах есть специальная команда %_HINTS. В случае базы данных Oracle при выборе из таблицы MARA, например, нужно принудительно использовать индекс с именем «Т», тогда запрос будет иметь следующий вид:

e6) SELECT * UP TO 10 ROWS FROM maraWHERE MTART = 'HAWA' AND

Страница 14 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 15: Оптимизация ABAP v1-2

MATKL = '100'%_HINTS ORACLE 'index(mara"T")'.

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

Выводы (скорость): Если вы не уверены, в том, что делаете, лучше расширение типа %_HINTS, в запросах не использовать, но если решили применить такую конструкцию, то, как минимум, сообщите об этом администратору базы данных.

Из опыта соединения таблиц и подзапросов, как известно, при больших объемах данных, вложенные запросы существенно замедляют быстродействие основного. Однако была задача, выбрать документы материала с исключением из выборки сторнированных документов. Исходя из структуры данных и того как система хранит признак сторно, это довольно не простая задача, при большом объеме документов, более > 1.000.000 позиций, так как в позиции прямого документа в таблице MSEG нет ссылки на сторнирующий. Поэтому построенный обычный запрос по загрузке данных во внутреннюю таблицу, с последующим отсечением сторнированных документов, в цикле по полученным записям используя единичную выборку из MSEG (SELECT SINGLE ...) и при положительном ее результате (sy-subrc = 0) – удаление позиции из внутренней таблицы, оказался неприемлемым. Система падала в дамп по таймауту. Поэтому, в этом случае, подзапросы оказались более оптимальными, чем локальная обработка внутренних таблиц. Конечный запрос имел такой вид.

f6) SELECT mseg~mblnr mseg~mjahr mseg~zeile mseg~bwart mseg~xauto mseg~matnr mseg~werks mseg~lgort mseg~insmk mseg~shkzg mseg~dmbtr mseg~bwtar mseg~menge mseg~bustm mkpf~budat

INTO TABLE lt_msegFROM mseg JOIN mkpf ON mkpf~mblnr = mseg~mblnr AND

mkpf~mjahr = mseg~mjahrWHERE mseg~matnr IN s_matnr

AND mseg~werks IN s_werks AND mseg~lgort IN s_lgort AND mseg~sobkz = ' ' AND mseg~smbln = ' ' AND mkpf~budat IN r_date AND NOT EXISTS ( SELECT * FROM *mseg WHERE smbln = mseg~mblnr AND sjahr = mseg~mjahr AND smblp = mseg~zeile ).

Вот такой вот частный случай оптимизации с использованием подзапросов. Вполне возможно, что на вашей системе этот запрос может быть не оптимальным.

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

Страница 15 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 16: Оптимизация ABAP v1-2

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

Соединение большой таблицы с относительно маленькой. Такие задачи обычно возникают часто при построении отчетов. Наиболее типичная ситуация, например, у нас есть позиции документа материала. В каждой позиции есть код единицы измерения. Клиент попросил, чтобы в отчете была колонка с полным текстом наименования единицы измерения. Варианты как это можно реализовать следующие.

* Общие данные для обработкиDATA: BEGIN OF ls_t006a, msehi LIKE t006a-msehi, mseht LIKE t006a-mseht, msehl LIKE t006a-msehl,END OF ls_t006a.

DATA: BEGIN OF lt_t006a OCCURS 0, INCLUDE STRUCTURE ls_t006a.DATA: END OF lt_t006a.

DATA: BEGIN OF lt_mseg_short OCCURS 0, mblnr LIKE mseg-mblnr, mjahr LIKE mseg-mjahr, zeile LIKE mseg-zeile, bwart LIKE mseg-bwart, matnr LIKE mseg-matnr, menge LIKE mseg-menge, meins LIKE mseg-meins, mseht LIKE t006a-mseht, msehl LIKE t006a-msehl,END OF lt_mseg_short.

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

ВремВып: 10.677.944 микросек. g6) CLEAR: lt_mseg_short[], lt_t006a[].

SELECT mblnr mjahr

zeilebwartmatnrmengemeins

INTO TABLE lt_mseg_shortFROM mseg.

LOOP AT lt_mseg_short.SELECT SINGLE mseht msehl INTO CORRESPONDING FIELDS OF ls_t006aFROM t006a WHERE spras = sy-langu AND

msehi = lt_mseg_short-meins.

lt_mseg_short-mseht = ls_t006a-mseht.lt_mseg_short-msehl = ls_t006a-msehl.MODIFY lt_mseg_short.

Страница 16 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 17: Оптимизация ABAP v1-2

ENDLOOP.

Сделаем то же самое, но теперь используем соединение таблиц MSEG и T006A, код будет значительно короче и выполняться будет быстрее

ВремВып: 6.795.740 микросек. h6) CLEAR: lt_mseg_short[], lt_t006a[].

SELECT m~mblnrm~mjahrm~zeilem~bwartm~matnrm~mengem~meinst~msehtt~msehl

INTO TABLE lt_mseg_shortFROM mseg AS m

JOIN t006a AS t ON t~spras = sy-langu AND t~msehi = m~meins.

Значительно лучше, но данные результаты можно еще улучшить, для этого модифицируем запрос G6. Путем вынесения чтения списка единиц измерения в отдельный запрос и затем в цикле будем читать данные из внутренней таблицы.

ВремВып: 5.563.088 микросек. i6) CLEAR: lt_mseg_short[], lt_t006a[].

SELECT mblnrmjahrzeilebwartmatnrmengemeins

INTO TABLE lt_mseg_shortFROM mseg.

SELECT msehimsehtmsehl

INTO TABLE lt_t006aFROM t006aWHERE spras = sy-langu.

LOOP AT lt_mseg_short. READ TABLE lt_t006a WITH KEY msehi = lt_mseg_short-meins.

lt_mseg_short-mseht = ls_t006a-mseht.lt_mseg_short-msehl = ls_t006a-msehl.MODIFY lt_mseg_short.

ENDLOOP.

Как видим скорость выполнения этого запроса самая минимальная. Однако есть еще один вариант решения данной задачи, состоящий в том, чтобы объединить варианты G6 и I6.

ВремВып: 5.889.013 микросек. j6) CLEAR: lt_mseg_short[], lt_t006a[].

SELECT mblnrmjahrzeilebwart

Страница 17 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 18: Оптимизация ABAP v1-2

matnrmengemeins

INTO TABLE lt_mseg_shortFROM mseg.

LOOP AT lt_mseg_short.READ TABLE lt_t006a WITH KEY msehi = lt_mseg_short-meins.

IF sy-subrc <> 0.CLEAR: ls_t006a.SELECT SINGLE msehi mseht msehl INTO ls_t006aFROM t006a WHERE spras = sy-langu AND

msehi = lt_mseg_short-meins.APPEND ls_t006a TO lt_t006a.lt_mseg_short-mseht = ls_t006a-mseht.lt_mseg_short-msehl = ls_t006a-msehl.

ELSE.lt_mseg_short-mseht = lt_t006a-mseht.lt_mseg_short-msehl = lt_t006a-msehl.

ENDIF.MODIFY lt_mseg_short.

ENDLOOP.

Т.е. суть выборки заключается в том, чтобы выполнять выбор единиц измерения из базы данных, только в том случае, если во внутренней таблице измерения нет нужной нам величины. В данном случае этот вариант оказался немного медленнее, чем вариант I6, но это связано с тем, что тесты делаются на системе IDES и в проводках документов материала из возможных 260 единиц измерения используются 76. В большинстве же реальных систем, наверное, количество вариантов использования единиц измерения будет меньше, соответственно количество запросов SELECT SINGLE xxxx FROM T006A будет тоже меньше и вполне возможно, что запрос J6 будет самым быстрым. Это как раз тот самый случай, когда нужно моделировать запросы на клиентских данных. Так же способ обработки I6 будет более быстрым, если справочными таблицами, будут такие таблицы как основные записи материала или дебиторов/кредиторов, тогда выигрыш будет очень значительным, так как обычно, в периоде работы редко используются все существующие в системе коды материалов или дебиторов/кредиторов.

Примечание: В данном случае, при чтении данных в операторе READ TABLE не использовалось расширение BINARY SEARCH, так как таблица lt_t006a не была объявлена как сортированная, а для не сортированных данных, использовать такой поиск нельзя. Более подробно о том, как идет выбор данных, из внутренних таблиц, рассмотрим в части, где будет описаны способы объявления таких таблиц.

Выводы (скорость): При соединении большой таблицы со справочниками таблицами, содержащими относительно основной таблицы минимальный набор значений, более оптимальным будет использование отдельного чтения значений справочников во внутренние таблицы с последующей ручной обработкой соединения в цикле LOOP AT <xxxx>. ENDLOOP. Если же использовать объединение основное таблицы, со справочными, используя оператор JOIN в одном запросе выбора данных, то это может немного замедлить выбор данных, хотя и не на много, так как обычно справочные таблицы буферизированы. Проверить буферизацию таблиц можно используя транзакцию SE13.

Выводы (память): С точки зрения оптимизации памяти, более выгодным будет использование хранения справочных данных в отдельных внутренних таблицах, без

Страница 18 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 19: Оптимизация ABAP v1-2

использования объединения чтения данных одним запросом в большую внутреннюю таблицу, содержащую все необходимые данные. Примеры такого чтения показаны в запросах I6 и J6, где данные справочника текстов единиц измерений, лежат в отдельной внутренней таблице.

7. COMMIT WORK.

COMMIT WORK — В общем виде, производит фиксирование измененных или добавленных данных в базу данных. В системе, есть два варианта использования COMMIT, собственно сам COMMIT WORK и есть еще специальный функциональный модуль «DB_COMMIT». Функциональный модуль вызывает операцию фиксирования изменений на уровне базы данных, собственно говоря, там используется внутренний запрос к базе данных, используя блок:

EXEC SQL. COMMIT WORKENDEXEC.

При этом в случае ошибки применения изменений, вы не можете получить никакие сообщения, т.е. этот ФМ отработает, но вот узнать, как произошла операция и в случае ошибки выполнить ROLLBACK уже будет невозможно. Поэтому в системе введена специальная команда COMMIT WORK, которая на самом деле выполняет не только фиксация измененных данных, но и выполняет еще кучу полезных действий:

• Выполняет запуск всех подпрограмм, которые вызываются с использованием инструкций вида PERFORM <имя подпрограммы> ON COMMIT и всех функциональных модулей объявленных как CALL FUNCTION <имя модуля> IN UPDATE TASK.

• Вызывает инициирование событий для службы Object Services. Поэтому если у вас есть фоновые процессы, запускающиеся по событию, то именно эта операция генерирует все заявленные в блоке обработки события.

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

• Закрывается текущий LUW блок и все курсоры на уровне базы данных.• Вызывается событие TRANSACTION_FINISHED, что устанавливает для системного

класса CL_SYSTEM_TRANSACTION_STATE значения COMMIT_WORK.

При массовых вставках или изменениях данных, не следует использовать COMMIT WORK, после каждой вставки или изменения данных. Так же не следует заливать все данные и делать один общий COMMIT на все вставленные/измененные записи. Оптимальным, обычно является фиксация обновления/вставки через 200-500-1000 обработанных строк, по факту от 1% до 3% от объема всех данных. К сожалению, это правило часто не работает с функциями BAPI, которые не являются повторно входимыми, т. е. нельзя вызвать, например проводку документа ММ, не сделав фиксацию или отмену (ROLLBACK) предыдущего вызова этой же функции проводки документа. Поэтому, так как в таком случае ничего не поделаешь, то после каждого вызова будем делать COMMIT. Пример такой функции это проводка документа материала: BAPI_GOODSMVT_CREATE. Хотя если функция допускает такую возможность, то ее нужно использовать, например функция BAPI_FIXEDASSET_CHANGE - Изменение карточек ОС, позволяет делать COMMIT на группу вызовов этой функции.

Страница 19 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 20: Оптимизация ABAP v1-2

Из практики нужно было изменить порядка ~20 000 карточек, в программке случайно сбился счетчик, в итоге COMMIT вызвался только после обновления всех карточек ОС. К чему это привело, во-первых, каждая следующая запись карточки ОС, обновлялась все медленнее и медленнее, так как рос лог транзакции, во-вторых, когда лог вырос до 400 Мб наступил COMMIT, и вот конца уже этого процесса никто не дождался, так как 3 часа выполнения COMMIT-а и было принято решение, удалить процесс. Далее подправили программку, чтобы COMMIT был через 200 записей и в итоге ~20 000 карточек обновилось всего минут за 40.

Выводы (скорость): При массовых обновлениях записей стараемся фиксировать изменения не через каждую запись и не для всего блока изменений одним вызовом COMMIT. Находим оптимальное значение, например для 20 000 записей, оптимальным было делать фиксация через 200 вставленных/измененных записей, т.е. из этого следует правило, обрабатываем от 1% до 3% записей, после чего выполняем COMMIT WORK.

Другая ситуация с COMMIT возникает когда выполняем создание сложных объектов системы, например проводка документа материала используя ранее названную функцию BAPI: BAPI_GOODSMVT_CREATE. При этом система фактически делает создание всех связных документов, которые порождаются в зависимости от настройки системы и используемого функционала, ну как минимум для оцениваемых материалов создается финансовый документ. При этом, это уже техническая реализация процесса в SAP, но ваша программа получит статус, что обновление выполнено фактически еще до того, как данные будут записаны физически в базу данных. Чем это грозит, ну скажем, попытка прочитать сразу же после COMMIT-а проведенный документ закончится ошибкой чтения, документа с номером ХХХХХХХ не найден.

Какой выход из этой ситуации, ну скажем там часто имеющий место быть вариант, установка задержки в секунду или полсекунды после каждого COMMIT, мне кажется, является очень грубой ошибкой, особенно при массовой обработке данных. Например, мы обрабатываем 500 000 записей. Если после каждой будет установлена задержка в 1 секунду, то это значит, что время работы программы будет растянуто на 5 с лишним суток, хотя при нормально проверке используя функции блокировки скорость обновления данных 15-20 записей в секунду. Поэтому общая обработка вместо 5 суток занимает всего 7 с половиной часов, по факту было около 6. Как видим оптимизация быстродействия на лицо. Как это выполнить, в системе? Практически для всех объектов, есть понятие записи блокирования объекта. Запись блокирования это фактически специальный признак устанавливаемый системой, и сообщающий о том, что запись кем-то изменяется (техническую реализацию рассматривать не будем). Блокирование делается для того, чтобы исключить конфликт одновременного изменения объекта. Для работы с записями блокирования есть специальные функции, поэтому чтобы не лезть в таблицу блокировок запросами, можно воспользоваться уже готовыми реализациями. Общий алгоритмы работы будет следующий, после каждого обновления объекта, пытаемся его заблокировать. Если блокировка прошла успешно значит, объект уже полностью прошел изменения, блокировку можно снять и далее запросы к таблицам, будут возвращать, что записи есть в базе данных.

Для использования объектов блокирования, сначала нужно посмотреть какой объект используется. Например, возьмем карточки ОС. Самое просто это зайти в изменения любой карточки ОС, транзакция AS02, а затем в другом режиме запустить транзакцию SM12. Если у вас больше ничего в других окнах не открыто на изменение, тогда там будет одна запись

Страница 20 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 21: Оптимизация ABAP v1-2

блокирования, для карточки ОС. Делаете двойной клик на этой записи и получаете детальную информацию по объекту блокирования, рисунок 4: SM12V.png.

Рисунок 6: SM12V.png

Нас интересует код объекта, на рисунке он выделен рамкой, это и есть объект блокировки. Теперь нужно пойти в транзакцию SE11, чтобы узнать функциональные модули управления блокировкой. Вводим полученный объект блокирования, как на рисунке 5: SE11-1.png, далее жмем просмотр объекта.

Страница 21 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 22: Оптимизация ABAP v1-2

Рисунок 7: SE11-1.png

Мы попадаем в экран просмотра, рисунок 6: SE11-2.png и там по меню: "Перейти к" – "Модули блокировки", получаем две функции, одна для блокирования объекта основное средство, а другая для деблокирования, рисунок 7: SE11-3.png.

Страница 22 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 23: Оптимизация ABAP v1-2

Рисунок 8: SE11-2.png

Страница 23 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 24: Оптимизация ABAP v1-2

Рисунок 9: SE11-3.png

Ну а дальше дело техники. После вызова функции создания/обновления карточки ОС, если параметр BAPIRET2 не содержит ошибок, т.е. например создание карточки прошло успешно, то далее вставляем код попытки блокирования на карточку ОС:

CALL FUNCTION 'ENQUEUE_EANLA' EXPORTING bukrs = l_bukrs anln1 = l_anln1 anln2 = l_anln2 _wait = 'X' EXCEPTIONS foreign_lock = 1 system_failure = 2 OTHERS = 3.IF sy-subrc = 0.* Запись находится в БД, блокировку можно снять и бежать дальше. CALL FUNCTION 'DEQUEUE_EANLA' EXPORTING bukrs = l_bukrs anln1 = l_anln2 anln2 = l_anln3.ELSE.* Системная ошибка, пишем лог-обработки для дальнейшего анализа.ENDIF.

Т.е. система будет пытаться поставить блокировку на созданный/измененный объект, если блокировка установилась, то мы ее тут же снимаем и идем обрабатывать следующие данные, так как запись точно уже существует в базе данных, если же получили ошибку, то такой

Страница 24 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 25: Оптимизация ABAP v1-2

объект не обрабатываем, скорее всего, какие-то проблемы на уровне базиса. Как работает флаг «_wait = 'X'»? В системе на уровне параметра enque/delay_max, задается количество попыток установки блокировки. По умолчанию это значение установлено = 0005, код который выполняется примерно такой:

if waitflag is initial. times = 1.

else. delonrej = 1.

call 'C_ENQ_WILDCARD' id 'DELAY_MAX' field times.endif.

И далее:

DO time TIMES.call 'C_ENQUEUE'… <далее куча параметров>

ENDDO.

Т.е. будет попытка вызвать системную функцию блокировки 5 раз, если блокировка была отклонена, система делает следующую попытку установить блокировку, при этом время задержки перед повторным вызовом будет примерно равно 1 секунда, как говорит нам документация SAP, после 5 попыток будет выдана ошибка установки блокировки объекта. Посмотреть, как оно именно работает можно в модуле LSENAF01, где-то со строки 230, для версии системы 6.0

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

8. Определение внутренних таблиц.

8.1. Организация внутренних таблиц.

В основной памяти, внутренние таблицы, как и таблицы базы данных, организованы страницами. При объявлении внутренней таблицы в программе ABAP, система создает только ссылку, без создания каких-либо начальных структур в памяти. Все объекты, описывающие и поддерживающие организацию данных внутренней таблицы, создаются при первой вставке данных, т.е. создается заголовок таблицы, страницы данных и если это сортированная или хэш-таблица, то вспомогательные структуры поддержки таких таблиц.

Заголовок таблицы содержит важную информацию о внутренней таблице, например это ссылка на первую страницу данных внутренней таблицы, ссылку на специальный блок данных, который называется – страница управления. Данная страница содержит информацию о физических адресах в памяти второй и всех последующих страниц данных. Далее в заголовке содержится общее количество записей, которое содержится в таблице, эту информацию можно получить, используя конструкцию DESCRIBE TABLE <tab_name> LINES <lines> и т.д. Кстати это число 4 байта, что автоматически ограничивает количество строк в таблице значением 2,147,483,647 записей. Ссылка на объявленную в программе внутреннюю таблицу в настоящее время занимает 8

Страница 25 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 26: Оптимизация ABAP v1-2

байт. Заголовок таблицы занимает около 100 байт памяти в зависимости от платформы. Пространство, необходимое для страницы управления, является динамическим и зависит от количества выделенных страниц под данные.

И так, данные хранятся в блоках называемых страницами. Каждая страница содержит от одной и более записей данных внутренней таблицы. Система выделяет память сразу для всей страницы, для страниц с номерами 3 и более. Для страниц с номерами 1 и 2 память выделяется по другому, эти страницы физически могут занимать места, менее, максимального размер страницы, т.е. менеджер памяти для страниц 1 и 2 определяет их размер в зависимости от хранящихся данных, остальные страницы будут кратны 8 или 16 кб. Размер в 8 или 16 кб для страницы, выбирается в зависимости от длины записи данных, которые будут храниться, рисунок 10: Easy-tbl-1.png.

Рисунок 10: EASY-TBL-1.png

В отличие, от доступа к данным в таблицах базы данных, где данные читаются страницами, доступ к данным внутренней таблицы идет по строкам, т.е. если например, требуется получить данные записи с номером Z, система будет считывать из памяти только запись с этим номером, а не считывать всю страницу данных. Прямой доступ к записи может идти или по индексу или используя хэш-функцию. Более подробно о доступе к данным будет написано ниже.

Ранее в предыдущих версиях при объявлении таблицы использовалось описание OCCURS <size>, где переменная <size> управляла тем, каким образом система будет выделять страницы для хранения данных во внутренней таблице. Общая рекомендация была следующей, если предполагается загружать в таблицу много данных, то это число должно быть большим, таким образом, система резервирует и выделяет при каждом запросе, сразу большое число страниц для данных, что уменьшает затраты на работу с памятью при ее выделении. С версии 4.6 этот параметр остался для совместимости и на самом деле уже не управляет способом выделения страниц, на данный момент при определении внутренней таблицы, введен новый параметр: INITIAL SIZE <max_rec>. Большинство разработчиков, как мне кажется, не используют данный параметр, при объявлении внутренних таблиц,

Страница 26 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 27: Оптимизация ABAP v1-2

считая его не обязательным, или не зная, на что он особо влияет. Так вот, использование этого параметра, напрямую завязано с оптимизацией памяти и определяет, сколько места, система выделяет для первой и если нужно второй динамической страницы данных. Число <max_rec>, задает предполагаемое количество записей данных, которое мы считаем, будет храниться во внутренней таблице. Таким образом, зная размер одной записи данных, который равен длине всех полей включенных в объявление внутренней таблицы и умножив это число на <max_rec>, система получает размер необходимый для первой страницы данных и этот размер часто может быть меньше, чем выделяемая по умолчанию память для такой первой страницы. Так же очень важно не ошибиться при задании данного значения. В главе описывающей объединение таблиц в запросах, есть пример J6, в котором предполагается использовать внутреннюю таблицу для хранения данных по коду + описание единицы измерения. В общем виде внутренняя таблица в данном примере была объявлена следующим образом:

* Общие данные для обработки (рабочая область)DATA: BEGIN OF ls_t006a, msehi LIKE t006a-msehi, mseht LIKE t006a-mseht, msehl LIKE t006a-msehl,END OF ls_t006a.

* Общие данные для обработки (таблица хранения данных)DATA: BEGIN OF lt_t006a OCCURS 0, INCLUDE STRUCTURE ls_t006a.DATA: END OF lt_t006a.

Три поля в общем виде занимают ~43 байта для базы без использования юникода или ~86 байт, если база в кодировке юникода. Всего в таблице базы данных содержится описание 260 единиц измерения, т.е. для хранения всех единиц измерения в системе юникода потребуется около 22 кб, однако если мы знаем, что в нашей системе используется, например всего 20 различных единиц измерения, то для их хранения достаточно всего 1,7 кб памяти. Поэтому если объявить данную таблицу с параметром INITIAL SIZE 20, система выделит 1,7 кб памяти под первую страницу, ровно столько, сколько надо для хранения этих 20 записей, что значительно меньше выделяемых по умолчанию размеров под данные. Однако, тут есть опасность ошибиться при указании этого параметра, например мы задали количество записей в 20, а на самом деле их оказалось как в приведенном выше примере 76, что по расчетам потребует порядка 6.4 кб памяти, однако на самом деле памяти будет выделено значительно больше, так как исходя из запрошенных 20 записей, система выделит под первую страницу 1,7 кб. Далее на второй динамической странице данных, будет выделено место под хранение в два раза большего числа записей, чем было затребовано, для первой страницы данных. Это место для хранения 40 записей или 3.4 кб, но так как еще есть 16 записей из требуемых 76, то под их хранение уже будет выделана третья, фиксированная страница размером 8 или 16 кб. Таким образом, система вместо требуемых 6.4 кб, под хранение данных такой внутренней таблицы, будет использовать 1,7 кб + 3,4 кб + 8 (16) кб = 13,1 (21,1) кб памяти, т.е. имеем увеличение затрат по памяти от 2 до 3 раз. Конечно, размеры в килобайтах кажутся мелочью, какая разница 6 или 26 кб, но посчитайте, сколько таких небольших внутренних таблиц объявляется в вашей средней разработке.

Выводы (память): Для оптимизации памяти, если вы используете небольшие внутренние таблицы, всегда обязательно указывайте предполагаемое максимальное количество записей, которое будет загружаться в такие таблицы, используя команду INITIAL SIZE. Используя параметр размера числа записей во внутренней таблице, лучшим решением, будет задать чуть большее значение по количеству хранимых данных.

Страница 27 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 28: Оптимизация ABAP v1-2

Примечание: Вспоминая старый анекдот, про атомный взрыв мощностью от 20 до 50 килотонн и на вопрос как же это такой разброс в цифрах вышел, был получен ответ, что думали, будет 20, но оно как бабахнет. Хочется напомнить, что вы работаете с данными и тут надо не просто думать, что будет 20, но надо еще и уметь считать, причем хотя бы в рамках знаний арифметики за 1-3 класс начальной школы, иначе вместо оптимизации, получим наоборот увеличенное потребления памяти.

8.2. Индексы внутренних таблиц.

Практически по налоги с базами данных, система выполняет формирование индексов для быстрой работы с записями данных внутренних таблиц, правда в отличие от баз данных, управлять процессами формирования и типами создаваемых индексов мы не можем, система выполняет это автоматически. Например, индекс создается только в том случае, если физическая последовательность данных, не соответствует логической последовательности. Фактически автоматическое создание индекса происходит в следующих случаях:

• INSERT – Выполнена вставка записи в произвольное место внутренней таблицы. Кстати, если запись вставлена в конец таблицы, по аналогии с оператором APPEND, то логическая последовательность считается не нарушенной и индекс не создается.

• DELETE – Выполнено удаление любой произвольной записи в таблице, кроме последней записи.

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

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

Так же автоматически система выбирает тип создаваемого индекса. На данный момент возможны два варианта построения:

• Линейный индекс• Древовидный индекс

И так в общем виде к рисунку 10: EASY-TBL-1.png, система строит индексную мини-таблицу на которую прописывает ссылку из заголовка внутренней таблицы, рисунок 11: INDEX-TBL-1.png. Мини-таблица состоит из двух частей. Первая содержит список записей удаленных оператором DELETE, а вторая содержит логически упорядоченные записи таблицы и ссылки на физическое размещение требуемых записей на страницах данных. Таким образом, при вставке записей, идет упорядочивание только индексной таблицы, а не переопределение данных на страницах. Индексная таблица не содержит пробелов. Когда идет вставка записи, система проверяет список свободных записей, если такая запись найдена, то данные вставляются на место найденной записи, и такой блок переносится из списка свободных в список упорядоченных записей индекса с последующей пересортировкой данных списка индекса.

Страница 28 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 29: Оптимизация ABAP v1-2

Рисунок 11: INDEX-TBL-1.png

Так выглядит таблица с линейным индексом данных. При увеличении размера внутренней таблицы система может посчитать, что оптимальным, будет не линейный, а древовидный индекс, так как накладные расходы на упорядочивание записей линейного индекса считаются уже большими. Поэтому, в какой-то момент времени, система выполнит перестроение такого индекса из линейного в древовидный. Исходя из полученной информации, дерево будет построено не бинарным, а похоже будет использоваться так называемое B-дерево, рисунок 12: Index-Tree-1.png,

Примечание: Кому интересна теория, то по минимуму о B-деревьях прочитать можно тут http://ru.wikipedia.org/wiki/B-tree.

Для больших объемов данных, индекс, построенный по принципу B-дерева, будет более быстрым при вставке или удалении записей из произвольной позиции внутренней таблицы. Недостатком древовидного индекса, по сравнению с линейным, будет увеличение памяти на его хранение. В среднем, на древовидный индекс, требуется на 50% больше памяти, чем на аналогичный по размеру линейный. В данном случае разработчики на ABAP, не управляют вариантами построения внутреннего индекса к внутренней таблице. Система сама определяет, то какой индекс строить, отталкиваясь от размеров внутренней таблицы, количества записей и еще каких-то своих внутренних параметров оценки производительности выбранного метода построения внутреннего индекса.

Страница 29 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 30: Оптимизация ABAP v1-2

Рисунок 12: Index-Tree-1.png

Выводы (скорость): Для ускорения работы с внутренними таблицами, система строит внутренний индекс к таблицам. Построение индекса инициируется операциями INSERT или DELETE из произвольной позиции таблицы или же операцией SORT, т.е. когда логическая последовательность данных перестает совпадать с физической. Далее для каждой последующей операции система кроме вставки данных на страницы хранения, выполняется автоматическая поддержка актуальности созданного индекса. Если индекс начинает формироваться или перестраиваться с одного типа на другой, для большой внутренней таблицы, это может вызвать замедленнее операции вставки или удаления, которая привела к перестроению индекса.

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

8.3. Работа с внутренними таблицами.

Далее рассмотрим варианты работы с выбранными данными с различными типами внутренних таблиц. Итак, таблица может быть объявлена как стандартная, стандартная с заголовком, сортированная и так называемая хеш-таблица. С точки зрения хранения данных последние две являются таблицами с сортированными строками, но при этом таблица типа хэш должна иметь уникальный ключ, а вот таблица типа сортированная допускает наличие

Страница 30 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 31: Оптимизация ABAP v1-2

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

Для начала проверим влияние чтения данных в различные типы внутренних таблиц. Например, нужно сделать выборку позиций заказов на закупку, таблица EKPO. Варианты объявления данных следующие:

DATA: lt_ekpo_sort LIKE SORTED TABLE OF ekpo WITH UNIQUE KEY ebeln ebelp,lt_ekpo_hash LIKE HASHED TABLE OF ekpo WITH UNIQUE KEY ebeln ebelp,

lt_ekpo_standart LIKE ekpo OCCURS 0, lt_ekpo_header LIKE ekpo OCCURS 0 WITH HEADER LINE.

Выборка данных делалась следующим образом.

ВремВып: 4.089.295 микросек. A8) SELECT * INTO TABLE lt_ekpo_sort

FROM ekpo.

ВремВып: 4.147.068 микросек. B8) SELECT * INTO TABLE lt_ekpo_header

FROM ekpo.

ВремВып: 4.022.872 микросек. C8) SELECT * INTO TABLE lt_ekpo_standart

FROM ekpo.

ВремВып: 4.203.968 микросек. D8) SELECT * INTO TABLE lt_ekpo_hash

FROM ekpo.

Каждый запрос выполнялся 100 циклов, при этом после 10 и 50 циклов результаты были разными для разных типов таблиц. Однако, при увеличении числа прогонов общее время выполнения, судя по всему, будет практически одинаковым, т.е. операции чтения из базы данных, всегда дольше, по времени, чем вставка прочитанных данных во внутреннюю таблицу для любого типа. Проверка была выполнена на таблице EKPO, общий объем считанных данных в районе 30 000 записей. Так же, тест прогонялся на таблице MSEG, в которой находилось около 125 000 записей, результат на большом числе итераций 50 и 100, так же не зависел от типа внутренней таблицы, в которую производилась выборка, что в принципе подтверждают правила организации внутренних таблиц.

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

Стандартные таблицы – внутренняя таблица, представляющая собой список строк данных, размещенных в памяти, при этом, каждая следующая строка при выборе, будет добавлена в конец уже существующих данных, по аналогии с добавлением записей оператором APPEND, по факту поступления или же оператором INSERT в требуемую позицию внутри внутренней таблицы. Не забываем, что первая операция INSERT в

Страница 31 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 32: Оптимизация ABAP v1-2

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

Для доступа к записям/строкам такой таблицы, используют прямой доступ по номеру записи или перебор в цикле. Исходя из документации доступ по номеру записи наиболее быстрый, но если номер записи не известен, то, будет использоваться, прямой перебор пока нужная запись будет не найдена, поиск, используя конструкцию BINARY SEARCH, не работает, так как в общем виде данные таблицы не сортированные. Соответственно, время поиска пропорционально количеству записей в такой таблице, так как в самом худшем случае приходится пройтись по всей таблице. Как вариант ускорения доступа к данным такой таблицы, можно вызвать операцию сортировки данных с последующим использованием, бинарного поиска (BINARY SEARCH), записей для уже отсортированной таблицы. Правда при этом не следует забывать, что сортировка больших внутренних таблиц данных сама по себе требует иногда значительного времени, хотя если мы заполняем внутреннюю таблицу сами, то можно поддерживать упорядоченность записей в ручном режиме. Для примера можно рассмотреть пример кода J6, где мы заполняем таблицу справочник кодов единиц измерения. Суть метода базируется на том, что при использовании бинарного поиска, если запись не найдена во внутренней таблице, то системная переменная SY-TABIX, содержит номер позиции, в которой должна была бы находиться такая запись. Поэтому код будет выглядеть следующим образом:

* Общие данные для обработкиTYPES: BEGIN OF tp_t006a, msehi LIKE t006a-msehi, mseht LIKE t006a-mseht, msehl LIKE t006a-msehl,END OF tp_t006a.

DATA: ls_t006a TYPE tp_t006a, lt_t006a TYPE TABLE OF tp_t006a, lt_t006a_st TYPE SORTED TABLE OF tp_t006a WITH UNIQUE KEY msehi.

DATA: BEGIN OF lt_mseg_short OCCURS 0, mblnr LIKE mseg-mblnr, mjahr LIKE mseg-mjahr, zeile LIKE mseg-zeile, bwart LIKE mseg-bwart, matnr LIKE mseg-matnr, menge LIKE mseg-menge, meins LIKE mseg-meins, mseht LIKE t006a-mseht, msehl LIKE t006a-msehl,END OF lt_mseg_short.

ВремВып 157.575 микросек. e8) LOOP AT lt_mseg_short. READ TABLE lt_t006a INTO ls_t006a

WITH KEY msehi = lt_mseg_short-meins BINARY SEARCH. IF sy-subrc <> 0. CLEAR: ls_t006a. SELECT SINGLE msehi mseht msehl INTO ls_t006a FROM t006a WHERE spras = sy-langu AND msehi = lt_mseg_short-meins. INSERT ls_t006a INTO lt_t006a INDEX sy-tabix. ENDIF. lt_mseg_short-mseht = ls_t006a-mseht. lt_mseg_short-msehl = ls_t006a-msehl.

Страница 32 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 33: Оптимизация ABAP v1-2

MODIFY lt_mseg_short.ENDLOOP.

Т.е. после использования бинарного поиска (при первом прохожее таблица единиц измерения пустая), если запись не найдена, то SY-TABIX будет содержать номер позиции, куда нужно вставить новую запись. Как следствие, таким образом, мы поддерживаем упорядоченность таблицы, используем бинарный поиск и ускоряем обработку цикла. Без бинарного поиска, тот же цикл работает немного дольше, пример F8, ниже.

ВремВып: 164.022 микросек. f8) LOOP AT lt_mseg_short. READ TABLE lt_t006a INTO ls_t006a

WITH KEY msehi = lt_mseg_short-meins. IF sy-subrc <> 0. CLEAR: ls_t006a. SELECT SINGLE msehi mseht msehl INTO ls_t006a FROM t006a WHERE spras = sy-langu AND msehi = lt_mseg_short-meins. APPEND ls_t006a TO lt_t006a. ENDIF. lt_mseg_short-mseht = ls_t006a-mseht. lt_mseg_short-msehl = ls_t006a-msehl. MODIFY lt_mseg_short.

ENDLOOP.

Конечно, скорость обработки отличается не в разы, но на больших таблицах это может дать дополнительные проценты скорости выполнения вашего приложения. Кстати, обнаружился интересный эффект, скорость доступа к данным стандартной таблицы, для которой поддерживается ручная упорядоченность набора и используется бинарный поиск, выше, чем скорость доступа к данным такой же, но объявленной как сортированная таблица.

ВремВып: 164.925 микросек. g8) LOOP AT lt_mseg_short. READ TABLE lt_t006a_st INTO ls_t006a

WITH KEY msehi = lt_mseg_short-meins. IF sy-subrc <> 0.

CLEAR: ls_t006a. SELECT SINGLE msehi mseht msehl INTO ls_t006a FROM t006a WHERE spras = sy-langu AND msehi = lt_mseg_short-meins. INSERT ls_t006a INTO lt_t006a_st INDEX sy-tabix. ENDIF. lt_mseg_short-mseht = ls_t006a-mseht. lt_mseg_short-msehl = ls_t006a-msehl. MODIFY lt_mseg_short.

ENDLOOP.

Эффект такой, по скорости наблюдается проигрыш даже прямому перебору, возможно это связано с тем, что внутренняя таблица содержит относительно мало данных и накладные расходы на обработку сортированной таблицы на таком объеме выше, чем для не сортированной. После прогона 100 циклов, можно сказать, что для небольшого объема данных использовать сортированную таблицу оказалось затратной операцией. В основном время выбора из сортированной таблицы равно скорости выбора из не сортированной, при этом скорость обработки для стандартной таблицы, отсортированной в ручном режиме, всегда была выше.

Сортированная таблица – таблица, которая содержит изначально упорядоченные записи,

Страница 33 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 34: Оптимизация ABAP v1-2

при чтении значений из таблицы базы данных, записи вставляются во внутреннюю таблицу согласно заданному ключу. При объявлении таблицы обязательно требуется задать поля, по которым будет выполняться сортировка и так же определить уникальный или не уникальный будет ключ для таблицы. Согласно документации SAP, для таких таблиц, всегда используется бинарный поиск – «…since the system always uses a binary search.». Скорость доступа к записям сортированной таблицы логарифмически пропорциональна количеству записей в таблице.

Примечание: Бинарный поиск, более известен как метод половинного деления, т.е. при наличии сортированных данных, на пример у нас есть таблица с одним ключевым полем, для простоты это поле типа целое число, пусть внутренняя таблица содержит следующие записи, таблица 1. Всего записей 14, нам нужно найти запись с ключем 95. Система выполняет деление количества записей в таблице пополам, т.е. ( 14 / 2 ) + 1 = 8 и читает запись следующую по индексу, т.е. в данном случае это запись с индексом 8. Значение ключа этой записи 67, так как это меньше чем искомое значение, то система будет обрабатывать вторую половину списка, т.е. оставшийся диапазон с 9 по 14, который тоже делится пополам, после чего выходим на запись с номером 12. Сверяем значение ключа 89, оно меньше, чем требуемое 95, поэтому снова делим оставшийся диапазон из двух записей пополам. Выходим на запись номер 14, значение ключа 100, больше чем 95, значит надо взять предыдущий интервал и делить уже пополам его. Однако, в этом интервале всего одна запись и ее ключ 95, значит индекс искомой записи 13. Если, искомый ключ, был, например 90, то мы получили бы ошибку, что запись с требуемы ключом не найдена. Потому что, оставшийся интервал состоит из одной записи и делить больше нечего. Таким образом, для поиска значения ключа потребовалось сделать 4 чтения, вместо 13 при поиске записи по последовательному перебору.

Таблица 1

№ Ключ Поле 1 1 2 3 Номер чтения записи

Результат

1 1 Aaaaaaaaaa2 2 Ccccccccccc3 7 Zzzzzzzzzzzzz4 15 Yyyyyyyyyyy5 23 Еееееееееее6 45 Wwwwwww7 50 Yyyyyyyyyy8 67 Qqqqqqqqq 1 ↓9 68 Rrrrrrrrrrrr

10 77 Bbbbbbbbb11 80 Uuuuuuuuu12 89 Ooooooooo 2 ↓13 95 Vvvvvvvvvvv 4 (нужная нам

запись)14 100 Kkkkkkkkkk 3 ↑

Хэш-таблицы – таблица, содержащие упорядоченные записи, которые сортируются автоматически при чтении значений из таблицы базы данных. Для хэш-таблиц всегда должен существовать уникальный ключ, т.е. в отличие от сортированных таблиц, не уникальные значения ключей не могут присутствовать во внутренней таблице. Скорость доступа к записям такой таблицы не зависит от количества записей в таблице, так как индекс записи фактически вычисляется по ключу, через хэш-функцию. Какой алгоритм расчета индекса по

Страница 34 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 35: Оптимизация ABAP v1-2

ключу используется системой, не известно. Так же, из за отсутствия исходных текстов, не известен алгоритм разрешения коллизий для хэш-функции (в общем виде, чем больше коллизий при расчете индекса выдает функция, тем медленнее идет чтение данных по ключу). Поэтому, остается только верить, что действительно, скорость доступа к записям хэш-таблицы по ключу, не зависит от количества записей в этой таблице – «The response time for key access remains constant». Обратной стороной «медали» будет размер, который занимает такая таблица в памяти.

Примечание: Как работает хэш-функция и что это вообще такое. В самом простом случае, считаем, что имеем черный ящик, на вход которого подается уникальный ключ записи, на выходе получаем индекс, строки в памяти, куда нужно вставить или откуда нужно прочитать запись. Для еще большего упрощения возьмем пример значений записей из таблицы 1 выше. Так как ключ у нас уникальное число, то пусть наша хэш-функция возвращает индекс записи = значение ключа. Это значит, что записи будут размещены во внутренней таблице следующим образом:

Таблица 2

№ Ключ Поле 11 AA Aaaaaaaaaa2 AB Ccccccccccc34567 CA Zzzzzzzzzzzzz89

101112131415 CB Yyyyyyyyyyy

Пустые записи с 16 по 22

23 DD ЕееееееееееПустые записи с 24 по 44

45 DF WwwwwwwПустые записи с 46 по 49

50 FD YyyyyyyyyyПустые записи с 51 по 66

67 GH Qqqqqqqqq68 HH Rrrrrrrrrrrr

Пустые записи с 69 по 76

77 HJ BbbbbbbbbПустые записи с 78 по 79

80 JJ UuuuuuuuuПустые записи с 81 по 88

89 KL OooooooooПустые записи с 90 по 94

95 LL VvvvvvvvvvvПустые записи с 96 по 99

100 ZZ Kkkkkkkkkk

Страница 35 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 36: Оптимизация ABAP v1-2

Как видим, если не принимать мер оптимизации, будет выделено памяти под все 100 записей, хотя по факту будет храниться всего 14, как и в случае с сортированной таблицей. Поэтому, чтобы оптимизировать память под хранением данных, система использует специальный внутренний индекс, пример на рисунке 13: Index-hash-1.png.

Рисунок 13: Index-hash-1.png

В целом все это работает следующим образом, на вход специальной программе реализующей хеш-функцию передается ключ, по ключу функция возвращает индекс записи во вспомогательной таблице ключей, например для записи с ключом DF, будет возвращен индекс 45. Далее, так как записи в таблице индексов имеют фиксированную длину, то можно быстро перейти на запись 45, в которой уже будет находится ссылка, на требуемую нам запись данных.

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

• Стандартные таблицы можно использовать как начальные накопительные буферы данных, т.е. если в любом случае вы читаете данные из базы данных, а затем выполняете полный проход по такой таблице по всем прочитанным записям. Однако, поиск значений по ключу, для таких таблиц, будет самым медленным и в конечном итоге приводит по факту к полному перебору значений таблицы с начала до конца, пока необходимая запись не будет найдена. При этом если количество запросов к несуществующим записям большое, то это вызывает постоянные полные проходы, при каждом запросе не существующего значения ключа, по всей внутренней таблице, что вызывает катастрофическое падении производительности. Если вы заполняете данные стандартной таблицы сами, то для использования конструкции BINARY SEARCH старайтесь поддерживать упорядоченность вставляемых записей, используя особенности инициализации системной переменной SY-TABIX, из примера E8.

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

Страница 36 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 37: Оптимизация ABAP v1-2

внутренней таблицы, по заданному ключу в случайном порядке или же доступ будет использоваться по части ключа для обработки групп записей. Однако, при быстром увеличении количества записей в сортированной таблице, скорость доступа по ключам будет падать, так как количество чтений записей, при поиске будет расти. Так же для сортированных таблиц следует избегать определения не уникальных ключей, на пример не имеет смысла делать сортированную таблицу по полю, для которого существует только два значения «space» и «X», после чего считывать в такую таблицу, например 10 000 записей.

• Хэш-таблицы, будут обеспечивать равномерный и максимально быстрый доступ к записям по ключу, не зависимо от количества записей в такой таблице, так как скорость расчета индекса нахождения записи по ключу, будет величиной постоянной. При этом время проверки отсутствия записи по ключу, будет такое же, как и проверка наличия записи. Поэтому, если у вас большое количество проверок на существование записей во внутренней таблице, при большом объеме обрабатываемых данных, то хэш-таблица очень неплохой вариант.

9. Работа с внутренними таблицами: LOOP, READ, DELETE и т.д.

LOOP AT <xxx>. ENDLOOP. Циклы по внутренним таблицам, казалось бы, что тут оптимизировать, есть таблица, есть ее построчная обработка, однако как оказалось и тут есть подходы к совершенству. Кстати, если использовать данную оптимизацию для запросов G6, J6 и I6, то скорость обработки еще возрастет по сравнению с простым соединением из примера H6. И так суть оптимизации заключается в использовании FIELD-SYMBOLS.

* Данные для работыDATA: lt_mseg_loop LIKE mseg OCCURS 1 WITH HEADER LINE,FIELD-SYMBOLS: <fs> LIKE LINE OF lt_mseg_loop.

ВремВып: 71.894 микросек. a8) SELECT * INTO TABLE lt_mseg_loop

FROM mseg.

LOOP AT lt_mseg_loop.

ENDLOOP.

ВремВып: 8.001 микросек. b8) SELECT * INTO TABLE lt_mseg_loop

FROM mseg.

LOOP AT lt_mseg_loop ASSIGNING <fs>.

ENDLOOP.

Как видим, простой проход по циклу, используя конструкцию с FIELD-SYMBOLS, дает разницу в скорости выполнения почти в 9 раз. Еще более существенные цифры получаются, если требуется выполнить какие-то обновления во внутренней таблице, используя конструкцию MODIFY для первого примера A8.

ВремВып: 193.230 микросек

Страница 37 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 38: Оптимизация ABAP v1-2

c8) SELECT * INTO TABLE lt_mseg_loopFROM mseg.

LOOP AT lt_mseg_loop.lt_mseg_loop-bwart = '000'.MODIFY lt_mseg_loop.

ENDLOOP.

ВремВып: 11.722 микросек. d8) SELECT * INTO TABLE lt_mseg_loop

FROM mseg.

LOOP AT lt_mseg_loop ASSIGNING <fs>.<fs>-bwart = '000'.

ENDLOOP.

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

Выводы (скорость): По возможности используйте при работе с внутренними таблицами, чтение данных через переменные, объявленные в FIELD-SYMBOLS, это существенно ускоряет работу циклов по внутренним таблицам. Негативом, в данном случае, может быть только то, что при модификации значений внутренней таблицы, нет явной команды типа MODIFY и как следствие нужно быть аккуратными, чтобы случайно не затереть данные таблицы, и потом долго искать, где же это произошло. При этом выигрыш будет тем больше, чем больше полей находится во внутренней таблице, т.е. если длина записи большая, то выигрыш будет очень существенным.

LOOP AT <xxx> WHERE <val>. ENDLOOP. Так же рассмотрим варианты, когда нужно пройти не по всем записям внутренней таблицы, а только по подходящим под условие WHERE. Тут тоже возможна оптимизация обработки в зависимости от типа объявленной таблицы. Выборку делаем в сортированную таблицу, так как такой вариант более быстрый с точки зрения загрузки данных.

Данные для обработки:DATA: itab TYPE SORTED TABLE OF bkpf

WITH UNIQUE KEY bukrs belnr gjahr.

FIELD-SYMBOLS: <itab> LIKE LINE OF itab.

ВремВып: 33.764 микросек. e8) LOOP AT itab ASSIGNING <itab>

WHERE bukrs EQ '1000' AND belnr CP '55*'.ENDLOOP.

ВремВып: 24 микросек. f8) READ TABLE itab TRANSPORTING NO FIELDS

WITH KEY bukrs = '1000'

Страница 38 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 39: Оптимизация ABAP v1-2

belnr = '5500000000'BINARY SEARCH.

LOOP AT itab ASSIGNING <itab> FROM sy-tabix.IF <itab>-belnr+0(2) <> '55'.

EXIT.ENDIF.

ENDLOOP.

ВремВып: 45 микросек. g8) READ TABLE itab TRANSPORTING NO FIELDS

WITH KEY bukrs = '1000'belnr = '5500000000'

BINARY SEARCH.LOOP AT itab ASSIGNING <itab> FROM sy-tabix

WHERE bukrs EQ '1000' ANDbelnr CP '55*'.

ENDLOOP.

ВремВып: 11 микросек.

h8) READ TABLE itab TRANSPORTING NO FIELDSWITH KEY bukrs = '1000'

belnr = '5500000000'BINARY SEARCH.

LOOP AT itab ASSIGNING <itab> FROM sy-tabix WHERE bukrs EQ '1000' AND

Belnr(2) EQ '55'.ENDLOOP.

Выводы (скорость): Думаю, цифры говорят сами за себя. Хотя обратите внимание на запросы G8 и H8, т.е. простая замена в условии «belnr CP '55*'» на «Belnr(2) EQ '55'», тут же вывело запрос H8 по скорости на первое место, причем сначала он проигрывал в два раза запросу F8, а поле такой замены стал выигрывать в два раза.

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

Есть внутренняя таблица, в которой находится 115.528 записей. В ней отмаркированы 22.106 записей для удаления, маркировка сделана принудительно путем проставления цифры «1» в поле IS_NULL некоторых записей, точнее каждой пятой.

* Данные для работыDATA: BEGIN OF gs_alvtab, belnr TYPE bkpf-belnr, is_null(1) TYPE c, END OF gs_alvtab,

gt_alvtab LIKE STANDARD TABLE OF gs_alvtab,tabix TYPE sy-tabix,idx1 LIKE sy-tabix,idx2 LIKE sy-tabix.

FIELD-SYMBOLS: <fs_alvtab> LIKE gs_alvtab.

ВремВып: 30.944 микросек. e8) LOOP AT gt_alvtab ASSIGNING <fs_alvtab> WHERE is_null = 1.

Страница 39 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 40: Оптимизация ABAP v1-2

DELETE gt_alvtab INDEX sy-tabix.ENDLOOP.

Простой проход по таблице, без выполнения предварительной сортировки таблицы. Кстати, у меня в данном случае отказ от использования конструкции LOOP AT gt_alvtab ASSIGNING <fs_alvtab>, и ее замена на LOOP AT gt_alvtab INTO gs_alvtab, дал разницу в скорости всего в 1000 микросекунд. Причина только в том, что таблица содержит два поля с длинной записи 11 байт.

ВремВып: 40.718 микросек. – СортировкаВремВып: 32.706 микросек. – Удалениеf8) SORT gt_alvtab BY is_null.

LOOP AT gt_alvtab ASSIGNING <fs_alvtab> WHERE is_null = 1. DELETE gt_alvtab INDEX sy-tabix. ENDLOOP.

Аналогичное удаление, но уже по сортированной внутренней таблице. Тут у нас два временных интервала, первый это время на сортировку таблицы и второй, собственно само удаление. Как видим прироста скорости никакого нет, наблюдается даже замедление работы., при этом если сортировку сделать «SORT gt_alvtab2 BY is_null ASCENDING,» то время удаления станет 31.628 микросекунд, т.е. будет небольшое улучшение в скорости, но все равно вариант E8 быстрее, скорость сортировки при этом не изменяется.

ВремВып: 40.718 микросек. – СортировкаВремВып: 38.970 микросек – Удалениеg8) READ TABLE gt_alvtab WITH KEY is_null = 1

BINARY SEARCH TRANSPORTING NO FIELDS.tabix = sy-tabix.

WHILE sy-subrc = 0. sy-subrc = 0. READ TABLE gt_alvtab ASSIGNING <fs_alvtab> INDEX tabix. IF sy-subrc <> 0. EXIT. ENDIF. IF <fs_alvtab>-is_null <> 1. sy-subrc = 4. ELSE. DELETE gt_alvtab INDEX tabix. ENDIF.ENDWHILE.

Тоже в некотором роде вариант удаления данных. Первыми строками проверяем, есть ли записи для удаления и дальше в цикле пока такие записи есть, производим удаление. Если таблица не сортированная, тогда мы не можем использовать вариант поиска с ключом BINARY SEARCH, поэтому код будет выглядеть, немного по-другому.

ВремВып: 151.734 микросек – Удалениеh8) READ TABLE gt_alvtab WITH KEY is_null = 1 TRANSPORTING NO FIELDS.

tabix = sy-tabix.

WHILE sy-subrc = 0. sy-subrc = 0. READ TABLE gt_alvtab ASSIGNING <fs_alvtab> INDEX tabix. IF sy-subrc <> 0. EXIT. ENDIF. IF <fs_alvtab>-is_null <> 1. ADD 1 TO tabix. ELSE.

Страница 40 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 41: Оптимизация ABAP v1-2

DELETE gt_alvtab INDEX tabix. ENDIF.

ENDWHILE.

Как видим «Сортировка+Удаление», будет почти в два раза быстрее, чем просто «Удаление» из не отсортированной таблицы. Кстати, если вы хотите, только проверить существование записи с заданными ключами, то всегда используйте ключ TRANSPORTING NO FIELDS.

ВремВып: 37.390 микросекi8) tabix = LINES( gt_alvtab ).

DO tabix TIMES.READ TABLE gt_alvtab INDEX sy-index

TRANSPORTING NO FIELDS.IF sy-subrc <> 0. EXIT. ENDIF.

ENDDO.

ВремВып: 44.628 микросекj8) tabix = LINES( gt_alvtab ).

DO tabix TIMES. READ TABLE gt_alvtab INTO gs_alvtab INDEX sy-index. IF sy-subrc <> 0. EXIT. ENDIF.

ENDDO.

На ~115 000 записей как видим расхождение в скорости обработки уже существенное, при этом gt_alvtab, содержит только 5 полей, для записей с большим количеством полей и соответственно большей длинной записи, цифры будут еще больше.

Следующий вариант использование оператора DELETE <внутренняя таблица> WHERE <условие отбора>. При этом тест делался как для уже отсортированной таблицы, так и для не сортированной.

ВремВып: 30.401 микросек – Удаление не сортированные данные.ВремВып: 40.718 микросек. – СортировкаВремВып: 19.893 микросек – Удаление сортированные данные.k8) DELETE gt_alvtab WHERE is_null = 1.

Так как в данном случае на 115.528 записей количество вариантов значения поля is_null не очень большое, всего пять вариантов, а мы удаляем 20% записей, то для сортированных таблиц можно воспользоваться вариантом удаления диапазона записей.

ВремВып: 40.718 микросек. – СортировкаВремВып: 5.350 микросек. – Удалениеl8) SORT gt_alvtab BY is_null. READ TABLE gt_alvtab WITH KEY is_null = 1 BINARY SEARCH TRANSPORTING NO FIELDS. idx1 = sy-tabix.

READ TABLE gt_alvtab WITH KEY is_null = 2 BINARY SEARCH TRANSPORTING NO FIELDS.

idx2 = sy-tabix - 1. CHECK idx2 >= idx1. DELETE gt_alvtab FROM idx1 TO idx2.

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

Страница 41 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 42: Оптимизация ABAP v1-2

ВремВып: 40.718 микросек. – СортировкаВремВып: 5.768 микросек. – Удалениеm8) READ TABLE gt_alvtab WITH KEY is_null = 1

BINARY SEARCH TRANSPORTING NO FIELDS.idx1 = sy-tabix.CHECK idx1 <= LINES( gt_alvtab ).DELETE gt_alvtab FROM idx1.

Примечание: В примере M8 удалилось 80% записей, при этом по скорость это оказалось фактически равным времени выполнения запроса L8, в котором удалялось 20% но из средины таблицы, поэтому время выполнения удаления в этом примере, не нужно сравнивать с предыдущими примерами, все таки условия удаления для этого примере несколько другие.

Еще один метод удаления, конструкция DELETE TABLE, но результаты по сравнению с остальными в данном случае очень плохие. Удалялись те же самые 20% записей, время просто катастрофическое.

ВремВып: 80.131.915 микросек. – Удалениеn8) LOOP AT gt_alvtab ASSIGNING <fs_alvtab> WHERE is_null = 1.

DELETE TABLE gt_alvtab FROM <fs_alvtab>.ENDLOOP.

Кстати если сделать цикл без условия с удалением всех записей, то скорость удаления значительно быстрее, но смысл, удалять таким образом все записи? Более быстрым будет просто команда очистки таблицы CLEAR gt_alvtab.

По возможности следует освобождать память, используемую внутренними таблицами. Операция DELETE, как показала практика, такого не выполняет, место на страницах данных внутренней таблицы не освобождается, размер страницы остается таким же, поэтому требуется использовать команду REFRESH или CLEAR. Только после этого память становится освобожденной. Особенно следует обратить внимание на внутренние таблицы, которые работают внутри функциональных модулей, и при этом таблицы объявлены в заголовке функциональной группы. Поэтому при использовании ФМ перед выходом следует делать команду REFRESH или CLEAR для всех таких глобальных таблиц, за исключением буферов справочников.

При копировании данных из одной таблицы в другую для различных последующих обработок следует избегать конструкций вида IT_DATA2[] = IT_DATA1[], вместо этого лучше скопировать только действительно нужные записи в таблицу IT_DATA2. По скорости копирования вариант IT_DATA2[] = IT_DATA1[], конечно же быстрый, но при этом затраты на память удваиваются, если таблицы большие, то вполне память может и закончиться.

Кстати, такое копирование вида IT_DATA2[] = IT_DATA1[], можно и даже нужно использовать в ситуации, когда из большой таблицы в ходе работы было выполнено массовое удаление данных, после которого таблица IT_DATA1[], например, уже содержит всего 10% не удаленных записей данных. Как было сказано выше, при операции DELETE, страницы данных остаются без изменения, память не освобождается. Так вот, выполнив копирование вида IT_DATA2[] = IT_DATA1[], мы получим вторую таблицу IT_DATA2[], которая будет занимать в памяти только 10% от размера таблицы IT_DATA1[], после чего оператором REFRESH или FREE можно полностью освободить память от страниц данных таблицы IT_DATA1[].

Страница 42 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 43: Оптимизация ABAP v1-2

Как видим копирование данных «таблица 1» = «таблица 2» по времени выполняется практически мгновенно в отличие от копирования данных в цикле LOOP <…> ENDLOOP.

ВремВып: 4 микросек. o8) gt_alvtab = gt_alvtab2.

ВремВып: 32.226 микросек.p) LOOP AT gt_alvtab2 ASSIGNING <fs_alvtab>. APPEND <fs_alvtab> TO gt_alvtab.

ENDLOOP.

Для очистки таблиц есть еще оператор FREE. На первый взгляд работа этого оператора аналогична оператору REFRESH, но это не так. Между использованием операторов REFRESH и FREE есть большая разница. Связано это с более низким уровнем работы с памятью на уровне операционной системы и системы SAP. Когда система SAP запрашивает выделение памяти у операционной системы, под запрос пользователя, то та выделяет ее блоками из общей доступной памяти (Extended Memory). Эти блоки памяти поступают в распоряжение пользователя и по своим размерам они значительно больше, чем страницы, которые выделяет потом уже внутренний менеджер памяти SAP для использования во внутренних таблицах программ на ABAP. Таким образом в одном выделенном блоке, будет находится куча страниц с данными внутренних таблиц. Так вот оператор REFRESH освобождает страницы данных внутренней таблицы, но при этом остается заголовок таблицы и первая динамическая страница данных, на которую есть ссылка из структуры заголовка. Впоследствии, такая таблица остается доступной для заполнения, при этом нет затрат на инициализацию внутренних структур такой таблицы. Оператор FREE работает несколько по-другому, он вызывает глобальное перераспределение памяти, все связанные внутренние структуры, такие как заголовок внутренней таблицы, первая страницы данных и т.д. будут освобождены, а объявленная переменная в программе, получит статус, готова к новому распределению. При обращении на заполнение такой таблицы новыми данными, система сначала начнет выполнять весь цикл создания внутренних структур описывающих такую таблицу и только потом приступит к загрузке данных.

Выводы (скорость): В случае не сортированных данных, используйте конструкцию DELETE <int_tab> WHERE <key> = <val>, кстати, пример E8 по времени как видим не сильно отстал от примера K8, для не сортированных данных. В общем виде пример K8, это наиболее быстрый способ удаления данных из внутренней не сортированной таблицы. Если же таблица изначально уже содержит отсортированные значения, то наиболее быстрыми будут способы удаления L8 и M8. Делать отдельную сортировку, а затем использовать способы L8 или M8 не имеет смысла, так как общее время удаления с учетом сортировки получается большим, чем для примера K8.

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

При использовании операции REFRESH создается ложное впечатление, что память не освобождена. Это вызвано тем, что внутренняя структура описывающая таблицу остается и по факту выделенный блок памяти на уровне операционной системы остается занятым, но при этом для пользователя память считается свободной и может быть распределена как под эту таблицу, так и под другие данные пользователя. Использование же оператора FREE вызывает полную очистку данных внутренней таблицы. Это в свою очередь, вызывает глобальный менеджер памяти на предмет

Страница 43 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 44: Оптимизация ABAP v1-2

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

10. Тестирование программ.

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

10.1. Быстрая проверка времени выполнения запросов.

Для проверки своих запросов, особенно если есть различные варианты можно воспользоваться в транзакции SE38 на первом экране нужно по меню перейти: Среда – Примеры – Примеры производительности, рисунок 14: SE38-0.png.

Рисунок 14: SE38-0.png

Слева дерево примеров производительности, а справа два окна, куда можно ввести исходный текст своей программы, при этом можно использовать объявления переменных и все конструкции языка ABAP. После заполнения обоих вариантов, запускаем отчеты по кнопке «Измерить время выполнения» и система покажет время выполнения обоих запросов. Если текст программы довольно большой, со ссылками на различные внутренние переменные, тогда можно сделать замеры самому, используя оператор: GET RUN TIME FIELD time<n>, перед началом интересующего нас блока и в конце. Разница между значениями time1 и time2 и будет временем выполнения блока команд в микросекундах. Пример кода:

DATA: time2 TYPE i,

Страница 44 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 45: Оптимизация ABAP v1-2

time1 TYPE i, time_diff TYPE i.

GET RUN TIME FIELD time1.SELECT * FROM sbook INTO sbook_wa UP TO 1 ROWSWHERE carrid = 'LH'.

ENDSELECT.GET RUN TIME FIELD time2.

time_diff = time2 - time1.WRITE: / 'SELECT TIME:', time_diff.

10.2. SLIN – Расширенная проверка программы.

Простая транзакция, которая позволяет быстро выполнить проверку качества написанного кода для программы. Стандартно можно быстро проверить простые отчеты, если нужно проверить что-то более глобальное, то следует использовать транзакцию «Инспектор кода SAP». Программу можно проверить или запустив отдельно транзакцию SLIN или же находясь в инспекторе кода, например транзакция SE38 – ABAP редактор, выполнить по меню следующий пункт: «Программа» – «Проверить» – «Расширенная проверка программы». При этом если вы находитесь в одном из модулей программы, то система проверку начнем с базового модуля. Например, имеем отчет, который определенным способом выбирает документы резервирований, программа типа отчет, включает в себя несколько модулей, по которым разнесена логика работы. Находясь в редакторе кода, выбираем расширенную проверку программы, рисунок 15: SE38-1.png.

Рисунок 15: SE38-1.png

Будет запущена транзакция SLIN, куда будет автоматически подставлено имя программы

Страница 45 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 46: Оптимизация ABAP v1-2

находящейся в редакторе кода в текущий момент. По умолчанию большинство позиций будет отмечено для расширенной проверки, рисунок 16: SLIN-0.png. Любая расширенная проверка вашей программы, покажет, что с точки зрения системы SAP, вы писать правильно не умеете, если до этого у вас нет привычки, запускать расширенную проверку для всех своих текстов.

Рисунок 16: SLIN-0.png

Запустим проверку, отметив все галки в блоке «Проверки». Получим таблицу со списком данных проверки. Так как сам по себе проверяемый отчет не слишком большой, то в данном случае совсем уже критических предупреждений сравнительно не много, рисунок 17: SLIN-1.png. Данные в таблице сгруппированы в три колонки, первая колонка содержит операции, которые с точки зрения системы являются критическими, далее идет колонка предупреждений и последняя колонка содержит информационные сообщения, когда вроде как все хорошо, но системе есть что вам сказать интересного. Для просмотра детальной информации нужно сделать двойной клик мышью в интересующем поле и система перейдет к экрану детальной информации по проблемам.

Страница 46 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 47: Оптимизация ABAP v1-2

Рисунок 17: SLIN-1.png

• Тестовая среда – Ошибки тестовой среды возникают в случае тестирования стандартных программ SAP, для которых компания разработчик запретила выполнение тестов, например тестирование программы SAPMM07M – Пул модулей для движений материала, выдает такую ошибку и соответственно далее все тесты пропускаются, что в принципе правильно, а то ведь протестируешь стандартную SAP-программу и боюсь там такого будет… что нечего вводить в искушение не окрепшие умы, а окрепшие, они и так знают через что и как оно местами написано.

• PERFORM/FORM-интерфейсы – критические ошибки в данном случае не встречал, а из предупреждений, система предполагает, что если тексте программы нет явного вызова объявленной подпрограммы, то скорее всего, такая подпрограмма не используется, хотя возможно есть динамический вызов, рисунок 18: SLIN-2.png.

Рисунок 18: SLIN-2.png

Т.е. система предупредила, что прямого вызова нет, но удалять, не разобравшись, данную подпрограмму не следует, так как возможно есть динамический вызов. Далее, если вы не хотите, чтобы расширенная проверка выдавала предупреждение по поводу подпрограммы, в данном случае FORM ALV_PF_STATUS, можно использовать код

Страница 47 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 48: Оптимизация ABAP v1-2

"#EC CALLED, для отключения выдачи сообщения. Для применения этого кода, добавьте его в таком виде в программу:

FORM user_command USING ucomm LIKE sy-ucomm "#EC CALLED rs_selfield TYPE slis_selfield. CASE ucomm. WHEN 'DELETE'.

Т.е. в строке кода объявления подпрограммы FORM USER_COMMAND, поставьте комментарий "#EC CALLED, после этого расширенная проверка программы больше не будет обрабатывать данную процедуру и выдавать ее в журнал предупреждений. С точки зрения производительности и читаемости кода, в конечном результате наличие подпрограмм, которые не используются, наверняка будет лишним, поэтому перед сдачей больших проектов такая проверка будет не лишней.

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

FORM get_maktx_text USING p_matnr CHANGING p_maktx.

Сообщение будет оформлено следующим образом, рисунок 19: SLIN-3.png. В общем виде если тип не важнее рекомендуется указать директиву TYPE ANY, хотя в данном случае наверное более правильно было бы указать TYPE matnr, т.е. явно сказать системе, что в данном параметре будет передаваться код материала. В принципе, объявление параметров без явного указания типа, тянется со старых версий. В новых версиях системы, настоятельно рекомендуется указывать типы параметров, что позволит, на этапе компиляции получить ошибки не правильной передачи параметров при вызове такой подпрограммы и избежать проблем в работе.

Рисунок 19: SLIN-3.png

Код отключения сообщения для параметра – "#EC *, который вы должны указать в строке, напротив передаваемой переменной.

Исправление: Удалите из кода все подпрограммы, которые не используются. Переменные параметров в подпрограммах объявите, используя ссылки на типы системы. Такие объявления помогут на этапе компилирования выявить возможные проблемы не правильной передачи параметров, особенно для подпрограмм, которые используются много по тексту разработки.

• CALL FUNCTION – интерфейсы – Выполняется проверка вызовов функциональных модулей. Критические ошибки не попадались, так как обычно я использую вставку вызова функционального модуля через шаблоны, поэтому остаются только предупреждения связанные с отсутствием обработки SY-SUBRC кода после вызова функции, других предупреждений мне не попадалось, рисунок 20: SLIN-4.png.

Страница 48 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 49: Оптимизация ABAP v1-2

Рисунок 20: SLIN-4.png

Код отключения сообщения для параметра – "#EC *, который нужно поставить за именем функционального модуля, например так: CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY' "#EC *.

Сообщение будет подавлено. Наверное, хорошим тоном будет, все таки обработка ошибок работы функционального модуля, если разработчик предполагает, возврат ошибок работы функции.

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

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

• Непротиворечивость экранов – Проверяется правильность созданных экранов в программе. Из полезного проверяется наименование полей и наличие их же или в словаре или вашей программе. Как по мне это наиболее частая ошибка при работе с экранами, так как передача заполнения полей экрана выполняется по имени переменной, то имя переменной экрана, должно быть так же названо и в вашей программе, иначе ошибок не будет, но и значения, введенные с экрана, не попадут в соответствующие переменные программы.

• Полномочия – Проверяются объекты полномочий на их наличие в системе, смысла особого не вижу, так как я обычно вставляю объект полномочий в программу через кнопку «Модель» и соответственно ошибки в имени вставляемого объекта быть не должно.

• GUI-статус и TITLEBAR – Проверяется, есть ли вызываемые GUI-статусы и заголовки экранов в пуле программы. Возможно, кому-то это и нужно при больших разрабатываемых программах с десятками статусов и заголовков.

• Ид. SET/GET-параметров – Проверяет, создан ли в системе используемый SET/GET параметр.

• MESSAGE (сообщение) – Проверяются номер сообщения и класс на существование в системе. Так же проверяется количество формальных параметров передаваемых при вызове номера сообщения из программы с их количеством при объявлении номера сообщения. Дополнительно проверяются длинные тексты к сообщениями, если они созданы.

• Цепочки знаков – данная проверка считает ошибкой использование прямых

Страница 49 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 50: Оптимизация ABAP v1-2

текстовых цепочек знаков в коде программы, т.е. например такой код считается ошибочным: fieldcat_ln-seltext_m = '№ Позциии', т.е. когда текст явно задан. Рекомендуется использовать текстовые переменные вида TEXT–xxx, ведение текстов к программе выполняется через меню: «Перейти» – «Текстовые элементы» – «СимволыТекстПеременн», рисунок 21: SLIN-5.png

Рисунок 21: SLIN-5.png

Код отключения сообщения для параметра – "#EC NOTEXT, который нужно поставить после текстовой цепочки.

Предупреждения в данном поле, возникают, в том случае, если в программе объявлены текстовые переменные, которые нигде не используются, рисунок 22: SLIN-6.png. Скрыть такие сообщения, нельзя, т.е. SAP настоятельно рекомендует, удалять не используемые текстовые переменные, в данном случае это код TEXT-303. Причина такого поведения скрыта в том, что все текстовые переменные грузятся в память при первом запуске программы и находятся постоянно в памяти. Если учесть, что текстовые переменные находятся в каждом пуле функций и т.д. то, большое число не используемых текстовых переменных во всех этих программах, это занятая бесполезными данными память.

Рисунок 22: SLIN-6.png

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

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

Исправление: Перенесите все тексты в область ведения текстов программы и далее используйте ссылки на тексты вида TEXT-xxx. Как максимум это возможно упростит перевод программы в случае ее локализации на другие языки ну и как минимум для Украины перевод бывает иногда очень актуален.

• Вывод CURR/QUAN-полей – в системе SAP количество дробных знаков для полей типа сумма определяется в настройке, поэтому например, при выводе такого поля на экран, система требует, чтобы был указан код валюты, который и определит, как выводить число. Кстати, именно по этому, при создании, например собственной таблицы в базе данных, если вы объявляете поле сумма или количество, то не можете активировать таблицу, пока не укажите для таких полей ссылочное поле, которое

Страница 50 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 51: Оптимизация ABAP v1-2

будет содержать тип единицы измерения. Более подробно как система работает с данными типа CURR можно прочитать по ссылке: http://sapforum.biz/index.php/topic,1514.msg8659.html#msg8659. Вывод строки суммы или количества без указания ссылочной величины считается ошибкой и требует исправления. Текст, примера ошибки, показан на рисунке 23: SLIN-7.png.

Рисунок 23: SLIN-7.png

Данную проверку можно отключать, для этого нужно использовать хинт: "#EC UOM_IN_MES, который следует указать в строке, где используется, например вывод количества, без ссылки на единицу измерения.

Исправление: Код, который вызвал данную ошибку, расширенного анализа, например такой:

SELECT * INTO ls_mseg UP TO 1 ROWSFROM mseg. WRITE: / ls_mseg-menge.ENDSELECT.

Для исправления строку вывода количество следовало бы оформить следующим образом.

WRITE: / ls_mseg-menge UNIT ls_mseg-meins.

Ну а для полей типа сумма указывать дополнение CURRENCY <код валюты>.

• Свойства полей – Ошибочные ситуации я не встречал, а вот предупреждения в большинстве случаев относятся к переменным, которые объявлены в коде, но не используются нигде в тексте программы. В 99% случаев так оно и есть, так как вы просто забыли про объявленную переменную или перестали ее использовать, и 1% оставляем на переменные, доступ к которым, выполняется не явно, например? через переменные типа FIELD-SYMBOLS. Текст сообщения выгляди следующим образом, рисунок 24: SLIN-.png.

Рисунок 24: SLIN-8.png

Другой случай, когда система выдает такую ошибку это объявление переменной, заполнение ее каким-то значением и затем не использование этой переменной. Например, код получения строки курсора в экранной таблице обычно оформляется отдельной подпрограммой:

FORM get_line_click USING grid TYPE cxtab_control CHANGING p_count. DATA: l_field TYPE scrfname, l_stepl TYPE systepl, l_value TYPE char50.

GET CURSOR FIELD l_field LINE l_stepl VALUE l_value. p_count = l_stepl + grid-top_line - 1. IF l_stepl = 0. p_count = 1. ENDIF.ENDFORM. " get_line_click

Расширенный анализ, выдаст предупреждение по поводу переменных L_VALUE и

Страница 51 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 52: Оптимизация ABAP v1-2

L_FIELD. В данном случае, нам нужна только позиция курсора, а вот переменные имени поля и значения используются только потому, что команда языка GET CURSOR FIELD, требует эти переменные, куда будет помещено имя поля и ключа. Поэтому переменные мы объявили, система вернет в них значения, но далее эти переменные нигде не используются, поэтому рекомендуется использовать хинт – "#EC NEEDED, который скрывает такие предупреждения из расширенного анализа. Хинт как обычно указываем в строке объявления переменных, после чего система больше не выводит предупреждения по таким переменным.

Исправление: Переменные, которые действительно не используются в коде, просто удалите или как минимум закомментируйте. Переменные, доступ к которым выполняется не явно, пометьте хинтом – "#EC NEEDED, чтобы скрыть данное предупреждение. В общем виде суть данной проверки избавиться в коде от не используемых переменных, что в большой программе позволяет экономить память и улучшает читаемость и анализ кода.

• Избыточные операторы – В данную проверку, попадают блоки операций, которые не используются в программе, например это макросы которые нигде не используются или, объявленные подпрограммы, которые не содержат вообще никакого кода внутри себя, рисунок 25: SLIN-9.png.

Рисунок 25: SLIN-9.png

Ошибки данной категории опадет в раздел информационных сообщений и не считаются критическими.

Исправление: Удалите не используемые макросы из текста программы, если это возможно или задайте хинт – "#EC NEEDED, для скрытия предупреждения. Пустые подпрограммы так же удалите, если их реализация не предполагается. Как и в предыдущем случае улучшается читаемость кода и его размер.

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

PARAMETERS: p_skun RADIOBUTTON GROUP 001 DEFAULT 'X', p_slif RADIOBUTTON GROUP 001.

Выдаст следующее предупреждение при расширенной проверке, рисунок 26: SLIN-10.png. Как видно из текста предупреждения оно выдается только при запуске расширенной проверки программы, т.е. при стандартной компиляции этого предупреждения нет.

Страница 52 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 53: Оптимизация ABAP v1-2

Проблема заключается в том, что ключ группы объявлен как 001. Не знаю, почему это нельзя делать, может у SAP есть какие-то виды на то каким должно быть имя группы.

Исправление: Используйте буквенные имена групп или хотя бы начинайте имя группы с буквы, например – P01 вместо имени – 001, это снимет проблему или используйте хинт, отключения данного предупреждения – "#EC *.

• Проверка размеров загрузки – Встретить такие проблемы, наверное сложно, если конечно вы не пишите очередной IS (индастир солюшен) в свое области, но программы бывают разные, поэтому вы должны знать, что размер выполняемого кода, количество переменных, констант и т.д. объявленных в вашей программе имеет определенный предел. Система, анализирует размеры вашего кода и при достижении границы в 90% выдается информационное сообщение, при границе 95% выдается предупреждение, при достижении 98% будет выдаваться ошибка. Общие ограничения в системе следующие:

Общий используемый размер адресации программы не должен превышать – 2 097 152 байт, при этом не забываем, что комментарии по тексту программ, тоже входят в эти байты.

Количество констант в программе – 16 384 штук. Количество переменных в программе – 16 384 штук. Literals – 65 536 байт. Количество описанных структур – 65 536 штук. Количество используемых объявлений INCLUDE – 4 000 вызовов.

При этом достигать порога в 90% очень не рекомендуется, а рекомендуется, как минимум оставлять зазор в 10% на внутренние нужды, как самой системы, так и на свои же дальнейшие корректировки и исправления этого кода.

Исправление: Если вы написали в рамках одной программы почти 2 Гб кода, с кучей переменных, то вы очень плодовитый ABAP-программист и теперь для вас пришло время, когда пора как-то подумать о том, чтобы правильно структурировать все написанное и привести его в читаемый вид. Как это сделать, вынесите часть кода во внешние программы, которые будут вызваться через SUBMIT, или же перенесите некоторый код в группы функций или глобальные классы.

• Интернационализация – Проверяется корректность работы операции перевода в нижний/верхний регистр, на языковую среду в которой происходит операция перевода. Фактически на данный момент правильному вызову перевода в верхний/нижний регистр должна предшествовать команда SET LOCALE LANGUAGE, после чего уже можно вызвать операцию TRANSLATE data-txt TO UPPER CASE.

Так же проверяются доступные к использованию форматы даты и разделители между полей даты.

• Проблематичные операторы – Мне встречались только сообщения по поводу объявления переменных в блоке MODULE. Такие переменные фактически являются глобальными, о чем система вас и предупреждает, рисунок 27: SLIN-11.png. Но в целом еще проверяется дублирование операции WHEN в структурах CASE, вызовы операций FREE MEMORY, проверяются написанные вызовы функциональных

Страница 53 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 54: Оптимизация ABAP v1-2

модулей, секция EXPORTING, зарезервированные имена, начинающиеся с %_ и еще много другого, о чем можно или прочитать в справке к операции проверки или же увидеть в ходе проверки своей программы с объяснением причины ошибки.

Рисунок 26: SLIN-11.png

Исправление: Постарайтесь избегать объявления переменных в секциях MODULE / ENDMODULE. Если же такое пришлось сделать по каким-либо причинам, то подавить вывод этого предупреждения можно используя хинт – "#EC *.

• Расширения структур – С версии, наверное ECC 5.0, ну уж точно что в текущей ECC 6.0 при объявлении структур или собственных таблиц в базе данных, система требует при активации определить категорию расширения создаваемой структуры/таблицы. Так вот если при создании разрешить расширение структуры, то при использовании переменной типа созданной структуры система будет выдать ошибку при расширенной проверке, суть которой сводится к тому, что в случае расширения структуры или таблицы может произойти проблема с размерами структур в программе, которые имеют тип созданной в словаре структуры. В общем виде раньше, когда программы в системе были не компилированные, такой проблемы быть в принципе не могло. Сейчас же по факту дополнения структуры, возможно, придется в ручном режиме перегенерировать исходные коды, в которых встречается объявления переменных типа расширенной структуры.

Приме текста сообщения, рисунок 27: SLIN-12.png.

Рисунок 27: SLIN-12.png

Исправление: При объявлении собственных структур в словаре данных, выбирайте признак расширения структуры – «Без расширения».

• Направления программирования – предупреждения данного типа не попадались и в справке к данному типу проверки никакой информации тоже не нашел.

• Устаревшие операторы – наверное, будет содержать самый большой список ошибок и предупреждений, что логично, так как языку уже лет и лет… и со временем SAP начинает считать некоторые конструкции устаревшими. Поддержка таких элементов языка похоже отменяться не будет, так как кроме нового кода, есть большой груз старых текстов. Лично я например, все никак не привыкну перестать использовать такие конструкции как STRUCTURE или LIKE или объявления таблиц с заголовком и т.д. Кстати, к быстродействию данный раздел проверки тоже имеет прямое отношение. Например, если нужно прочитать строку внутренней таблицы по индексу, используя READ TABLE, и при этом значения вы не передаете ни в какую структуру, то SAP рекомендует использовать расширение команды TRANSPORTING NO FIELDS, чтобы лишний раз не гонять данные, рисунок 28: SLIN-13.png

Страница 54 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 55: Оптимизация ABAP v1-2

Рисунок 28: SLIN-13.png

Исправление: Наверное, вдумчиво пересмотреть используемые наборы команд системы и перейти на одобренные лучшими собаководами новые правила языка ABAP.

• Пакетная проверка – насколько я понимаю, проверяются ссылки на структуры ABAP-словаря, являются ли эти ссылки существующими в системы и так же являются ли они активными.

• Общие для программ тесты – Проверятся циклы LOOP.. ASSIGNING <structure>, на предмет переопределения ссылки или ее деинициализации через UNASSIGN внутри цикла. Проверяются операторы приводящие к вызову COMMIT WORK, внутри цикла выбора данных используя операции SELECT. Так же проверяется изменение параметров подпрограммы объявленных через USING, при вызове которых передаются константы, например такой код, вызовет ошибку проверки синтаксиса:

PERFORM get_line USING ‘X’.

FORM get_line USING p_test. <……> p_test = ‘Z’. <……>

ENDFORM.

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

10.3. SCI / SCII – SAP Code Inspector.

Транзакция расширенной проверки программы SLIN неплохо справляется в случае проверки одной программы, даже возможно и большой, но если в разработке участвуют несколько человек, которые пишут не одну программу, а на пример это группа программ включающая в себя как диалоговые модули так и различные отчеты, то использовать отдельно для каждой программы расширенную проверку по отдельности не очень удобно. Поэтому SAP разработал отдельный расширенный анализатор кода, транзакция «SCI – SAP инспектор кода», который позволяет выполнить проверку группы разработок, имеет возможность настроить точные параметры проверки в группе и т.д. Центральный экран транзакции SCI показан на рисунке 29: SCI-1.png. Данная транзакция доступна с версии 6.0, для более ранних версий ее можно загрузить отдельно, используя ноту 543359.

Страница 55 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 56: Оптимизация ABAP v1-2

Рисунок 29: SCI-1.png

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

Простой метод, не вводим в блоке инспекция, никакого имени, а сразу нажимаем кнопку создания проверки или другой вариант быстрого перехода к проверке это использование транзакции SCII – Инспектор кода: инспекция, результат будет такой же, мы попадем на экран задания объекта и параметров проверки кода.

Рисунок 30: SCI-2.png

Система перейдет к следующему экрану, где запросит у вас имя объекта для проверки и параметров выполнения, т.е. собственно говоря, как будем проверять. Фактически, данное дерево проверки, содержит данные транзакции SLIN, но содержит дополнительные параметры по управлению анализом кода. Стандартно, некоторые ветки в дереве уже будут отмечены, однако если галка стоит на более высоком уровне дерева, это не значит, что все подветки ниже так же отмечены для варианта обработки, рисунок 31: SCI-3.png.

Примечание: Параметры проверки, выполняемые в транзакции SLIN, находятся в данном дереве отдельным пунктом по пути: «Проверка синт./Генерация» – «Расшир. проверка программы», там же можно задать галки аналогичные транзакции SLIN.

Страница 56 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 57: Оптимизация ABAP v1-2

Рисунок 31: SCI-3.png

На данном экране можно или выбрать уже созданный вариант набора объектов, указать объекты, находящиеся в общем запросе или же указать непосредственно программу или группу функций для проверки, тип объекта выбирается из выпадающего списка. Далее можно опять же выбрать уже созданные ранее вариант параметров проверки или же выбрать вариант временного определения и задать нужные галки непосредственно в дереве ниже. Затем, когда мы определили объект проверки и правила проверки можно запустить саму проверку, рисунок 32: SCI-4.png.

Рисунок 32: SCI-4.png

Результат проверки будет представлен в виде дерева, по аналогии с информацией транзакции SLIN, например в данном случае как видим, критическими с точки зрения системы это задание текстов непосредственно в тексте программы, без использования текстовых

Страница 57 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 58: Оптимизация ABAP v1-2

элементов, рисунок 33: SCI-5.png.

Рисунок 33: SCI-5.png

Фактически получаем те же данные, что и для транзакции SLIN, однако в отличие от нее, данная транзакция может быть немного преднастроена на то, какие ошибки считать критическими, какие предупреждением, а что считать информацией. К сожалению не все сообщения можно настраивать, но что-то в реакции системы можно регулировать. Для этого на первом экране транзакции SCI нужно перейти по меню «Перейти к» – «Управление» – «Приоритеты сообщений», рисунок 34: SCI-12.png. Например, если для таблицы в операции SELECT не заданы ограничения выбора в WHERE, то система, по умолчанию, выдает сообщение класса – предупреждение.

Рисунок 34: SCI-12.png

Для изменения реакции для выбранного действия можно просто кликнуть мышью на значек предупреждения, в данном случае это желтый треугольник и в появившемся окне система предложит вам выбрать новую реакцию на данный код. Например, можем задать, чтобы выдавалось сообщение об ошибке, рисунок 35: SCI-13.png. В колонке рядом, «Актуальный приоритет», система покажет, что текущая настройка отсутствия команды WHERE при использовании оператора SELECT – критическая ошибка.

Страница 58 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 59: Оптимизация ABAP v1-2

Рисунок 35: SCI-13.png

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

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

Вариант проверки: Вводим имя вариант и нажимаем создать вариант, система перейдет к дереву, в котором нужно задать те проверки, которые мы считаем правильными для своей разработки. После отметок нужных текстов, сохраним вариант, рисунок 36: SCI-7.png. В отличии от транзакции SLIN, при формировании параметров проверки в транзакции SCI, можно более гибко управлять параметрами проверки данных.

Рисунок 36: SCI-7.png

Например, стандартно система считает, что переменные параметров для оператора PERFROM должны начинаться с префикса P_, а это значит, что если у вас параметры будут начинаться с другого префикса, то система будет выдавать информационное предупреждение о не согласованности в именах переменных. Я, предпочитаю чуть изменить такое соглашение. Например, начинать параметры для простых переменных, с префикса – P_, параметры структур – PS_, параметры таблиц – PT_. Теперь нужно объяснить системе наши

Страница 59 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 60: Оптимизация ABAP v1-2

соглашения по именам. Для этого нужно перейти к атрибутам проверки и ввести свои параметры соглашений, напротив каждого теста, есть колонка «Атрибуты» и кнопка перехода к ведению атрибутов, рисунок 37: SCI-10.png.

Рисунок 37: SCI-10.png

Нажав данную кнопку, получим большой список соглашений по всем параметрам, находим блок, относящийся к параметрам вызова подпрограмм, рисунок 38: SCI-11.png. Как видим, предпочтения по именам делятся в разрезе USING / CHANGING / TABLES. В данном случае можем добавить в параметры USING / CHANGING свой тип PS_*, а в параметр TABLES заменить на PT_*, ну и если имеются какие-то предпочтения по именам подпрограмм, то можно заполнить поле FORM, т.е. фактически мы говорим какие соглашения по именам считать правильными.

Рисунок 38: SCI-11.png

Таким образом, можно настроить инспектор кода под правила разработки, которые существуют в вашей компании, ну если конечно они существуют.

Набор объектов: Создадим вариант, включающий в себя объекты проверки. В набор можно включить как отдельные программы и группы функций, так и определить более сложные варианты выбора объектов кода для проверки, например если уже есть созданные ранее наборы объектов, то можно их объединить в новом варианте и т.д. В общем, вариантов включения объектов кода для проверки существует масса, рисунок 39: SCI-7.png.

Страница 60 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 61: Оптимизация ABAP v1-2

Рисунок 39: SCI-7.png Создав вариант включения объектов, сохраним его, аналогично варианту проверки данных и теперь можно объединить вариант проверки с вариантов набора объектов, создав инспекцию проверки данных.

Инспекция проверки: Инспекция проверки, как говорилось ранее, состоит из объединения варианта проверки и набора объектов. Поэтому введем имя инспекции, после чего выберем кнопку создания. На следующем экране система уже не будет предлагать нам ручной вариант настройки варианта проверки, хотя внести в ручном режиме набор объектов можно. Выберем из списка созданный ранее набор объектов и вариант проверки в соответствующие поля на экране, рисунок 40: SCI-8.png.

Рисунок 40: SCI-8.png

Теперь можно или выполнить вариант инспекции или сохранить вариант, например, чтобы запускать проверку периодически. Вариантов выполнения есть два, выполнить в диалоге или в фоновом режиме. Если разработки большие или проверка выполняется периодически, например каждые выходные, чтобы на утро иметь результаты обработки, то можно настроить

Страница 61 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)

Page 62: Оптимизация ABAP v1-2

периодическое фоновое задание для запуска данной инспекции. Результат проверки будут сохранены в логе созданной инспекции. Кстати, красная лампочка рядом с именем инспекции показывает, что проверка не выполнялась, после выполнения проверки лампочка станет зеленой и появится кнопка перехода к просмотру данных проверки, рисунок 41: SCI-9.png.

Рисунок 41: SCI-9.png

Параметры вывода результатов проверки будут представлены деревом. Просмотреть результат можно в любой момент, нужно только ввести имя варианта инспекции и нажать кнопку просмотра «Результаты». Так же можно создать новый вариант запуска анализатора или удалить старые варианты, рисунок 42: SCI-14.png. Двойной клик мышью на тексте сообщения, позволяет перейти к просмотру текст программы, которая сгенерировала это сообщение. Кнопка информации выдает краткую справку о причинах реакции системы и небольшие разъяснения о причинах и как можно исправить и отключить данное сообщение. Не все сообщения можно отключать.

Рисунок 42^ SCI-14.png

Примечание: Транзакции SLIN и SCI анализируют написанный код. Кроме функции приведения кода к общему стандартному виду, что само по себе не маловажно, данные транзакции позволяют предварительно определить критические с точки зрения производительности места в программах, без их непосредственного выполнения, т.е. но фактически это потенциально критические места. Фактические узкие места, в работе программ, анализируются другими методами, например включением трассировки выполнения программы и т.д.

Страница 62 из 62 по материалам форума www . sapforum . biz (Версия: 1.2)