92760


### 質問の概要

PostgreSQLのsequenceでは、現在の値 + 1(INCREMENTが1の場合）の数値で採番されると考えています。  
そのため、5まで採番されている時、次にINSERTされる時は5+1で6で採番されます。  
しかし、最初の1件目のINSERTにおいては、現在の値が1であり、INCREMENTも1であるにもかかわらず、1でINSERTされるのが不思議です。規則性からすると2でINSERTされてしまう気がします。

### 再現手順

PostgreSQLで次のSQLによりsequenceを作成したとします。

[code]

    CREATE SEQUENCE test_seq
        INCREMENT 1
        START 1
        MINVALUE 1;
    
[/code]

ここで次のSQLでlast_valueを確認すると、`1`となります。

[code]

    SELECT * FROM test_seq; 
    
[/code]

そして、レコードをINSERTすると、idが`1`で採番されます。（last_value + 1 = 2 なので規則性に反する。）  
ここで、再度last_valueを確認しても、`1`のままです。（last_valueが更新されていない）

2件目をINSERTすると、idが`2`で採番されます。（last_value + 1 なので規則性に則る）  
そして、再度last_valueを確認すると`2`となります。（last_valueが正しく更新されている）

この2件目以降は規則的に続きます。つまり、1件目のみ規則性に反する動きをしているのです。

ただ機械的にlast_value+1でINSERTしているわけではなく、1件目のINSERTの場合はlast_valueをそのまま入れるなど例外的な処理が定義されているのでしょうか？  
だとしたら、そのINSERTが1件目かどうかはどうやって判断されているのでしょうか？

これが確認できる文献等、あるいは仕組み等をおしえていただけませんでしょうか。  
何卒よろしくお願いいたします。

## バージョン

PostgreSQL14

## 参考

  * [Documentation PostgreSQL 14](https://www.postgresql.org/docs/14/sql-createsequence.html)

## 質問の不備について

なにかしらの質問の不備がありましたら申し訳ありません。その際は訂正いたします。


CC BY-SA 4.0
2022-12-13T09:19:47.983
92746
2022-12-14T02:00:59.010
2022-12-13T09:53:07.820
3060
55957
question
postgresql database
PostgreSQLのSequenceの最初の採番について


シーケンスにはキャッシュ値があります。PostgreSQLではデフォルト１です。  
これを以下のように10にして作成しかつ、２つのセッションそれぞれで

[code]

    select nextval('test_seq3'); 
    
[/code]

と

[code]

    SELECT * FROM test_seq3;
    
[/code]

を実行し動きを確認してみてください。  
理解できるとおもいます。

[code]

    CREATE SEQUENCE test_seq3
        INCREMENT 1
        START 2
        MINVALUE 1
        CACHE 10
    ;
    
[/code]

また、シーケンスのキャッシュ値は以下のSQLでも確認できます。

[code]

    select 
     c.relowner ::regrole as owner
    ,c.relnamespace::regnamespace as schema
    ,c.relname
    ,s.seqtypid :: regtype
    ,s.seqstart,s.seqincrement,s.seqmax,s.seqmin,s.seqcache,s.seqcycle
    ,c.reltype::regtype
    ,array_to_string(relacl,',') relacl
    from  pg_sequence s 
    inner join pg_class  c on s.seqrelid = c.oid
    where c.relname ='test_seq3'
    ;
    
[/code]


CC BY-SA 4.0
2022-12-13T15:37:53.497
92758
2022-12-13T15:37:53.497
55896
92746
answer


`YumaInaura 稲浦悠馬`さんのコメントにより解決できました。

sequenceには`is_called`というフラグがあり、最初の呼び出しまでは`is_called=false`のため例外的に`1`が返され、2回目以降は`is_called=true`となるため2から順に規則的に返されるという結論で腑に落ちました。

### 確認用SQL

[code]

    -- 確認
    SELECT * FROM test_seq;
    
    -- セット
    SELECT setval('test_seq',1,true);
    -- 採番
    select nextval('test_seq'); 
    
    -- セット
    SELECT setval('test_seq',1,false);
    -- 採番
    select nextval('test_seq'); 
    
[/code]

### 参考

[エンジニアのTipsメモ
PostgreSQLのシーケンス操作](http://tips.traction.jp/2017/05/postgresql.html)

> しかし、ここで注意しなければいけないことがあります。それは、現在値を１にする方法です。  
>  例えば、上のように以下のやり方で現在値を１にした場合、次に割り振られる花のIDは２になります。  
>  `SELECT SETVAL('花_id_seq',１)`  
>  これは、シーケンスのカラムの1つである「is_called」が「true」になっているためです。  
>
> 「is_called」は「last_value」が既に呼ばれたかどうかのフラグで、呼び出されている場合は真（true）、まだ呼び出されていない場合は偽（false）になります。  
>
> シーケンスが連番を発生させるためには現在値を知る必要がありますので、連番を作成する時点でシーケンスは「last_value」を呼び出します。際初期段階においては、ここで「is_called」が真になります。「is_called」が偽の場合とは、シーケンスがまだ連番を発生させていない状態、すなわちまだ連番になるデータがない状態を意味します。  
>
> つまり、同じ現在値（「last_value」）が１の場合でも、「is_called」の真偽で最初期状態か１の値が割り振られたデータがある状態かに分かれます。

[PostgreSQLのシーケンスのnextvalの挙動](https://qiita.com/EichiSanden/items/0155dcdf886ecb50aba2)

> create sequence で start with 1 と指定すると、最初に nextval() した時に 1 が返ってくる。  
>  ここまでは良くて、次に値をリセットしたくて setval('hoge',1) した後で nextval('hoge') すると 2
> が返ってきてファッ!?となった。  
>  それぞれの状態を調べてみると、create sequenceした直後は is_called の値が false だが、 setval した直後は
> true なようだ。

[Postgres: Get nextval in sequence without actually incrementing
sequence?](https://dba.stackexchange.com/questions/253090/postgres-get-
nextval-in-sequence-without-actually-incrementing-sequence)

> Based on previous answers I realised that it fails for the first value
> added: The query returns 2 instead on 1 when there are no values added. Here
> is my solution:
[code]

>     SELECT (CASE WHEN is_called THEN last_value + i.inc
>             ELSE last_value END ) AS nextvalue
>       FROM myserial,
>           (SELECT seqincrement AS inc
>            FROM pg_sequence
>            WHERE seqrelid = 'myserial'::regclass::oid) AS i;
>  
[/code]


CC BY-SA 4.0
2022-12-14T00:01:52.997
92760
2022-12-14T00:45:33.940
2022-12-14T00:45:33.940
55957
55957
92746
answer


## 回答ではありません

is_calledとcacheについて調べたログだけ載せておきます。

[code]

    postgres=# select version();
                                                         version                                                     
    -----------------------------------------------------------------------------------------------------------------
     PostgreSQL 14.6 on x86_64-pc-linux-musl, compiled by gcc (Alpine 12.2.1_git20220924-r4) 12.2.1 20220924, 64-bit
    (1 row)
    
    postgres=# CREATE SEQUENCE test_seq3
        INCREMENT 1
        START 2
        MINVALUE 1
        CACHE 10
    ;
    CREATE SEQUENCE
    postgres=# select * from test_seq3;
     last_value | log_cnt | is_called 
    ------------+---------+-----------
              2 |       0 | f
    (1 row)
    
    postgres=# select nextval('test_seq3');
     nextval 
    ---------
           2
    (1 row)
    
    postgres=# select * from test_seq3;
     last_value | log_cnt | is_called 
    ------------+---------+-----------
             11 |      32 | t
    (1 row)
    
    postgres=# select setval('test_seq3', 2);
     setval 
    --------
          2
    (1 row)
    
    postgres=# select * from test_seq3;
     last_value | log_cnt | is_called 
    ------------+---------+-----------
              2 |       0 | t
    (1 row)
    
    postgres=# select nextval('test_seq3');
     nextval 
    ---------
           3
    (1 row)
    
    postgres=# select * from test_seq3;
     last_value | log_cnt | is_called 
    ------------+---------+-----------
             12 |      32 | t
    (1 row)
    
    postgres=# select setval('test_seq3', 2, false);
     setval 
    --------
          2
    (1 row)
    
    postgres=# select * from test_seq3;
     last_value | log_cnt | is_called 
    ------------+---------+-----------
              2 |       0 | f
    (1 row)
    
    postgres=# select nextval('test_seq3');
     nextval 
    ---------
           2
    (1 row)
    
    postgres=# select * from test_seq3;
     last_value | log_cnt | is_called 
    ------------+---------+-----------
             11 |      32 | t
    (1 row)
    
    postgres=# 
    
[/code]


CC BY-SA 4.0
2022-12-14T02:00:59.010
92766
2022-12-14T02:00:59.010
54957
92746
answer