sql >> データベース >  >> RDS >> PostgreSQL

PostgreSQLギャップレスシーケンス

    シーケンスには、同時挿入を可能にするギャップがあります。ギャップを回避したり、削除された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;
    



    1. DAYNAME()の例– MySQL

    2. SQL Serverインスタンスでまだ使用されている廃止された機能を見つける最も簡単な方法(T-SQLの例)

    3. クエリ結果から結果のランダムサンプルを選択します

    4. SQLiteユーザー権限