Учимся правильно использовать FOR ALL ENTRIES IN
В этой статье я хочу рассказать о тонкостях работы конструкции FOR ALL ENTRIES IN: в частности, о том, что происходит на уровне БД; об оптимизации запросов; о database-hints.
Кратко о конструкции
Конструкция FOR ALL ENTRIES IN применяется в SELECT запросах до оператора WHERE. После неё указывается внутренняя таблица с данными, поля которой можно использовать в операторе WHERE в качестве условий выборки.
Что происходит на уровне БД
Очень часто программисты при написании кода на ABAP используют эту конструкцию. Она удобна, экономит время при разработке, но мало кто задумывается над тем, как она работает. И вот однажды, из-за возросшего объемы данных, наступает такой момент, когда написанная программа начинает «тормозить». Чаще всего проблема бывает в запросах, и разработчик начинает оптимизировать их: добавляет индексы, убирает запросы из циклов и т.д. Но практически он никогда не обращает внимания на конструкцию FOR ALL ENTRIES IN, так как считает, что оптимизировать в ней нечего.
Давайте на простом примере (Рис. 1) проанализируем работу этой конструкции. Из таблицы BKPF выберем 1000 строк во внутреннюю таблицу LT_BKPF, а потом из таблицы BSIS выберем данные, используя конструкцию FOR ALL ENTRIES IN LT_BKPF.
======================================
report z_test.
" объявляем переменные
"
data: begin of ls_bkpf,
bukrs type bkpf-bukrs,
belnr type bkpf-belnr,
end of ls_bkpf.
data: lt_bkpf like table of ls_bkpf.
data: lt_bsis like table of bsis.
" выбираем данные из таблицы bkpf и помещаем их во внутреннюю таблицу lt_bkpf
"
select
bukrs belnr
up to 1000 rows from
bkpf
into corresponding fields of table
lt_bkpf
where
gjahr = '2013'.
check lines( lt_bkpf ) > 0.
" выбираем данные из таблицы BSIS
" в FOR ALL ENTRIES IN передаем внутреннюю таблицу LT_BKPF
"
select
*
from
bsis
into corresponding fields of table
lt_bsis
for all entries in
lt_bkpf
where
bsis~bukrs = lt_bkpf-bukrs and
bsis~belnr = lt_bkpf-belnr and
bsis~gjahr = '2013'.
======================================
Рис. 1 Пример работы конструкции FOR ALL ENTRIES IN
Для анализа работы конструкции будем использовать транзакцию ST05 – трассировка SQL-запросов.
1. В одном режиме запускаем 05 и включаем трассировку (Рис. 2):
Рис. 2 Запуск трассировки
2. В другом режиме выполняем нашу программу. После чего в 05 выключаем трассировку и выводим результат (Рис. 3, Рис. 4, Рис. 5), установив фильтр на таблицы и , чтобы отсеять ненужный нам мусор:
Рис. 3 Отключение трассировки
Рис. 4 Вывод результата трассировки
Рис. 5 Результат трассировки
Мы видим, что к таблице BKPF у нас отработал один запрос и вернул 1000 строк – здесь все нормально. А вот к таблице BSIS у нас выполнилось 100 запросов, да еще и каждый из них содержит 10 запросов, объединенных через конструкцию UNION ALL SELECT. Это, по сути, означает, что один запрос с FOR ALL ENTRIES IN превратился во время выполнения в 1000 отдельных запросов. Если обобщенно – сколько записей во внутренней таблице FOR ALL ENTRIES IN, столько будет отдельных запросов к базе данных. Понятно, что при большом объеме данных это всё будет очень медленно работать.
Оптимизируем
«Как же это оптимизировать?» - спросите вы. Чтобы увеличить быстродействие, нам придется немного усложнить код нашей программы (Рис. 6):
======================================
report z_test.
" объявляем переменные
"
data: begin of ls_bkpf,
bukrs type bkpf-bukrs,
belnr type bkpf-belnr,
end of ls_bkpf.
data: lt_bkpf like table of ls_bkpf.
data: lt_bkpf_tmp like lt_bkpf.
field-symbols: <wa_bkpf> like ls_bkpf.
data: lt_bsis like table of bsis.
data: begin of ls_bukrs,
bukrs type bukrs,
end of ls_bukrs.
data: lt_bukrs like table of ls_bukrs.
" выбираем данные из таблицы BKPF и помещаем их во внутреннюю таблицу LT_BKPF
"
select
bukrs belnr
up to 1000 rows from
bkpf
into corresponding fields of table
lt_bkpf
where
gjahr = '2013'.
check lines( lt_bkpf ) > 0.
" получаем список уникальных БЕ во внутреннюю таблицу LT_BUKRS
"
loop at lt_bkpf assigning <wa_bkpf>.
ls_bukrs-bukrs = <wa_bkpf>-bukrs.
collect ls_bukrs into lt_bukrs.
endloop.
" для каждой БЕ выполняем запрос
"
loop at lt_bukrs into ls_bukrs.
" выбираем из LT_BKPF номера документов, относящиеся к определенной
Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland
ЗарегистрироватьсяУ вас уже есть учетная запись?
Войти
Обсуждения 3
Комментарий от
Олег Точенюк
| 11 августа 2015, 22:16
Комментарий от
Николай Кронский
| 12 августа 2015, 16:11
Комментарий от
Руслан Закарьяев
| 20 августа 2015, 16:03
Олег Точенюк 11 августа 2015, 22:16
За database-hints нормальные базисники обычно отрывают руки а тушку после этого вывешивают в назидание так сказать, а ибо нефиг :-) А вообще-то как заметили в другом месте, то как работает FOR ALL ENTRIES очень сильно зависти от настроек профиля системы, поэтому использовать его конечно можно, но лично я на эту радость забил давно.
Конечно, вместо FOR ALL ENTRIES можно использовать конструкцию "WHERE field IN r_field", но и тут есть подводные камни: если в r_field большое число строк (2000+), то будет dump с ошибкой DBIF_RSQL_INVALID_RSQL из-за того, что размер SQL-запроса в килобайтах превысил некоторую границу. Чтобы его избежать, придется выполнять несколько запросов, передавая значения в IN партиями.
Но, в целом, вы правы. Лучше, по возможности, избегать использование конструкций, работа которых неочевидна.