iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 23
0
Postgresql 的 Sequence 關聯物件

Postgresql 與 Oracle 均有 Sequence 此一關聯物件.
為何叫做關聯物件? 關聯物件包含 Table / View / Sequence 等,
另外有 foreign table (PG) 或是 External Table (Oracle) 等延伸型態.
關聯物件都有相同的基本屬性,在前面的 四探 Postgresql Catalog 中

https://ithelp.ithome.com.tw/articles/10218574

已經有出現關於 Sequence 的查詢 pg_class, pg_sequence 的方法.

Sequence 甚至可以直接查詢相關資訊欄位, 如 極大值,遞增值等.
在 10 版以後,一些相關資訊移到pg_sequence 存放,留下三個欄位,一般只需查 last_value, is_called.

通常在 psql 下 透過 \d 就能列出 關聯物件, sequence 也是關聯物件會一起顯示.
\d seq_name 這樣就能顯示一些相關資訊,如下所示:

\d t190920_testid_seq
                   Sequence "miku.t190920_testid_seq"
+---------+-------+---------+------------+-----------+---------+-------+
|  Type   | Start | Minimum |  Maximum   | Increment | Cycles? | Cache |
+---------+-------+---------+------------+-----------+---------+-------+
| integer |     1 |       1 | 2147483647 |         1 | no      |     1 |
+---------+-------+---------+------------+-----------+---------+-------+
Owned by: miku.t190920.testid

其實就是透過 pg_seqnence 等查詢,而且注意到是有跟 table miku.t190920 的欄位 testid 
有關聯.

這是之前 四探 Postgresql Catalog 中使用的.但是注意到這裡的資訊是沒有 sequence 使用到哪裡了.
這時候要直接查詢 sequence 的 last_value 欄位,而不是用 select currval('sequence');
會得到錯誤
select currval('sequence');
ERROR:  55000: currval of sequence "t190920_testid_seq" is not yet defined in this session

若是呼叫取值 nextval(), 該值就跳過了.

select last_value
  from t190920_testid_seq;

+------------+
| last_value |
+------------+
|          1 |
+------------+

若是要逐一檢查,對DBA的日常作業來說,負擔頗重,故一般會撰寫一些 function 來輔助.
後面會跟大家分享一個 function.在這之前,先來建立兩個 sequence.並進行操作

create sequence nana.s1 
as smallint;

create sequence nana.s2
as bigint;

select nextval('nana.s1');

+---------+
| nextval |
+---------+
|       1 |
+---------+

select setval('nana.s1', 3);

+--------+
| setval |
+--------+
|      3 |
+--------+

select nextval('nana.s1');

+---------+
| nextval |
+---------+
|       4 |
+---------+

select nextval('nana.s2');

+---------+
| nextval |
+---------+
|       1 |
+---------+

select setval('nana.s2', 40000, false);

+--------+
| setval |
+--------+
|  40000 |
+--------+

select nextval('nana.s2');

+---------+
| nextval |
+---------+
|   40000 |
+---------+

兩次設定值的操作,差異在 is_called flag 的設定.default 是 true.
設定為 false時, nextval()會取目前值,反之則取下一值.
當呼叫 nextval()取值時,亦會設定 is_called flag.

接著就是剛才提到的 function.

-- ****** function start ******
create or replace function sequence_list()
returns table( seq_name text
        , seqtyp text
        , is_call boolean
        , current_value bigint
        , limt bigint
        , remaining bigint)
as $code$
declare
    cmd text;
    schemaz name;
    seqz name;
    seqid oid;
begin
    for schemaz, seqz, seqid in
        select relnamespace::regnamespace
             , relname
             , oid
          from pg_class
         where relkind = 'S'
         order by relnamespace
    loop
        raise debug 'Inspecting %.%', schemaz, seqz;
        
        cmd := format('select ''%s.%s''
                            , s.seqtypid::regtype::text
                            , is_called
                            , last_value
                            , s.seqmax
                            , (s.seqmax - last_value) / s.seqincrement
                         from %I.%I
                            , pg_sequence s 
                        WHERE s.seqrelid = %s'
                      , quote_ident( schemaz )
                      , quote_ident( seqz )
                      , schemaz
                      , seqz
                      , seqid
               );
        raise debug 'Query [%]', cmd;
        return query execute cmd;
    end loop;
end
$code$
language plpgsql
strict;

-- ****** function end ******
'
可以用以下方式呼叫
select * from sequence_list();

或是再搭配函數 pg_size_pretty() 提高數字的可讀性,
當然也可以利用 where 做過濾,或是排序,這些部分可自行
靈活運用.

select seq_name
     , seqtyp
     , is_call
     , current_value
     , pg_size_pretty(limt)
     , pg_size_pretty(remaining)
  from sequence_list();

+-[ RECORD 1 ]---+---------------------------+
| seq_name       | miku.sample_arr_id_seq    |
| seqtyp         | integer                   |
| is_call        | t                         |
| current_value  | 2                         |
| pg_size_pretty | 2048 MB                   |
| pg_size_pretty | 2048 MB                   |
....
+-[ RECORD 10 ]--+---------------------------+
| seq_name       | nana.s1                   |
| seqtyp         | smallint                  |
| is_call        | t                         |
| current_value  | 4                         |
| pg_size_pretty | 32 kB                     |
| pg_size_pretty | 32 kB                     |
+-[ RECORD 11 ]--+---------------------------+
| seq_name       | nana.s2                   |
| seqtyp         | bigint                    |
| is_call        | t                         |
| current_value  | 40000                     |
| pg_size_pretty | 8388608 TB                |
| pg_size_pretty | 8388608 TB                |
+----------------+---------------------------+

這樣就能方便觀察 sequence 情況, 主要是針對數值部分.
至於 cache / cycle 這兩個值,透過 psql \d 時就會顯示,
在上面就有例子了.為了讓函數精簡起見,就不列入其中.

但這個函數是有缺陷的.原因在 計算 remaining 時的算式
(s.seqmax - last_value) / s.seqincrement 

雖然有考慮到了 seqincrement , 但是 Postgresql 的 sequence 
不全然是遞增的,seqincrement 可以設定為 負值,此時是遞減的.
計算時應該改用 seqmin,不過沒關係.明天我們將再繼續就 sequence 
及其應用做探討,屆時再來修正此函數.


上一篇
Postgresql 的UUID資料型態
下一篇
Postgresql Sequence 的操作及容量
系列文
以Postgresql為主,聊聊資料庫.31

尚未有邦友留言

立即登入留言