#sql #stored-procedures #hana #s4hana #slt
#sql #хранимые процедуры #hana #s4hana #slt
Вопрос:
Проблема: Моя команда в настоящее время проходит миграцию ERP с ECC-системы на новую систему S / 4 Hana. В рамках go-live нашей команде необходимо реплицировать все таблицы из системы S / 4 в нашу схему SLT, в которой будут размещены данные. Большая часть таблиц будет обрабатываться репликацией SLT из SAP. Но из-за сжатых сроков мы определили 4 таблицы, для репликации которых потребуется несколько дней. Идея состоит в том, чтобы скопировать существующие данные из удаленного источника (ABAP / SDA) и поместить в нашу схему SLT. Как только это будет выполнено, мы можем активировать репликацию с прямой передачей и разрешить обновлять все новые или измененные записи с помощью репликации SLT.
Предпринятые подходы: Наш текущий подход заключается в установлении соединения SDA с серверной базой данных S / 4 и разбивке данных по годам для вставки в нашу локальную таблицу с использованием хранимой процедуры. При таком подходе возник ряд проблем, но в настоящее время он работает. Это просто очень медленно.
Вопросы для форума:
- Так ли вы подходите к решению такого рода проблем? Если нет, то какое ваше предлагаемое решение?
- Видите ли вы в целевой таблице что-нибудь, что необходимо настроить для повышения производительности?
- Выделяет ли что-нибудь для вас в хранимой процедуре, которую, возможно, потребуется настроить?
Пример: давайте представим, что у нас есть исходная таблица с именем: A_tbl
- 500 миллионов записей в A_tbl
- Ширина примерно 500 столбцов
Тогда у нас будет наша целевая таблица: B_tbl
- То же количество столбцов, что и A_tbl (500)
- Циклическое разбиение из 12
- Индексируется по 5 столбцам
Текущая процедура:
CREATE OR REPLACE procedure LOAD_B_TBL_FROM_A_TBL ()
as
begin
declare v_offset_nbr integer;
declare v_record_count integer;
declare v_commit_count integer;
declare i integer;
declare v_year nvarchar(4);
declare v_record_per_commit_count CONSTANT INT = 1000000;
declare v_table_name CONSTANT NVARCHAR(30) = 'A_TBL';
declare v_start_year CONSTANT INT = 2011;
declare v_end_year CONSTANT INT = 2022;
declare year_nbr integer;
for year_nbr in v_start_year..v_end_year do
select IfNull(max(offset_nbr),0) into v_offset_nbr from B_TBL_SCHEMA.bulk_load_log where table_name = :v_table_name AND year_nbr = to_varchar(year_nbr); -- Get offset number of records
select count(*) into v_record_count from A_TBL_SCHEMAA_TBL A_TBL WHERE A_TBL.YEAR = to_varchar(year_nbr); -- Count the source records.
v_record_count = v_record_count - v_offset_nbr; -- Subtract out the records already committed for the current year. Failsafe if procedure fails
v_commit_count = v_record_count / v_record_per_commit_count; -- Number of times we need to loop
IF v_record_count < v_record_per_commit_count THEN -- Don't enter the loop if it's not necessary
INSERT INTO B_TBL_SCHEMA.B_TBL (
SELECT * FROM A_TBL_SCHEMAA_TBL
WHERE A_TBL.YEAR = to_varchar(year_nbr)
); -- Insert into our target table
COMMIT;
insert into B_TBL_SCHEMA.bulk_load_log values(
v_table_name,
to_varchar(year_nbr),
:v_offset_nbr,
now()
); -- Insert into a logging table to keep up with offset
ELSE
for i in 0..v_commit_count do -- Loop number of commit times. (500 million / 1 million) = 500 commits necessary to process entire table
INSERT INTO B_TBL_SCHEMA.B_TBL (
SELECT * FROM A_TBL_SCHEMAA_TBL
WHERE A_TBL.YEAR = to_varchar(year_nbr)
LIMIT :v_record_per_commit_count OFFSET :v_offset_nbr
); -- Insert into our target table
COMMIT;
v_offset_nbr = v_offset_nbr v_record_per_commit_count; -- Update the offset before logging so we know where to begin if procedure fails
insert into B_TBL_SCHEMA.bulk_load_log values(
v_table_name,
to_varchar(year_nbr),
:v_offset_nbr,
now()
); -- Insert into logging table to keep up with offset
COMMIT;
end for;
end if;
end for;
end;
Ответ №1:
Я думаю, что самый простой и быстрый способ переноса таблиц без какого-либо дополнительного администрирования — это EXPORT
оператор с BINARY
параметром format . Это также можно сделать через контекстное меню HANA studio в схеме или через File -> Export... -> SAP HANA -> Catalog Objects
, File -> Import...
.
При таком подходе вы можете вручную задать количество потоков для экспорта и импорта без дополнительного сложного кода. После импорта в целевую систему у вас будет та же таблица с той же структурой в той же схеме, что и в исходной системе, поэтому, чтобы переместить таблицу с новым именем или схемой, вам нужно сначала скопировать ее в исходный код. После импорта вы можете insert ... select ...
создать целевую таблицу или скопировать таблицу с желаемым разделением в исходной системе или перераспределить импортированную таблицу в целевой и использовать ее в качестве целевой таблицы.
В чем преимущества:
- Вам не нужен какой-либо код или SDA-соединение между системами.
- Вам не нужно проверять и перепроверять, если ваш код работал нормально, и все данные были переданы без дубликатов.
- У вас будет точно такая же таблица в цели.
- Таблица экспортируется и импортируется во внутреннем столбчатом формате параллельно, поэтому между ними не появляется восстановление кортежей (я не знаю, достаточно ли умен драйвер HANA SDA, чтобы передавать столбцы по каналу SDA и выполнять восстановление кортежей в цели). И количество столбцов и записей мало влияют, только столбцы с данными с высокой мощностью требуют времени, пустые или столбцы с низкой мощностью экспортируются в мгновение ока.
И, наконец, я протестировал это в своей системе через HANA studio (импорт на локальный компьютер): таблица с 130 миллионами записей и 57 столбцами размером 5 ГБ была экспортирована в 8 потоков в течение 6 минут.
Как насчет вашего первоначального подхода: вы всегда должны отключать индексы и ограничения удаления для действительно массовых операций и перестраивать / включать их в самом конце, чтобы сэкономить время на перестройку индекса или проверку ограничений во время вставки.
Комментарии:
1. Спасибо за всю эту информацию, я действительно ценю это! В ответ на ваше предложение, мы действительно пытались выполнить двоичный экспорт ранее при проектировании системы, но, оглядываясь назад на мои заметки, мы попытались сделать это в таблице с 49 миллионами записей и получили какую-то ошибку дискового пространства.
Ответ №2:
Узким местом производительности, вероятно, является передача через SDA, которая использует только один поток.
Обходной путь может быть:
- создайте функцию с входным параметром для выбора подмножества таблицы A. Например, год.
- поместите список лет в табличную переменную
- вызовите map_merge для принудительного параллелизма и передачи выходных данных в целевую таблицу.
Вот некоторый псевдокод.
-- virtual table to source A over SDA: VT_TBL_A
-- target table B: TBL_B
CREATE OR REPLACE FUNCTION F_TBL_A_YEAR ( IN YEAR INT)
RETURNS TABLE ( ... )
as begin
select * from VT_TBL_A where year(MY_DT_COLUMN) = :YEAR;
end;
DO
BEGIN
DECLARE t_var LIKE TBL_B;
--list of all 'partitions'
years=select distinct year(MY_DT_COLUMN) as MY_YEAR from VT_TBL_A;
--call the function for each year
t_var = MAP_MERGE(:years, F_TBL_A_YEAR( :years.MY_YEAR));
insert into TBL_B select * from :t_var;
commit;
END;
Пока это выполняется, я предлагаю вам проверить системное представление M_REMOTE_STATEMENTS, чтобы подтвердить, что записи передаются по нескольким соединениям