Hologres V2.0版本開始支援INSERT OVERWRITE預存程序,方便使用者進行大批量資料的全量寫入或者分區級資料批量寫入。本文為您介紹在Hologres中如何使用預存程序實現INSERT OVERWRITE功能,如您執行個體版本低於V2.0,請升級執行個體,詳情請參見執行個體升級。如您暫時不方便升級,也可使用暫存資料表方式實現INSERT OVERWRITE功能。
功能說明
Hologres V3.0版本增強了hg_insert_overwrite能力,支援通過INSERT OVERWRITE命令直接匯入資料至分區父表。
Hologres從V2.0.15版本開始,支援通過
set hg_experimental_hg_insert_overwrite_enable_view=on;
命令開啟GUC,實現向有視圖依賴的表中匯入資料;暫不支援向有物化視圖依賴的表中匯入資料。V3.0版本起,無需設定上述GUC,即可支援向有視圖依賴的表中匯入資料;暫不支援向有物化視圖依賴的表中匯入資料。
對於Hologres V2.0.11以前的版本,匯入失敗需要手動清理暫存資料表;自V2.0.11版本開始,系統會自動清理暫存資料表。
使用限制
如果選擇部分欄位匯入,欄位順序需要與源表保持一致且一一對應。
由於hg_insert_overwrite需要以表Owner的身份建立一張暫存資料表,因此僅Superuser和表的Owner有許可權執行hg_insert_overwrite操作。
目標表的分區鍵支援INT、TEXT或VARCHAR類型。
Hologres V3.0版本起,明確要求不能在事務中使用hg_insert_overwrite,執行會報錯。
說明在舊版本的事務中使用hg_insert_overwrite也會有潛在問題,但很多時候可以正常正確執行,新版本將更加嚴格。
行為變更
Hologres V3.0版本起,hg_insert_overwrite有如下行為變更:
僅有target_table和sql兩個入參時,如果目標表是分區父表,則V3.0版本前會直接報錯,V3.0版本起可能寫入成功(當select_query執行結果對應的分區子表都已存在時),也可能報錯(當select_query執行結果對應的分區子表不存在時)。
如果hg_insert_overwrite執行中途被Cancel,則V3.0版本起需要執行如下SQL清理暫存資料表,V3.0版本前不需要清理暫存資料表。
--- 刪除before_time之前系統建立的暫存資料表 CALL hg_clean_insert_overwrite_tmp_tables(before_time::timestamptz);
使用預存程序實現INSERT OVERWRITE功能
命令格式
-- V3.0版本前的hg_insert_overwrite文法
CALL hg_insert_overwrite('<target_table>' regclass, ['<partition_value>' text], '<sql>' text);
-- V3.0及以上版本的hg_insert_overwrite文法
CALL hg_insert_overwrite('<target_table>' regclass, ['<partition_value>' array], '<sql>' text, ['<auto_create_partition>' bool]);
參數說明
Hologres V3.0版本起,hg_insert_overwrite語句中的partition_value資料類型改為ARRAY類型,即支援寫入分區父表並指定多個分區子表。您仍可使用TEXT類型作為partition_value的入參,但此時只支援寫入一張分區子表。
參數 | 說明 |
target_table | Hologres的內部表。 即資料目標儲存表,表必須已經存在。 |
partition_value | 分區表的分區值。
|
sql | 標準的SELECT語句。 可用來查詢MaxCompute或者Hologres的表,需確保SELECT出來的分區欄位值必須完全等於
|
auto_create_partition | 是否自動建立分區。僅V3.0及以上版本支援該參數。
|
V3.0版本起,針對INSERT OVERWRITE分區父表,即target_table
為分區父表的情況,不同參數設定的行為如下:
不指定
partition_value
參數時:auto_create_partition
值說明
TRUE
sql
執行結果對應的target_table
分區,全部執行資料覆寫。如果有不存在的分區子表,則先自動建立分區。與
sql
執行結果無關的target_table
分區,忽略。
FALSE
sql
執行結果中,如果對應的target_table
分區都存在:執行結果對應的
target_table
分區,全部執行資料覆寫。與執行結果無關的
target_table
分區,忽略。
sql
執行結果中,如果有對應的target_table
分區不存在,則直接報錯,其餘已存在的分區也不執行覆寫。
指定
partition_value
參數時:auto_create_partition
值說明
TRUE
對於
partition_value
指定的target_table
分區:如果分區實際不存在:自動建立分區。
sql
執行結果對應的分區:執行資料覆寫。與
sql
執行結果無關的分區:直接清空。
對於
partition_value
未指定的target_table
分區:sql
執行結果如果包含未指定的分區:不處理。與
sql
執行結果無關的分區:不處理。
FALSE
對於
partition_value
指定的target_table
分區:如果分區實際不存在:直接報錯,其餘分區也不執行覆寫。
sql
執行結果對應的分區:執行資料覆寫。與
sql
執行結果無關的分區:直接清空。
對於
partition_value
未指定的target_table
分區:sql
執行結果如果包含未指定的分區:不處理。與
sql
執行結果無關的分區:不處理。
使用樣本
情境一:使用預存程序將Hologres內部表資料匯入Hologres非分區表
-- 建立表A作為目標表
BEGIN;
CREATE TABLE public.tablea (
cid integer NOT NULL,
cname text,
code integer
,PRIMARY KEY (cid)
);
CALL set_table_property('public.tablea', 'orientation', 'column');
CALL set_table_property('public.tablea', 'storage_format', 'orc');
CALL set_table_property('public.tablea', 'bitmap_columns', 'cname');
CALL set_table_property('public.tablea', 'dictionary_encoding_columns', 'cname:auto');
CALL set_table_property('public.tablea', 'distribution_key', 'cid');
CALL set_table_property('public.tablea', 'time_to_live_in_seconds', '3153600000');
COMMIT;
-- 建立表B作為資料輸入
CREATE TABLE public.tableb (
cid integer NOT NULL,
cname text,
code integer
,PRIMARY KEY (cid)
);
INSERT INTO public.tableb VALUES(1,'aaa',10001),(2,'bbb','10002');
-- 使用hg_insert_overwrite 將表B資料插入表A
CALL hg_insert_overwrite('public.tablea' , 'SELECT * FROM public.tableb');
情境二:使用預存程序將Hologres內部表資料匯入Hologres分區表
-- 建立表A作為目標表
BEGIN;
CREATE TABLE public.tableA(
a text ,
b int,
c timestamp,
d text,
ds text,
PRIMARY key(ds,b)
)
PARTITION BY LIST(ds);
CALL set_table_property('public.tableA', 'orientation', 'column');
CREATE TABLE public.holo_child_1 PARTITION OF public.tableA FOR VALUES IN('20201215');
CREATE TABLE public.holo_child_2 PARTITION OF public.tableA FOR VALUES IN('20201216');
CREATE TABLE public.holo_child_3 PARTITION OF public.tableA FOR VALUES IN('20201217');
COMMIT;
-- 建立表B作為資料輸入
BEGIN;
CREATE TABLE public.tableB(
a text ,
b int,
c timestamp,
d text,
ds text,
PRIMARY key(ds,b)
)
PARTITION BY LIST(ds);
CALL set_table_property('public.tableB', 'orientation', 'column');
CREATE TABLE public.holo_child_3a PARTITION OF public.tableB FOR VALUES IN('20201215');
CREATE TABLE public.holo_child_3b PARTITION OF public.tableB FOR VALUES IN('20201216');
CREATE TABLE public.holo_child_3c PARTITION OF public.tableB FOR VALUES IN('20201217');
COMMIT;
INSERT INTO public.holo_child_3a VALUES('a',1,'2034-10-19','a','20201215');
INSERT INTO public.holo_child_3b VALUES('b',2,'2034-10-20','b','20201216');
INSERT INTO public.holo_child_3c VALUES('c',3,'2034-10-21','c','20201217');
-- 使用insert overwrite 將表B資料插入表A
CALL hg_insert_overwrite('public.tableA' , '20201215',$$SELECT * FROM public.tableB WHERE ds='20201215'$$);
情境三:使用預存程序將MaxCompute非分區表資料匯入Hologres
-- 在MaxCompute中建立一張非分區表。樣本選用MaxCompute公告資料集public_data專案下的customer表資料,其表DDL如下。
CREATE TABLE IF NOT EXISTS public_data.customer(
c_customer_sk BIGINT,
c_customer_id STRING,
c_current_cdemo_sk BIGINT,
c_current_hdemo_sk BIGINT,
c_current_addr_sk BIGINT,
c_first_shipto_date_sk BIGINT,
c_first_sales_date_sk BIGINT,
c_salutation STRING,
c_first_name STRING,
c_last_name STRING,
c_preferred_cust_flag STRING,
c_birth_day BIGINT,
c_birth_month BIGINT,
c_birth_year BIGINT,
c_birth_country STRING,
c_login STRING,
c_email_address STRING,
c_last_review_date STRING,
useless STRING);
-- 在Hologres中建立一張外部表格,用於映射MaxCompute中的源頭資料表。
CREATE FOREIGN TABLE customer (
"c_customer_sk" int8,
"c_customer_id" text,
"c_current_cdemo_sk" int8,
"c_current_hdemo_sk" int8,
"c_current_addr_sk" int8,
"c_first_shipto_date_sk" int8,
"c_first_sales_date_sk" int8,
"c_salutation" text,
"c_first_name" text,
"c_last_name" text,
"c_preferred_cust_flag" text,
"c_birth_day" int8,
"c_birth_month" int8,
"c_birth_year" int8,
"c_birth_country" text,
"c_login" text,
"c_email_address" text,
"c_last_review_date" text,
"useless" text
)
SERVER odps_server
OPTIONS (project_name 'public_data', table_name 'customer');
-- 在Hologres中建立一張內部表(以列存表為例),用於接收MaxCompute源頭表資料。
BEGIN;
CREATE TABLE public.holo_customer (
"c_customer_sk" int8,
"c_customer_id" text,
"c_current_cdemo_sk" int8,
"c_current_hdemo_sk" int8,
"c_current_addr_sk" int8,
"c_first_shipto_date_sk" int8,
"c_first_sales_date_sk" int8,
"c_salutation" text,
"c_first_name" text,
"c_last_name" text,
"c_preferred_cust_flag" text,
"c_birth_day" int8,
"c_birth_month" int8,
"c_birth_year" int8,
"c_birth_country" text,
"c_login" text,
"c_email_address" text,
"c_last_review_date" text,
"useless" text
);
COMMIT;
-- 匯入資料至Hologres。
IMPORT FOREIGN SCHEMA <project_name> LIMIT TO
(customer) FROM server odps_server INTO PUBLIC options(if_table_exist 'update');--更新外部表格
SELECT pg_sleep(30);--等待一些時間再匯入Hologres,以防Hologres meta資訊更新緩衝慢導致的資料不一致而同步不成功
CALL hg_insert_overwrite('holo_customer', 'SELECT * FROM customer where c_birth_year > 1980');
-- 在Hologres中查詢MaxCompute源表中的資料。
SELECT * FROM holo_customer limit 10;
情境四:使用預存程序將MaxCompute分區表資料匯入Hologres
-- 在MaxCompute中建立一張分區表。
DROP TABLE IF EXISTS odps_sale_detail;
CREATE TABLE IF NOT EXISTS odps_sale_detail
(
shop_name STRING
,customer_id STRING
,total_price DOUBLE
)
PARTITIONED BY
(
sale_date STRING
)
;
-- 向源表增加分區20210815
ALTER TABLE odps_sale_detail ADD IF NOT EXISTS PARTITION(sale_date='20210815')
;
-- 向分區寫入資料
INSERT OVERWRITE TABLE odps_sale_detail PARTITION(sale_date='20210815') VALUES
('s1','c1',100.1),
('s2','c2',100.2),
('s3','c3',100.3)
;
-- 在Hologres中建立一張外部表格,用於映射MaxCompute中的源頭資料表。
DROP FOREIGN TABLE IF EXISTS odps_sale_detail;
-- 建立外部表格
IMPORT FOREIGN SCHEMA <maxcompute_project> LIMIT TO
(
odps_sale_detail
)
FROM SERVER odps_server INTO public
OPTIONS(if_table_exist 'error',if_unsupported_type 'error');
-- 在Hologres中建立一張內部表,用於接收MaxCompute源頭表資料。
DROP TABLE IF EXISTS holo_sale_detail;
-- 建立Hologres分區表(內部表)
BEGIN ;
CREATE TABLE IF NOT EXISTS holo_sale_detail
(
shop_name TEXT
,customer_id TEXT
,total_price FLOAT8
,sale_date TEXT
)
PARTITION BY LIST(sale_date);
COMMIT;
-- 匯入資料至Hologres。
CALL hg_insert_overwrite('holo_sale_detail', '20210815', $$SELECT * FROM public.odps_sale_detail WHERE sale_date='20210815'$$);
-- 在Hologres中查詢MaxCompute源表中的資料。
SELECT * FROM holo_sale_detail;
情境五:使用預存程序將MaxCompute分區表資料匯入Hologres分區父表
-- 在MaxCompute中建立一張分區表。
DROP TABLE IF EXISTS odps_sale_detail;
CREATE TABLE IF NOT EXISTS odps_sale_detail
(
shop_name STRING
,customer_id STRING
,total_price DOUBLE
)
PARTITIONED BY
(
sale_date STRING
)
;
-- 向源表增加分區20210815和20210816
ALTER TABLE odps_sale_detail ADD IF NOT EXISTS PARTITION(sale_date='20210815')
;
ALTER TABLE odps_sale_detail ADD IF NOT EXISTS PARTITION(sale_date='20210816')
;
-- 向分區寫入資料
INSERT OVERWRITE TABLE odps_sale_detail PARTITION(sale_date='20210815') VALUES
('s1','c1',100.1),
('s2','c2',100.2),
('s3','c3',100.3)
;
INSERT OVERWRITE TABLE odps_sale_detail PARTITION(sale_date='20210816') VALUES
('s1','c1',100.1),
('s2','c2',100.2),
('s3','c3',100.3)
;
-- 在Hologres中建立一張外部表格,用於映射MaxCompute中的源頭資料表。
DROP FOREIGN TABLE IF EXISTS odps_sale_detail;
-- 建立外部表格
IMPORT FOREIGN SCHEMA <maxcompute_project> LIMIT TO
(
odps_sale_detail
)
FROM SERVER odps_server INTO public
OPTIONS(if_table_exist 'error',if_unsupported_type 'error');
-- 在Hologres中建立一張內部表,用於接收MaxCompute源頭表資料。
DROP TABLE IF EXISTS holo_sale_detail;
-- 建立Hologres分區表(內部表)
BEGIN ;
CREATE TABLE IF NOT EXISTS holo_sale_detail
(
shop_name TEXT
,customer_id TEXT
,total_price FLOAT8
,sale_date TEXT
)
PARTITION BY LIST(sale_date);
COMMIT;
-- 匯入資料至Hologres。不指定分區子表且auto_create_partition為TRUE,系統會自動建立兩個分區子表並匯入資料
CALL hg_insert_overwrite ('holo_sale_detail', $$SELECT * FROM public.odps_sale_detail$$, TRUE);
-- 在Hologres中查詢資料。
SELECT * FROM holo_sale_detail;
參數說明:
maxcompute_project:MaxCompute分區表所在的專案名稱。
使用暫存資料表實現INSERT OVERWRITE功能
命令格式
您可以使用如下SQL語句實現INSERT OVERWRITE的功能。
BEGIN ;
-- 清理潛在的暫存資料表
DROP TABLE IF EXISTS <table_new>;
-- 建立暫存資料表
SET hg_experimental_enable_create_table_like_properties=on;
CALL HG_CREATE_TABLE_LIKE ('<table_new>', 'select * from <table>');
COMMIT ;
-- 向暫存資料表插入資料
INSERT INTO <table_new> [( <column> [, ...] )]
VALUES ( {<expression>} [, ...] )
[, ...] | <query>}
ANALYZE <table_new>;
BEGIN ;
-- 刪除舊錶
DROP TABLE IF EXISTS <table>;
-- 暫存資料表改名
ALTER TABLE <table_new> RENAME TO <table>;
COMMIT ;
參數說明
參數 | 說明 |
table_new | 新建立的暫存資料表名稱。 表名稱也可以使用 |
table | 已存在的表名稱。 表名稱也可以使用 |
暫存資料表DDL | 建立暫存資料表有如下兩種方式。
|
使用樣本
情境一:MaxCompute向Hologres的非分區表匯入資料
在MaxCompute向Hologres匯入資料的情境中,希望將資料全量覆蓋,常見於離線加工後的結果表匯出為線上服務表。此情境使用樣本如下所示,將MaxCompute中的odps_region_10g表的資料寫入Hologres的region表中,且將Hologres中region表的資料全量覆蓋。
BEGIN ;
-- 清理潛在的暫存資料表
DROP TABLE IF EXISTS public.region_new;
-- 建立暫存資料表
SET hg_experimental_enable_create_table_like_properties=on;
CALL HG_CREATE_TABLE_LIKE ('public.region_new', 'select * from public.region');
COMMIT ;
-- 向暫存資料表插入資料
INSERT INTO public.region_new
SELECT *
FROM public.odps_region_10g;
ANALYZE public.region_new;
BEGIN ;
-- 刪除舊錶
DROP TABLE IF EXISTS public.region;
-- 暫存資料表改名
ALTER TABLE IF EXISTS public.region_new RENAME TO region;
COMMIT ;
情境二:MaxCompute向Hologres的分區表匯入資料
在每天定期更新MaxCompute分區表的資料,且需要將MaxCompute分區表向Hologres的分區表匯入資料的情境中,希望將資料全量覆蓋,實現離線資料對即時資料的修正。此情境使用樣本如下所示,將MaxCompute中的odps_lineitem_10g表的資料寫入Hologres的lineitem表中,且全量覆蓋Hologres中lineitem表的資料,兩個表都是按照ds欄位按天分區。
BEGIN ;
-- 清理潛在的暫存資料表
DROP TABLE IF EXISTS public.lineitem_new_20210101;
-- 建立暫存資料表
SET hg_experimental_enable_create_table_like_properties=on;
CALL HG_CREATE_TABLE_LIKE ('public.lineitem_new_20210101', 'select * from public.lineitem');
COMMIT ;
-- 向暫存資料表插入資料
INSERT INTO public.lineitem_new_20210101
SELECT *
FROM public.odps_lineitem_10g
WHERE DS = '20210101'
ANALYZE public.lineitem_new_20210101;
BEGIN ;
-- 刪除舊分區
DROP TABLE IF EXISTS public.lineitem_20210101;
-- 暫存資料表改名
ALTER TABLE public.lineitem_new_20210101 RENAME TO lineitem_20210101;
-- 將暫存資料表綁定至指定分區表
ALTER TABLE public.lineitem ATTACH PARTITION lineitem_20210101 FOR VALUES IN ('20210101');
COMMIT ;
情境三:Hologres向MaxCompute的非分區表匯入資料
如果您需要從Hologres向MaxCompute的非分區表匯入資料,建議採用暫存資料表匯入的方式,匯入完成後將暫存資料表改名為正式表即可。此情境使用樣本如下所示,將Hologres中holotable表的資料寫入MaxCompute的mc_holotable表中,且將MaxCompute的mc_holotable表資料全量覆蓋。
-- 在MC中建立目標表的暫存資料表
CREATE TABLE if not exists mc_holotable_temp(
age int,
job string,
name string
);
-- 在Hologres中建立暫存資料表的映射
CREATE FOREIGN TABLE "public"."mapping_holotable_temp" (
"age" int,
"job" text,
"name" text
)
SERVER odps_server
OPTIONS (project_name 'DLF_test',table_name 'mc_holotable_temp');
-- 在Hologres中更新原始表
UPDATE holotable SET "job" = 'president' WHERE "name" = 'Lily';
-- 將更新後的資料寫入暫存資料表的映射
INSERT INTO mapping_holotable_temp SELECT * FROM holotable;
-- 在MaxCompute中刪除舊的目標表
DROP TABLE IF EXISTS mc_holotable;
-- 暫存資料表更名為目標表即可
ALTER TABLE mc_holotable_temp RENAME TO mc_holotable;
匯入資料支援部分匯入和全表匯入兩種方式:
匯出部分欄位樣本:
INSERT INTO mapping_holotable_temp SELECT x,x,x FROM holotable; --x,x,x可以替換為您需要匯出的欄位名
匯出全部欄位樣本:
INSERT INTO mapping_holotable_temp SELECT * FROM holotable;