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()
がどのように機能するかを完全に理解し、好きなように実装できることを願っています。
Hello, I am Bilal, a research enthusiast who tends to break and make code from scratch. I dwell deep into the latest issues faced by the developer community and provide answers and different solutions. Apart from that, I am just another normal developer with a laptop, a mug of coffee, some biscuits and a thick spectacle!
GitHub