全部產品
Search
文件中心

Hologres:INSERT OVERWRITE

更新時間:Sep 13, 2024

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); 
  • Hologres V3.0版本前,CALL hg_insert_overwrite在元倉(hologres.hg_query_log)中只有CALL一條記錄。V3.0版本起,CALL hg_insert_overwrite會產生多條記錄,包括CALL、建立暫存資料表、INSERT匯入暫存資料表、刪除原表、RENAME暫存資料表等記錄。多條記錄可以通過transaction id實現關聯,關聯樣本如下。

    SELECT
        query_id,
        query,
        extended_info
    FROM
        hologres.hg_query_log
    WHERE
        extended_info ->> 'source_trx' = 'transaction_id' -- 通過CALL本身記錄的trans_id欄位可以取到transaction id
    ORDER BY
        query_start
    ;

使用預存程序實現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

分區表的分區值。

  • V3.0版本前,如果target_table為分區父表,則必須指定partition_value(TEXT類型),即只支援匯入一張分區子表。如果分區子表不存在,則會自動建立。

  • V3.0版本起,如果target_table為分區父表,可選是否指定partition_value(ARRAY類型),具體行為見下文。

sql

標準的SELECT語句。

可用來查詢MaxCompute或者Hologres的表,需確保SELECT出來的分區欄位值必須完全等於partition_value。如果SQL語句中含有單引號(''),需要通過$$sql$$改寫sql,以自動實現單引號轉義。

  • V3.0版本前,需確保SELECT出的分區欄位值必須完全等於partition_value

  • V3.0版本起,SELECT出的分區欄位值可以不完全等於partition_value具體行為見下文。

auto_create_partition

是否自動建立分區。僅V3.0及以上版本支援該參數。

  • TRUE:當sql的執行結果中包含不存在的分區子表時,自動建立對應分區子表。

  • FALSE(預設值):當sql的執行結果中包含不存在的分區子表時,不自動建立對應的分區子表。

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

新建立的暫存資料表名稱。

表名稱也可以使用Schema.Table格式。

table

已存在的表名稱。

表名稱也可以使用Schema.Table格式。

暫存資料表DDL

建立暫存資料表有如下兩種方式。

  • 通過複製已有表建立新表的結構

    SET hg_experimental_enable_create_table_like_properties=on;
    CALL HG_CREATE_TABLE_LIKE ('<table_new>', 'select * from <table>');
  • 建立表的結構

    CREATE TABLE IF NOT EXISTS <table_new> ([
      {
       column_name column_type [column_constraints, [...]]
       | table_constraints
       [, ...]
      }
    ]);
    
    CALL set_table_property('<table_new>', property, value);

使用樣本

情境一: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;