HOWTO · PostgreSQL

PostgreSQL の NEXTVAL 関数

この記事では、PostgreSQL の NEXTVAL() 関数の機能について説明します。

このページの内容

NEXTVAL() は、オブジェクトを別の値に進めて返す傾向があります。 SEQUENCE OBJECTS は、PostgreSQL の SEQUENCES から作成された単なる 1 行のテーブルです。

NEXTVAL() 関数は、SEQUENCE MANIPULATION FUNCTIONS の下で定義されています。

nextval ( regclass ) ? bigint

REGCLASS は SEQUENCE OBJECT を参照し、この関数は BIGINT を返す傾向があります。 それでは、この NEXTVAL() 関数の可能な実装と、その使用方法を見てみましょう。

PostgreSQLでのNEXTVAL()の実例

PostgreSQL での SEQUENCE IDENTIFIERS の用途の 1つは、一意の行識別子の値を取得するために使用する方法です。 詳細については、CREATE SEQUENCE URL を参照してください。

上記の使用方法をフォローアップし、NEXTVAL()関数を効果的に使用できるシステムの実装を試みます。 PostgreSQL を起動し、RANDOM_GEN (ランダム ジェネレーターの略) という単純なテーブルを作成します。

CREATE TABLE RANDOM_GENE(
	val INT PRIMARY KEY
);

一意でランダムであるため、シンプルな VALUE をテーブルの PRIMARY KEY として使用しました。 それでは、このテーブルにデータを追加してみましょう。

もちろん、PostgreSQL で SEQUENCE GENERATOR を使用するには、VAL を RANDOM にする必要があります。 このチュートリアルでは、増分値を持つ SERIAL GENERATOR を使用して UNIQUE VAL を取得します。

CREATE SEQUENCE serial_num;

そして、この SEQUENCE GENERATOR の値を使用するために、このテーブルから SELECT 操作を照会できます。

SELECT * from SERIAL_NUM;

しかし、問題があります。 この SELECT 操作を繰り返し呼び出すと、SEQUENCE GENERATOR から同じ値を取得する傾向があります。

したがって、テストせずにこの SEQUENCE の値をテーブルに INSERT した場合、値が重複することになります。

ここで、NEXTVAL() 関数の出番です。先に進み、この GENERATOR の値を進めてから、SELECT 操作を発行して昇順の値を取得できます。

したがって、次のように記述できます。

SELECT * from NEXTVAL('serial_num');

そして、これは、たとえば 5 回の反復に対して次のような出力を返します。

出力:

Iter      VAL
1          1
2          2
3          3
4          4
5          5

したがって、前に説明したように、NEXTVAL() は SEQUENCE GENERATOR を増やして進める傾向があります。 そのため、引数で渡された SEQUENCE に対して NEXTVAL() が呼び出されるたびに、SEQUENCE が次の昇順の値を指していると想像できます。

したがって、RANDOM_GEN テーブルへの INSERT 操作のために次のように何かを呼び出すことができます。

INSERT INTO RANDOM_GENE values (NEXTVAL('SERIAL_NUM')), (NEXTVAL('SERIAL_NUM')), (NEXTVAL('SERIAL_NUM'));

テーブルは次のようになります。

出力:

        val
1       1
2       2
3       3

VALUE 列だけでこれを実現するもう 1つの簡単な方法は、CREATE TABLE ステートメントで NEXTVAL() を定義することです。 次のようにクエリを書くことができます。

CREATE TABLE RANDOM_GENE(
	val INT primary key default NEXTVAL('SERIAL_NUM')
);

DEFAULT を使用して、SEQUENCE GENERATOR から値を取得するこの VAL 列のデフォルトの動作を定義しました。 もちろん、上記は2番目の列がないと役に立たないので、USER_NAME列を追加して、RANDOM_GEN値を受け取るUSERSを定義しましょう。

CREATE TABLE RANDOM_GENE(
	val INT primary key default NEXTVAL('SERIAL_NUM'),
        name TEXT
);

INSERT ステートメントを次のように記述できるようになりました。

INSERT into RANDOM_GENE (name) values ('John'), ('Marta'), ('Alex');

これにより、テーブルへのクエリで指定された名前が INSERT され、それぞれの値が SEQUENCE GENERATOR から昇順で取得されます。 今の表に目を向けると、次のようになります。

val     name
1	"John"
2	"Marta"
3	"Alex"

したがって、NEXTVAL() がどのように機能するかを完全に理解できました。 とりあえず、さまざまな環境での NEXTVAL() の動作を見てみましょう。

PostgreSQL のさまざまな環境と状況での NEXTVAL()

NEXTVAL() を使用すると、呼び出されるたびに SEQUENCE GENERATOR がインクリメントされる傾向があります。 したがって、この場合、重複について心配する必要はありません。

NEXTVAL() が呼び出されると、GENERATOR が進められ、次の値が計算されます。 同じ SEQUENCE に対して NEXTVAL() を呼び出す他のクエリを同時に実行すると、ジェネレーターのまったく異なる一意の値が取得されます。

したがって、Postgres サーバーでクエリを実行している可能性のある複数のトランザクションとプロセスに対して NEXTVAL() を使用することは、効率的かつ安全です。

PostgreSQL の NEXTVAL() とのギャップと値の違い

NEXTVAL() に見られる一般的な問題は、進歩の厳密な実装です。 SEQUENCE が新しい値に進むと、元の値に戻ったり、以前の値が使用されているかどうかを確認したりすることはほとんどありません。

そのため、UNIQUE 値列と NAME を持つテーブルがあり、INSERT しようとしている NAME がテーブルに既に存在する場合、違反エラーが発生する可能性があります。 NEXTVAL() は、そのシナリオで既に呼び出されています。

SEQUENCE は値に進みましたが、違反により INSERTION が妨げられています。 そのため、次に INSERT が呼び出されると、NEXTVAL() が再び進み、以前の値は完全にスキップされます。

ON CONFLICT 操作または適切に実行されない可能性のある操作についても同じことが言えます。 以下の例では、以下を使用してテーブル列 NAME で INSERT を呼び出します。

name TEXT unique

次に、次のようなクエリを使用して、意図的に ALEX の重複した名前を使用して、データをテーブルに INSERT します。

INSERT into RANDOM_GENE (name) values ('John'), ('Marta'), ('Alex'), ('Alex'), ('Mathew') on conflict (name) do nothing;

ON CONFLICT を配置して、重複違反エラーを回避し、テーブルで VALUE をチェックします。 テーブルは次のようになります。

val    name
1	"John"
2	"Marta"
3	"Alex"
5	"Mathew"

VAL 列に数値 4 の値がないことがわかります。 これは、重複した ALEX 挿入が SEQUENCE GENERATOR を進めましたが、INSERTED を取得しなかったためです。

したがって、名前 MATHEW の値はすでに 4 でしたが、INSERTION の値は 5 に進みました。

これで、NEXTVAL() がどのように機能するかを完全に理解し、好きなように実装できることを願っています。