シーケンスには、同時挿入を可能にするギャップがあります。ギャップを回避したり、削除されたIDを再利用しようとすると、パフォーマンスに大きな問題が発生します。 PostgreSQLwikiのFAQを参照してください。
PostgreSQL SEQUENCE
sはIDを割り当てるために使用されます。これらは増加するだけであり、複数のトランザクションが同時に新しいIDを取得できるようにする通常のトランザクションロールバックルールから免除されます。これは、トランザクションがロールバックすると、それらのIDが「破棄」されることを意味します。保持されている「無料」IDのリストはなく、現在のIDカウンターだけです。データベースが不潔にシャットダウンした場合も、通常、シーケンスは増分されます。
合成キー(ID)は意味がありません とりあえず。それらの順序は重要ではなく、重要な唯一の特性は一意性です。 2つのIDがどれだけ「離れている」かを意味のある形で測定することも、一方が他方よりも大きいか小さいかを意味のある形で判断することもできません。あなたにできることは、「等しい」または「等しくない」と言うことだけです。それ以外は安全ではありません。ギャップを気にする必要はありません。
削除されたIDを再利用するギャップのないシーケンスが必要な場合は、それを使用できます。そのために大量のパフォーマンスを放棄する必要があります。特に、INSERT
で同時実行することはできません。 ■テーブルをスキャンして最小の空きIDを探す必要があるため、他のトランザクションが同じIDを要求できないように、テーブルを書き込み用にロックします。 「postgresqlギャップレスシーケンス」を検索してみてください。
最も簡単なアプローチは、カウンターテーブルと次のIDを取得する関数を使用することです。これは、カウンターテーブルを使用して連続するギャップレスIDを生成する一般化されたバージョンです。ただし、IDは再利用されません。
CREATE TABLE thetable_id_counter ( last_id integer not null );
INSERT INTO thetable_id_counter VALUES (0);
CREATE OR REPLACE FUNCTION get_next_id(countertable regclass, countercolumn text) RETURNS integer AS $$
DECLARE
next_value integer;
BEGIN
EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
RETURN next_value;
END;
$$ LANGUAGE plpgsql;
COMMENT ON get_next_id(countername regclass) IS 'Increment and return value from integer column $2 in table $1';
使用法:
INSERT INTO dummy(id, blah)
VALUES ( get_next_id('thetable_id_counter','last_id'), 42 );
1つの開いているトランザクションがIDを取得すると、get_next_id
を呼び出そうとする他のすべてのトランザクションに注意してください。 最初のトランザクションがコミットまたはロールバックするまでブロックします。これは避けられない、ギャップのないIDの場合であり、仕様によるものです。
さまざまな目的で複数のカウンターをテーブルに格納する場合は、上記の関数にパラメーターを追加し、カウンターテーブルに列を追加して、WHERE
を追加するだけです。 UPDATE
の句 これは、パラメーターを追加された列に一致させます。そうすれば、複数の独立してロックされたカウンター行を持つことができます。 しない 新しいカウンター用に列を追加するだけです。
この関数は、削除されたIDを再利用せず、ギャップの導入を回避するだけです。
IDを再利用するには、IDを再利用しないことをお勧めします。
本当に必要な場合は、ON INSERT OR UPDATE OR DELETE
を追加することで実行できます。 削除されたIDをフリーリストサイドテーブルに追加し、INSERT
のときにフリーリストテーブルから削除する、対象のテーブルでトリガーします。 ed。 UPDATE
を処理する DELETE
として 続いてINSERT
。次に、上記のID生成関数を変更して、SELECT free_id INTO next_value FROM free_ids FOR UPDATE LIMIT 1
を実行するようにします。 見つかった場合は、DELETE
sその行。 IF NOT FOUND
通常どおり、ジェネレータテーブルから新しいIDを取得します。再利用をサポートするための以前の関数のテストされていない拡張は次のとおりです。
CREATE OR REPLACE FUNCTION get_next_id_reuse(countertable regclass, countercolumn text, freelisttable regclass, freelistcolumn text) RETURNS integer AS $$
DECLARE
next_value integer;
BEGIN
EXECUTE format('SELECT %I FROM %s FOR UPDATE LIMIT 1', freelistcolumn, freelisttable) INTO next_value;
IF next_value IS NOT NULL THEN
EXECUTE format('DELETE FROM %s WHERE %I = %L', freelisttable, freelistcolumn, next_value);
ELSE
EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
END IF;
RETURN next_value;
END;
$$ LANGUAGE plpgsql;