2016-10-05 20 views
0

私はpostgresデータベースを使用していますが、私の問題には2つのテーブルが含まれています。PostgreSQL INSERT with statements

CREATE TABLE events(
    id SERIAL PRIMARY KEY NOT NULL, 
    max_persons INTEGER NOT NULL 
); 

CREATE TABLE requests(
    id SERIAL PRIMARY KEY NOT NULL, 
    confirmed BOOLEAN NOT NULL, 
    creation_time TIMESTAMP DEFAULT NOW(), 
    event_id INTEGER NOT NULL /*foreign key*/ 
); 

ありnのイベントがあると、各イベントには、最大events.max_persons参加者を持つことができます。新しい要求を確認する必要があり、30分まで有効です。その期間が過ぎると、確認されなかった場合、リクエストは無視されます。すべての合計が要求とまだ有効であるすべての要求を確認したが、確認されていない、events.max_persons未満のとき

私はを何をしたいのかは、唯一の新しいrequestを挿入しています。

すでに1つのイベントを選択するためのクエリがあります。コマンドを - ここでの簡易版はそれはそれは、単一のINSERTでこれを達成するためにpossibileです

SELECT 
    e.id, 
    SUM(CASE WHEN r.confirmed = 1 THEN 1 ELSE 0 END) AS number_confirmed 
    SUM(CASE WHEN r.creation_time > (CURRENT_TIMESTAMP - INTERVAL '30 MINUTE') AND r.confirmed = 0 THEN 1 ELSE 0 END) AS number_reserved, 
    e.max_persons 
FROM events e, requests r 
WHERE l.id = ? 
    AND r.event_id = e.id    
    AND (r.confirmed = 1 OR r.creation_time > (CURRENT_TIMESTAMP - INTERVAL '30 MINUTE')) 
GROUP BY e.id, e.max_persons    
HAVING SUM(CASE WHEN r.confirmed = 1 OR (r.creation_time > (CURRENT_TIMESTAMP - INTERVAL '30 MINUTE')) THEN 1 ELSE 0 END) < e.max_persons"; 

動作するはずですか、あなたのアイデアを与えること、ありますか?

INSERT INTO requests 
    SELECT * FROM (VALUES (...)) row 
     WHERE ... 

をし、あなたの条件が満たされた場合にのみ、真であるWHERE句を書く:

答えて

2

次のようなことを行うことができます。

しかし、このアプローチには根本的な問題があります。つまり、競合状態に陥っているということです。

このような2つの文が同時に実行されると、どちらも条件が満たされている可能性がありますが、それぞれが行を追加してコミットすると、条件が破られる可能性があります。これは、いずれのステートメントもコミットする前に他のステートメントの効果を見ることができないためです。

このための2つのソリューションがあります:あなたがテストして挿入する前に

  • は、テーブルをロックします。これは単純ですが、並行性は非常に悪いです。

  • SERIALIZABLE取引を使用してください。これにより、シリアル化エラーが発生し、ステートメントの1つが再試行​​され、条件が満たされたときに条件が違反していることが検出されます。

+0

こんにちは、遅い答えを申し訳ありません。現時点では、max_personsとnumber_booked/number_reservedの違いを取得し、0より大きい場合にのみinsert文を実行するクエリを実行することで、私の問題を解決しました。もう1つの質問ですが、SERIALIZABLEトランザクションを使用している場合、2人で同時に同じイベントにサインアップしようとすると問題は起こりますが、残っているのは1つだけです。 – PrototypeX7

+0

あなたの現在のテクニックは、私が答えで示した問題に対して脆弱です.2つのトランザクションが並行して実行され、1つの場所だけが空いている場合、両方とも新しい要求を追加します。起こり得ないSERIALIZABLEによって。トランザクションの1つは、シリアル化エラーで終了し、再試行する必要があります。再試行中にイベントが既に予約されていることが示されます。 –

+0

ありがとう、それを試してみよう! – PrototypeX7