雲原生資料倉儲AnalyticDB PostgreSQL版支援將欄位定義為數群組類型。數群組類型可以是任何基礎類型、使用者自訂類型、枚舉類型或複合類型。本文介紹雲原生資料倉儲AnalyticDB PostgreSQL版數群組類型的使用,包括建表、資料寫入、查詢、修改及數組的操作符和函數。
數群組類型的聲明
數群組類型是通過在數組元素類型名後附加方括弧[]來命名的。
樣本:執行以下命令,建立一個由基礎類型數組構成的表。
CREATE TABLE sal_emp (
id int,
name text,
pay_by_quarter integer[],
schedule text[][]
) DISTRIBUTED BY(id);表的詳細資料如下:
表名:
sal_emp。id欄位:int類型,記錄員工號。name欄位:text類型字串,記錄員工姓名。pay_by_quarter欄位:一維integer數組,記錄員工每個季度的薪資。schedule欄位:兩維text數組,記錄員工周計劃。該表按
id雜湊分布。
數組值寫入
通過INSERT語句進行數組值寫入,樣本如下。
INSERT INTO sal_emp
VALUES (1,
'Bill',
'{10000, 10000, 10000, 10000}',
'{{"meeting", "lunch"}, {"training", "presentation"}}');
INSERT INTO sal_emp
VALUES (2,
'Carol',
'{20000, 25000, 25000, 25000}',
'{{"breakfast", "consulting"}, {"meeting", "lunch"}}');查詢資料寫入情況。
SELECT * FROM sal_emp;返回結果如下。
id | name | pay_by_quarter | schedule
----+-------+---------------------------+-------------------------------------------
2 | Carol | {20000,25000,25000,25000} | {{breakfast,consulting},{meeting,lunch}}
1 | Bill | {10000,10000,10000,10000} | {{meeting,lunch},{training,presentation}}
(2 rows)訪問數組
AnalyticDB PostgreSQL版使用以1為基的數組,即一個n元素的數組從array[1]開始,到array[n]結束。您也可以訪問一個數組的任意矩形片段(子數組),對於一維或更多維陣列, 可以用下標下界:下標上界表示一個數組的某個片段。
本章以查詢sal_emp表中的資料為例,示範如何訪問數組的一個元素。
樣本一: 查詢在第二季度薪資發生變化的員工姓名,樣本如下。
SELECT name FROM sal_emp WHERE pay_by_quarter[1] <> pay_by_quarter[2];返回結果如下。
name ------- Carol (1 row)樣本二:查詢所有員工第三季度的薪資,樣本如下。
SELECT pay_by_quarter[3] FROM sal_emp;返回結果如下。
pay_by_quarter ---------------- 10000 25000 (2 rows)樣本三:查詢員工Bill該周前兩天的第一件計劃,樣本如下。
SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill';返回結果如下。
schedule ------------------------ {{meeting},{training}} (1 row)
數組函數
AnalyticDB PostgreSQL版支援的數組函數如下表。
函數 | 傳回型別 | 描述 | 樣本 | 結果 |
array_append(anyarray, anyelement) | anyarray | 向數組末尾添加元素。 | array_append(ARRAY[1,2], 3) | {1,2,3} |
array_cat(anyarray, anyarray) | anyarray | 串連兩個數組。 | array_cat(ARRAY[1,2,3], ARRAY[4,5]) | {1,2,3,4,5} |
array_ndims(anyarray) | int | 返回數組的維數。 | array_ndims(ARRAY[[1,2,3], [4,5,6]]) | 2 |
array_dims(anyarray) | text | 返回數組維數的文本表示。 | array_dims(ARRAY[[1,2,3], [4,5,6]]) | [1:2][1:3] |
array_fill(anyelement, int[], [, int[]]) | anyarray | 返回數組初始化提供的值和維度,可選下界不為1。 | array_fill(7, ARRAY[3], ARRAY[2]) | [2:4]={7,7,7} |
array_length(anyarray, int) | int | 返回數組維度長度。 | array_length(array[1,2,3], 1) | 3 |
array_lower(anyarray, int) | int | 返回數組維數的下界。 | array_lower('[0:2]={1,2,3}'::int[], 1) | 0 |
array_prepend(anyelement, anyarray) | anyarray | 向數組開頭添加元素。 | array_prepend(1, ARRAY[2,3]) | {1,2,3} |
array_remove(anyarray, anyelement) | anyarray | 從數組中刪除所有等於給定值的元素(數組必須是一維的)。 | array_remove(ARRAY[1,2,3,2], 2) | {1,3} |
array_replace(anyarray, anyelement, anyelement) | anyarray | 用新值替換每個等於給定值的數組元素。 | array_replace(ARRAY[1,2,5,4], 5, 3) | {1,2,3,4} |
array_to_string(anyarray, text [, text]) | text | 使用分隔字元和NULL字串串連數組元素。 | array_to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*') | 1,2,3,*,5 |
array_upper(anyarray, int) | int | 返回數組維數的上界。 | array_upper(ARRAY[1,8,3,7], 1) | 4 |
cardinality(anyarray) | int | 返回數組中的總元素數量。如果數組是空,則為0。 | cardinality(ARRAY[[1,2],[3,4]]) | 4 |
string_to_array(text, text [, text]) | text[] | 使用指定的分隔字元和NULL字串把字串分裂成數組元素。 | string_to_array('xx~^~yy~^~zz', '~^~', 'yy') | {xx,NULL,zz} |
unnest(anyarray) | setof anyelement | 擴大一個數組為一組行。 | unnest(ARRAY[1,2]) | |
unnest(anyarray, anyarray [, ...]) | setof anyelement, anyelement [, ...] | 擴充多個數組(可能是不同的類型)到一組行。 僅允許在FROM子句中使用。 | unnest(ARRAY[1,2],ARRAY['foo','bar','baz']) | |
在
string_to_array函數中,如果分隔字元參數是NULL, 輸入字串中的每個字元將在結果數組中變成獨立的元素。如果分隔字元是一個空白字串, 那麼整個輸入字串將變為一個元素的數組。否則輸入字串將在每個分隔字串處分裂。在
string_to_array函數中,如果省略NLL字串參數或為NULL, 將沒有輸入字串的子串被NULL代替。在array_to_string函數中, 如果省略NULL字串參數或為NULL,在數組中的任何NULL元素將被跳過, 並且不再輸出字串中出現。
樣本
樣本一:使用
array_dims函數查詢數組的當前維數,樣本如下。SELECT array_dims(schedule) FROM sal_emp WHERE name = 'Carol';返回結果如下。
array_dims ------------ [1:2][1:2] (1 row)樣本二:使用
array_upper和array_lower函數查詢數組特定維的上界和下界,樣本如下。SELECT array_upper(schedule, 1), array_lower(schedule, 1) FROM sal_emp WHERE name = 'Carol';返回結果如下。
array_upper | array_lower -------------+------------- 2 | 1 (1 row)樣本三:使用
array_length函數查看特定維數數組的長度。SELECT array_length(schedule, 1) FROM sal_emp WHERE name = 'Carol';返回結果如下。
array_length -------------- 2 (1 row)樣本四:使用
cardinality函數,查看返數組中所有維度元素的總數量。SELECT cardinality(schedule) FROM sal_emp WHERE name = 'Carol';返回結果如下。
cardinality ------------- 4 (1 row)
修改數組
AnalyticDB PostgreSQL版支援修改數組,包含如下幾種修改方式:
完全代替一個數組值,樣本如下。
UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}' WHERE name = 'Carol';只更新數組中的某一個元素,樣本如下。
UPDATE sal_emp SET pay_by_quarter[4] = 15000 WHERE name = 'Bill';更新一個數組的某個片段,樣本如下。
UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}' WHERE name = 'Carol';使用串連操作符
||構造新的數組值。串連操作符允許把一個元素壓入一維數組的開頭或者結尾。樣本如下。SELECT ARRAY[1,2] || ARRAY[3,4];返回結果如下。
?column? ----------- {1,2,3,4} (1 row)串連操作符
||也支援兩個N維的數組,或者一個N維和一個N+1維的數組,樣本如下。SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];返回結果如下。
?column? --------------------- {{5,6},{1,2},{3,4}} (1 row)使用
array_prepend、array_append或array_cat函數構造數組值。array_prepend和array_append只支援一維數組,array_cat支援多維陣列。使用串連操作符比直接使用這些函數更好。實際上, 這些函數主要用於實現串連操作符。在使用者定義的建立函數裡直接使用函數可能有必要。通過函數構造數組值的樣本如下。
SELECT array_prepend(1, ARRAY[2,3]); array_prepend --------------- {1,2,3} (1 row) SELECT array_append(ARRAY[1,2], 3); array_append -------------- {1,2,3} (1 row) SELECT array_cat(ARRAY[1,2], ARRAY[3,4]); array_cat ----------- {1,2,3,4} (1 row) SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]); array_cat --------------------- {{1,2},{3,4},{5,6}} (1 row) SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]); array_cat --------------------- {{5,6},{1,2},{3,4}}
在數組中檢索
檢索一個數組中的某個數值時,必須檢索該數組中的每一個值。您可以通過數組操作符檢索合格記錄。
樣本一:檢索
pay_by_quarter欄位中包含10000元素的記錄。SELECT * FROM sal_emp WHERE 10000 = ANY (pay_by_quarter);樣本二:檢索數組中
pay_by_quarter欄位所有元素值都等於10000的記錄。SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter);樣本三:使用&&操作符檢索,左運算元是否與右運算元重疊。
SELECT * FROM sal_emp WHERE pay_by_quarter && ARRAY[10000];
AnalyticDB PostgreSQL版支援的數組操作符如下表。
操作符 | 描述 | 樣本 | 結果 |
= | 等於 | ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3] | t |
<> | 不等於 | ARRAY[1,2,3] <> ARRAY[1,2,4] | t |
< | 小於 | ARRAY[1,2,3] < ARRAY[1,2,4] | t |
> | 大於 | ARRAY[1,4,3] > ARRAY[1,2,4] | t |
<= | 小於或等於 | ARRAY[1,2,3] <= ARRAY[1,2,3] | t |
>= | 大於或等於 | ARRAY[1,4,3] >= ARRAY[1,4,3] | t |
@> | 包含 | ARRAY[1,4,3] @> ARRAY[3,1] | t |
<@ | 被包含於 | ARRAY[2,7] <@ ARRAY[1,7,4,2,6] | t |
&& | 重疊(有共同元素) | ARRAY[1,4,3] && ARRAY[2,1] | t |
|| | 數組與數組串連(一維) | ARRAY[1,2,3] || ARRAY[4,5,6] | {1,2,3,4,5,6} |
|| | 數組與數組串連(多維) | ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]] | {{1,2,3},{4,5,6},{7,8,9}} |
|| | 元素與數組串連 | 3 || ARRAY[4,5,6] | {3,4,5,6} |
|| | 數組與元素串連 | ARRAY[4,5,6] || 7 | {4,5,6,7} |
使用索引加速數組檢索
當數組中包含非常多元素,需要使用@>操作符檢索是否包含某個元素時,數組元素越多,檢索效能也越差,您可以通過構建索引來加速。
GIN索引僅支援資源類型為儲存彈性模式的執行個體,不支援Serverless執行個體。
本文以如下樣本,展示如何通過索引加速數組檢索。
-- 建立一個包含int數組列的表。
REATE TABLE multivalue (
id int,
values int[]
) DISTRIBUTED BY(id);
-- 向表中寫入資料,共寫入1000行,每行資料中的values數組包含1000000個元素。
INSERT INTO multivalue SELECT g, ARRAY(SELECT generate_series(1, 1000000)) FROM generate_series(1, 1000) as g;在沒有索引的情況下,查詢數組元素中包含800000這個元素的記錄,需要通過seq scan,效能較差。
EXPLAIN ANALYZE SELECT id FROM multivalue WHERE values @> ARRAY[800000];
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
Gather Motion 2:1 (slice1; segments: 2) (cost=0.00..1.00 rows=1 width=4) (actual time=711.216..3478.225 rows=1000 loops=1)
-> Seq Scan on multivalue (cost=0.00..1.00 rows=1 width=4) (actual time=10.420..2629.403 rows=508 loops=1)
Filter: ("values" @> '{800000}'::integer[])
Planning time: 0.080 ms
(slice0) Executor memory: 39K bytes.
(slice1) Executor memory: 12733K bytes avg x 2 workers, 12733K bytes max (seg0).
Memory used: 1048576kB
Optimizer: Postgres query optimizer
Execution time: 3483.157 ms
(9 rows)
Time: 3483.667 ms對數組列建立GIN索引後,相同查詢通過索引掃描,效能大幅提升,耗時由3483.667毫秒(ms)下降至9.731毫秒(ms)。
CREATE INDEX idx_values on multivalue USING GIN (values);
EXPLAIN ANALYZE SELECT id FROM multivalue WHERE values @> ARRAY[800000];
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Gather Motion 2:1 (slice1; segments: 2) (cost=12.04..14.10 rows=5 width=4) (actual time=3.727..4.127 rows=1000 loops=1)
-> Bitmap Heap Scan on multivalue (cost=12.04..14.10 rows=3 width=4) (actual time=1.826..1.872 rows=508 loops=1)
Recheck Cond: ("values" @> '{800000}'::integer[])
-> Bitmap Index Scan on idx_values (cost=0.00..12.04 rows=3 width=0) (actual time=1.462..1.462 rows=508 loops=1)
Index Cond: ("values" @> '{800000}'::integer[])
Planning time: 0.155 ms
(slice0) Executor memory: 49K bytes.
(slice1) Executor memory: 331K bytes avg x 2 workers, 331K bytes max (seg0). Work_mem: 9K bytes max.
Memory used: 1048576kB
Optimizer: Postgres query optimizer
Execution time: 9.138 ms
(11 rows)
Time: 9.731 ms當然,通過GIN索引提升數組檢索效能的同時,也會帶來寫入效能的下降(因為寫入資料時需要更新索引),另外索引資料本身也會佔用額外空間。業務在具體使用時,可以從查詢效能,寫入效能,儲存空間三個維度綜合來選擇是否需要索引。
相關文檔
更多關於Array類型的使用,請參見PostgreSQL文檔。