質問のまとめ
これは、SQL トランザクション内でのクエリのシリアル化可能性に関する質問です。
具体的には、PostgreSQL を使用しています。PostgreSQL の最新バージョンを使用していると思われるかもしれません。私が読んだことから、私がやろうとしていることをサポートするために使用されるテクノロジーは、「MultiVersion Concurrency Control」または「MVCC」として知られていると思います。
要約すると: 1 つのプライマリ テーブルと、そのプライマリ テーブルに接続された複数の外部キー リンク テーブルがある場合、テーブル内の特定のキーと任意の数の SELECT ステートメントに対して、それを保証するにはどうすればよいですか? 1 つのトランザクション内でそのキーを使用し、それぞれがリンクされたテーブルのいずれかから SELECT を実行すると、トランザクションを開始した時点で存在していたデータを取得できますか?
その他の質問
この質問は似ていますが、より広範であり、質問と回答は PostgreSQL に特に関連していませんでした: Transaction isolation and reading from multiple tables on SQL Server Express and SQL Server 2005
例
3つのテーブルがあるとしましょう:
bricks
brickworks (primary key)
completion_time (primary key)
has_been_sold
brick_colors
brickworks (primary key, foreign key pointing to "bricks")
completion_time (primary key, foreign key pointing to "bricks")
quadrant (primary key)
color
brick_weight
brickworks (primary key, foreign key pointing to "bricks")
completion_time (primary key, foreign key pointing to "bricks")
weight
煉瓦工場は一度に 1 つの煉瓦を生産します。4 つの象限のそれぞれで異なる色のレンガを作成します。
後で誰かがレンガを分析して色の組み合わせを決定し、その結果を brick_colors テーブルに書き込みます。
他の誰かがレンガを分析して重量を決定し、結果を brick_weight テーブルに書き込みます。
任意の時点で、既存のレンガには記録された色がある場合とない場合があり、記録された重量がある場合とない場合があります。
アプリケーションが存在し、このアプリケーションは、誰かが特定のブリックを購入したいという言葉を受け取ります (この時点でアプリケーションは、brickworks/completion_time 複合キーによって既に認識されています)。
アプリケーションは、クエリを開始する正確な時間にレンガのすべての既知のプロパティを選択したいと考えています。
色または重量の情報がトランザクションの途中で追加された場合、アプリケーションはそれについて知りたくありません。
アプリケーションは、SEPARATE QUERIES を実行しようとしています (brick_colors テーブルのために複数の行を返す可能性がある、外部キー リンク テーブルへの複数の JOIN を持つ SELECT ではありません)。
この例は意図的に単純化されています。複数の JOIN で 1 つの SELECT を使用せずにこれを実行したいという欲求は、私の例にたとえば 10 個の外部キー リンク テーブルが含まれていて、それらの多くまたはすべてが同じ主キーに対して複数の行を返すことができる場合 (brick_colors が私が上に持っている例)。
試みられた解決策
これが私がこれまでに思いついたものです:
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY ;
-- All this statement accomplishes is telling the database what rows should be returned from the present point-in-time in future queries within the transaction
SELECT DISTINCT true
FROM bricks b
LEFT JOIN brick_colors bc ON bc.brickworks = b.brickworks AND bc.completion_time = b.completion_time
LEFT JOIN brick_weight bw ON bw.brickworks = b.brickworks AND bw.completion_time = b.completion_time
WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ;
SELECT * FROM brick_colors WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ;
SELECT * FROM brick_weight WHERE b.brickworks = 'Brick-o-Matic' AND b.completion_time = '2017-02-01T07:35:00.000Z' ;
COMMIT ;
シリアル化可能性を確保するためだけに、最初の SELECT を JOIN で使用するのは無駄に思えます。
これを行う他の方法はありますか?