全部產品
Search
文件中心

AnalyticDB:Array數群組類型

更新時間:Feb 05, 2024

雲原生資料倉儲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])

1
2

unnest(anyarray, anyarray [, ...])

setof anyelement, anyelement [, ...]

擴充多個數組(可能是不同的類型)到一組行。 僅允許在FROM子句中使用。

unnest(ARRAY[1,2],ARRAY['foo','bar','baz'])

1 foo
2 bar
NULL 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_upperarray_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_prependarray_appendarray_cat函數構造數組值。array_prependarray_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文檔