iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 26
2
Postgresql hstore 資料型態初步介紹

hstore 是 Postgresql 引進多年的一個強大的套件,
但是較少為人所知.現在就來做一初步的介紹.

首先需要安裝 hstore extension.

create extension hstore;

hstore 是 Key -> Value 成對的集合(Set)資料型態,Key跟Value都是text(string).
每個 key 都是 Unique.
表達的方式為

"key1" => "val1", "key2" => "val2" ...

接著來看建構的方式.

使用轉型的方式,直接將 text 的內容

select 'a=>1,a=>2'::hstore;
+----------+
|  hstore  |
+----------+
| "a"=>"1" |
+----------+

select 'a=>天津風, b=>島風'::hstore;
+----------------------------+
|           hstore           |
+----------------------------+
| "a"=>"天津風", "b"=>"島風" |
+----------------------------+

或是使用建構函數 hsotre(),有四種方式
hstore(record), hstore(text, text) 以及搭配 array 的
hstore(text[]), hstore(text[], text[]).

record方式
select hstore(row('天津風', '島風'))
     , hstore(row(1, 2))
     , hstore(row('1', '2'));

+------------------------------+----------------------+----------------------+
|            hstore            |        hstore        |        hstore        |
+------------------------------+----------------------+----------------------+
| "f1"=>"天津風", "f2"=>"島風"   | "f1"=>"1", "f2"=>"2" | "f1"=>"1", "f2"=>"2" |
+------------------------------+----------------------+----------------------+

會自動給予 key, f1,f2, ...fn 型態.

後續方法都是要我們給定key.

hstore(text, text)方式
select hstore('1', '冬月');

+-------------+
|   hstore    |
+-------------+
| "1"=>"冬月" |
+-------------+

hstore(text[], text[])方式
select hstore(array['1', '2'], array['天津風', '島風']);
+----------------------------+
|           hstore           |
+----------------------------+
| "1"=>"天津風", "2"=>"島風" |
+----------------------------+

可以推測此建構函數是將array展開後,使用hstore(text, text)來配對.

第四種方式是 hstore(text[])
select hstore(array['1', '天津風', '2', '島風'])
     , hstore(array[['3', '秋月'], ['4', '涼月']]);
     
+----------------------------+--------------------------+
|           hstore           |          hstore          |
+----------------------------+--------------------------+
| "1"=>"天津風", "2"=>"島風"  | "3"=>"秋月", "4"=>"涼月"   |
+----------------------------+--------------------------+

將array展開,依序解析 key->value pair.
也可以這樣

select hstore(array['1', '天津風', '2', '島風'])
    || hstore(array[['3', '秋月'], ['4', '涼月']]);

+------------------------------------------------------+
|                       ?column?                       |
+------------------------------------------------------+
| "1"=>"天津風", "2"=>"島風", "3"=>"秋月", "4"=>"涼月"    |
+------------------------------------------------------+

看完建構式接著來看運算子,經過這段時間,相信大家對 pg_catalog 應該很熟悉了.
可以透過以下查詢

select lpad(replace(oprleft::regtype::text, '-', ''), 10, ' ')
    || ' ' || rpad(oprname, 4, ' ')
    || ' ' || rpad(oprright::regtype::text, 6, ' ')
    || ' = ' || oprresult::regtype::text as expr
     , oprcode::regproc
  from pg_catalog.pg_operator
 where oprleft::regtype = 'hstore'::regtype
    or oprright::regtype = 'hstore'::regtype
    or oprresult::regtype = 'hstore'::regtype
 order by oprname desc, oprleft, oprright, oprresult;

+-------------------------------------+------------------+
|                expr                 |     oprcode      |
+-------------------------------------+------------------+
|     hstore ~    hstore = boolean    | hs_contained     |
|     hstore ||   hstore = hstore     | hs_concat        |
|     hstore @>   hstore = boolean    | hs_contains      |
|     hstore @    hstore = boolean    | hs_contains      |
|     hstore ?|   text[] = boolean    | exists_any       |
|     hstore ?&   text[] = boolean    | exists_all       |
|     hstore ?    text   = boolean    | exist            |
|     hstore =    hstore = boolean    | hstore_eq        |
|     hstore <@   hstore = boolean    | hs_contained     |
|     hstore <>   hstore = boolean    | hstore_ne        |
|     hstore ->   text   = text       | fetchval         |
|     hstore ->   text[] = text[]     | slice_array      |
|     hstore -    text   = hstore     | miku.delete      |
|     hstore -    text[] = hstore     | miku.delete      |
|     hstore -    hstore = hstore     | miku.delete      |
|            %%   hstore = text[]     | hstore_to_array  |
|            %#   hstore = text[]     | hstore_to_matrix |
|     hstore #>=# hstore = boolean    | hstore_ge        |
|     hstore #>#  hstore = boolean    | hstore_gt        |
| anyelement #=   hstore = anyelement | populate_record  |
|     hstore #<=# hstore = boolean    | hstore_le        |
|     hstore #<#  hstore = boolean    | hstore_lt        |
+-------------------------------------+------------------+
(22 rows)

