証明書番号を保持するテーブルがあります。各証明書は、一連の文字とシリアル番号で構成されています。証明書は一意である必要があり、ギャップは許されません。つまり、A-1234 が存在する場合、A-1233 も存在する必要があります。各シリーズには独自のシリアルがあります。つまり、A-1234 と B-1234 は共存できます。
情報を保持するテーブルは次のとおりです。
CREATE TABLE "Certificate"
(
id serial NOT NULL,
series character(1) NOT NULL,
serial integer NOT NULL,
CONSTRAINT "Certificate_pkey" PRIMARY KEY (id ),
CONSTRAINT "Certificate_series_serial_key" UNIQUE (series , serial )
)
WITH (
OIDS=FALSE
);
ここで問題は、特定のシリーズを指定して、次のシリアル番号を作成する方法です。Postgres シーケンスは、何らかの理由で挿入が失敗した場合、またはシーケンスからの新しい値を必要とするトランザクションがロールバックされた場合にギャップが生じるため、進むべき道ではないようです。
私の考えは次のとおりでした。
#! /usr/bin/env python3.2
import postgresql.driver.dbapi20 as pgdb
credentials = #my db credentials
SERIES = 'B'
dbcon = pgdb.connect (**credentials)
cursor = dbcon.cursor ()
cursor.execute ('''insert into "Certificate" ("series", "serial")
(select %s, coalesce (max ("serial"), 0) + 1
from "Certificate"
where "series" = %s) ''', [SERIES] * 2)
input () #just to keep the transaction open some time
cursor.close ()
dbcon.commit ()
dbcon.close ()
残念ながら、これはスレッドセーフではありません。このスクリプトを 2 つのシェル (A と B など) で 2 回開始すると、次のようになります。
- シェル A でスクリプトを開始すると、 のトランザクション内に保持され
input ()
ます。 - シェル B でスクリプトを開始すると、 のトランザクション内に保持され
input ()
ます。 - シェル A で Enter キーを押すと、トランザクションがコミットされます。
- この正確な瞬間に、シェル B が (expected) をスローし
postgresql.exceptions.UniqueError: duplicate key value violates unique constraint "Certificate_series_serial_key"
ます。
特定の一連の文字に基づいて、スレッドセーフな方法で次の証明書をこのテーブルに挿入するにはどうすればよいですか?