GTT – global temporary tables, таблицы которые наполняются и очищаются в рамках ABAP-сессии (application session), но находятся при этом на уровне БД (то есть данные не передаются между Database и Application).
В ABAP-разработке их можно перевести как глобально сессионные таблицы или словарно-сессионные таблицы. Появляются Global Temporary Tables (GTT) с версии ABAP 7.50.
Что такое temporary tables?
В базах данных Temporary table – это объект на уровне базы данных, куда могут быть помещены данные после выборки из таблиц БД; при этом данные существуют в рамках сессии приложения и не передаются из базы на application, а остаются в базе. Это дает возможность применять SQL-операторы к «промежуточным» данным для вычислений и не делать дополнительные roundtrip между application и базой. В терминах ABAP-разработки temporary table можно рассматривать как альтернативу «промежуточным» внутренним таблицам или операторам UNION.
Temporary tables реализованы в различных базах: PostgreSQL, ORACLE, HANA ; также поддерживаются в 1С (ссылка здесь и здесь). Назначение их, по сути, одно и то же: в течение сессии сложить данные в таблицу, а по итогу сессии забрать данные и очистить таблицу.
GTT в ABAP представляет собой отдельную прозрачную таблицу, в которую складываются данные в рамках DB LUW и могут быть считаны только в рамках текущей сессии. На начало сессии и на завершение таблица должна быть пустая (очищена принудительной командой через ABAP, если нужно). Цель GTT – это дать возможность разделить сложную выборку на несколько шагов. За счет того, что данные хранятся в рамках одной сессии, то «закулисное администрирование» такой таблицы немного меньше. Можно сказать, что временная таблица работает почти как внутренняя таблица, но остаётся на стороне базы данных.
Демонстрационный пример в ABAP
Как правило, temporary table полезна, когда нужно получить данные из источников, которые имеют разное смысловое назначение, но сводятся к одной и той же структуре данных.
Пример: нужно получить список (MATNR) материалов, которые указаны в заказах на дату DATE1 и указаны в фактурах на дату фактурирования DATE1.
В SAP ERP (и как правило, в других системах тоже) заказы и фактуры находятся в разных таблицах, а также заголовок и позиция этих сущностей также находятся в разных таблицах (и даже не в одной ? ). В SAP ERP для заказов используются таблицы: VBAK, VBAP и др., а для фактур таблицы: VBRK, VBRP и др.. Однако для целей демонстрации (на стандарте экспериментировать не хорошо), я сделаю упрощённые аналогичные таблицы со структурой (рис.1 и табл.1).
Таблица | Поле | Комментарий |
ZTC8A016_ORDH | VBELN | Номер заказа, первичный ключ |
KUNNR | Номер (id) партнера-заказчика | |
ORDER_DATE | Дата заказа | |
CRDT | Дата создания заказа | |
| ||
ZTC8A016_ORDI
(позиции заказа, демо) | VBELN | Номер заказа, являются частью первичного ключа, связано внешним ключом с |
POSNR | Номер позиции заказа, являются частью первичного ключа | |
MATNR | Номер материала (MATNR) | |
| ||
ZTC8A016_INVH
(заголовок фактуры, демо) | INV_NUM | Номер фактуры, первичный ключ |
PAYER | Номер (id) партнера-плательщика | |
INVOICE_DATE | Дата фактуры | |
CRDT | Дата создания фактуры | |
| ||
ZTC8A016_INVI
(позиции фактуры, демо) | INV_NUM | Номер фактуры, является частью первичного ключа. Связано внешним ключом с ZTC8A016_INVH . INV_NUM |
INV_POSNR | Номер позиции фактуры, является частью первичного ключа. | |
MATNR | Номер материала |
Данная структура является сильным упрощением реальной структуры (например, нет цен и других важнейших атрибутов сущностей), но для целей демонстрации GTT – пусть будет так. Главное, что хотелось подчеркнуть, что данные находятся в разных источниках: группа таблиц заказов и группа таблиц фактур. Наполним таблицы с помощью утилиты.
А теперь укажем, как может быть решена данная задача различными способами.
Способ1. Считываем данные из каждой таблицы (точнее join) и объединяем на стороне application server (этот способ классический и держится уже много десятилетий, поэтому не буду его приводить).
Способ2. Делаем UNION из двух JOIN и возвращаем результат. Одна из возможных реализаций этого способа приведена здесь.
Способ3. Создаём GT-таблицу. Извлекаем данные в неё из JOIN по заказам, при этом не возвращая ничего на application server; затем извлекаем данные из JOIN по фактурам, также ничего не возвращая. А третьим шагом – считываем данные из временной таблицы и очищаем её. Реализация доступна по ссылке.
Создание Global Temporary Table в SAP NetWeaver (GTT в ABAP)
Давайте рассмотрим по шагам, как создать Global Temporary Table в ABAP, и как использовать.
В ABAP-справке содержится описание GTT и пример содержится в программе (DEMO_GTT).
Создадим для нашего примера GTT через SE11; таблица создаётся как прозрачная таблица, но с указанием признака GTT (global temporary table).
Затем, как и в прозрачной таблице, указываем структуру таблицы.
Таблица 2 Структура GTT (Global Temporary Table) для демонстрации
Поле | Тип | Комментарий |
MANDT | MANDT |
|
SRC_TYPE | CHAR1 | Ключевое поле, Содержит либо O – order, либо I – Invoice |
MATNR | MATNR | Материал |
Затем переходим по меню
Extras -> Change/Display Table Category
Технические параметры (тип таблицы, размер, буферизация, и т.д.) не ведутся для GTT.
Затем активируем таблицу и на этом создание GTT в словаре выполнено. Ограничения описаны в справке.
Использование и чтение данных из GTT в ABAP
Полная реализация этого способа тут.
GTT позволяет нам разделить чтение на части; поэтому мы можем использовать 2 метода по чтению, а именно: _fill_gtt_from_order и _fill_gtt_from_invoice. Таким образом, нам «удобнее» понимать, из каких шагов состит общая выборка. А также мы можем обращать внимание на переменную SY-DBCNT и, тем самым знать, сколько записей на каждом шаге было выбрано. Обращаю внимание, что мы не извлекаем данные во внутреннюю таблицу, а делаем INSERT во временную (в данном случае ZTC8A016_MAT_TMP, которую создали в предыдущем пункте).
METHOD _fill_gtt_from_order.
INSERT ztc8a016_mat_tmp FROM
(
SELECT DISTINCT
'O' AS src_type,
ordi~matnr AS matnr
FROM ztc8a016_ordi AS ordi
JOIN ztc8a016_ordh AS ordh ON ordi~vbeln EQ ordh~vbeln
WHERE ordh~order_date EQ @mv_trg_date
).
ENDMETHOD.
METHOD _fill_gtt_from_invoice.
INSERT ztc8a016_mat_tmp FROM
(
SELECT DISTINCT
'I' AS src_type,
invi~matnr AS matnr
FROM ztc8a016_invi AS invi
JOIN ztc8a016_invh AS invh ON invi~inv_num EQ invh~inv_num
WHERE invh~invoice_date EQ @mv_trg_date
).
ENDMETHOD.
Если там потребуется прочитать данные еще и из другого документа (например, поставок или закупочных документов), то существующие SELECT править уже не понадобиться, а нужно будет добавить метод, который заполняем temporary table. Таким образом, общая логика становится более наглядной и гибкой.
После того, как заполнение таблицы завершено, извлекаем нужные данные из временной таблицы. При этом мы можем применять «почти» любые ABAP SQL-операторы к этой таблице, включая агрегированные функции, JOIN, наложение условий через WHERE/HAVING и т.д. Однако, после того, как какие-либо данные добавлены в GTT не допускаются COMMIT (в том числе неявные / implicit).
_fill_gtt_from_order( ).
_fill_gtt_from_invoice( ).
""""""""" read
" допускается "почти" любой AbapSQL
SELECT DISTINCT matnr
FROM ztc8a016_mat_tmp
ORDER BY matnr DESCENDING
INTO TABLE @et_matnr_list
UP TO 3 ROWS.
IF mv_mode EQ '3'.
" из-за неудаленных данных - будетм RunTime Error (aka красный Dump)
" неявный коммит
MESSAGE i000(cl) WITH 'Runtime Error goes here'.
ENDIF.
""""""""" delete - MUST EXIST
DELETE FROM ztc8a016_mat_tmp.
MESSAGE i000(cl) WITH 'Почистили GTT в сессии'.
Полный пример доступен по ссылке на github.
Подробнее GTT описаны в справке.
Заключение
Основными преимуществами GTT (как в ABAP (SAP NetWeaver), так и в других базах) являются:
Возможность не передавать (не делать лишние database roundtrip) данные между базой и application (для данных более 50000 записей, это может иметь значение).
Разделить логику сбора данных на несколько шагов, что повысит удобочитаемость, а также расширяемость без риска «сломать существующее».
За счет «временности» накладные расходы на такую таблицу меньше, чем на обычную и работа с ней осуществляется немного быстрее.
На всякий случай обращу внимание, что GTT эффективно применимы не во всех возможных случаях, но эту возможность полезно знать.