可是官網文件上只列出了14個,可以觀察到有一些常見的像是 = <> ~ 在官網文件上省略掉了,
歸類到其他部分.或是功能相同,但是使用了不同的運算子.
因為有些是舊式符號如 ~ , 後來改用 <@.
有些是 hstore 函數的對應,也有為了函數使用的運算子,本身不直接使用的,如 #=
由此可知,對 pg_catalog 的理解,有助於我們更有系統化的來理解 Postgresql.

來看一些例子

select a.ddteam, b.amatsukaze, c.fuyuzuki
     , b.amatsukaze ~ a.ddteam as "amatsukaze contained ddteam"
     , c.fuyuzuki <@ a.ddteam as "fuyuzuki contained ddteam"
     , a.ddteam @> b.amatsukaze as "ddteam contains amatsukaze"
     , a.ddteam @ c.fuyuzuki as "ddteam contains fuyuzuki"
     , a.ddteam || c.fuyuzuki as "ddteam concat fuyuzuki, replace amatsukaze"
  from (values (hstore(array['1', '2'], array['天津風','島風']))) a (ddteam)
     , (values (hstore('1', '天津風'))) b (amatsukaze)
     , (values ('1=>冬月'::hstore)) c (fuyuzuki); 

+-[ RECORD 1 ]-------------------------------+----------------------------+
| ddteam                                     | "1"=>"天津風", "2"=>"島風" |
| amatsukaze                                 | "1"=>"天津風"              |
| fuyuzuki                                   | "1"=>"冬月"                |
| amatsukaze contained ddteam                | t                          |
| fuyuzuki contained ddteam                  | f                          |
| ddteam contains amatsukaze                 | t                          |
| ddteam contains fuyuzuki                   | f                          |
| ddteam concat fuyuzuki, replace amatsukaze | "1"=>"冬月", "2"=>"島風"   |
+--------------------------------------------+----------------------------+

最後的欄位,因為天津風跟冬月的key都是1, 而hstore 的key是unique的, 新的取代舊的.

關於 key 的

select a.ddteam
     , ddteam ? '3' as "ddteam contain key 1?"
     , ddteam ?& array['1','2'] as "ddteam contain all ['1','2']?"
     , ddteam ?| array['4','5'] as "ddteam contain any ['4','5']?"
  from (values (hstore(array['1', '2'], array['天津風','島風']))) a (ddteam); 

+-[ RECORD 1 ]------------------+----------------------------+
| ddteam                        | "1"=>"天津風", "2"=>"島風"   |
| ddteam contain key 1?         | f                          |
| ddteam contain all ['1','2']? | t                          |
| ddteam contain any ['4','5']? | f                          |
+-------------------------------+----------------------------+

關於 fetch value 的; 結果會是 text 與 text[] 兩種型態.

select ddteam
     , ddteam -> '2' as "ddteam fetch key 2"
     , ddteam -> '3' as "ddteam fetch key 3"
     , ddteam -> array['1', '2'] as "ddteam fetch key [1,2]"
     , ddteam -> array['1', '2', '3'] as "ddteam fetch key [1,2,3]"
  from (values (hstore(array['1', '2'], array['天津風','島風']))) a (ddteam); 

+-[ RECORD 1 ]-------------+----------------------------+
| ddteam                   | "1"=>"天津風", "2"=>"島風" |
| ddteam fetch key 2       | 島風                       |
| ddteam fetch key 3       | ¤                          |
| ddteam fetch key [1,2]   | {天津風,島風}              |
| ddteam fetch key [1,2,3] | {天津風,島風,NULL}         |
+--------------------------+----------------------------+

沒有對應value的就會顯示 NULL, 因為我在psqlrc 裡面設定 null 顯示為 ¤. 
但是進入 array 的, 在顯示時,psql 未將其改成用 ¤ 顯示.
查驗看看

select (ddteam -> '3' ) is null as "is null in ddtem #3"
     , ((ddteam -> array['1', '2', '3'])[3])  is null as "is null in array #3"
  from (values (hstore(array['1', '2'], array['天津風','島風']))) a (ddteam);

+---------------------+---------------------+
| is null in ddtem #3 | is null in array #3 |
+---------------------+---------------------+
| t                   | t                   |
+---------------------+---------------------+

今天先介紹到此.


上一篇
Postgresql Sequence 與 Surrogate_key
下一篇
Postgresql hstore 的運算子,函數及應用介紹
系列文
以Postgresql為主,聊聊資料庫.31

尚未有邦友留言

立即登入留言