2012-04-18 8 views
5

私は請求書発行のためのSaaSペットプロジェクトを持っています。私はすべてのクライアントが同じデータベースを共有し、同じticketsテーブルを共有するので、私はクライアントがチケット番号1001でそれぞれのスタートを欲しいと思います。私はPostgresで簡単な自動フィールドを使用することはできません。私は整数列型を使用し、最新の数値を取得するために擬似SQL(SELECT LATEST number FROM tickets WHERE client_id = [current client ID])をクエリし、その数値+ 1を使用して次のnumberを取得しようとしました。問題は並行性により、2つのチケットがこのように同じ番号で終わることは容易に可能であることです。私がDjango内でこれをやることができるか、あるいは生のSQLを使って(Bashや他の何かを使って)できるようにするための数です。Postgresの列を手動で順序付ける正しい方法は何ですか?

例を強制的に動作させる方法を探しているわけではありません。私はちょうど各クライアントのためにチケット番号を独立して増やすことを必要とするという問題への解決策を探しています。

+1

可能な複製:http://stackoverflow.com/questions/6046085/postgresql-sequence-that-ensure-a-unique-id –

+0

一意の番号を探しているので重複しません。 Mineは、別のフィールド(クライアントフィールド)に対して次の番号を順に探しています。実際には、私はオートフィールド(シリアル)を使用できないと述べています。 – orokusaki

+0

Gotcha、私は今理解しています。うまくいけば私の答えはあなたのために働く(または、他の誰かがよりエレガントな何かを持っている)。 –

答えて

5

この問題の「安い」解決策はありません。マルチユーザー環境で安全な(ただし必ずしも高速ではない)唯一のソリューションは、顧客ごとに1つの行を持つ「カウンタ」テーブルを持つことです。

各トランザクションはこのような何か、最初に新しいチケットを挿入する前に、顧客のエントリをロックすることがあります。

UPDATE cust_numbers 
    SET current_number = current_number + 1 
WHERE cust_id = 42 
RETURNING current_number; 

一歩

  1. に3つのことを行います現在の「シーケンシャル」を高めますその顧客の番号
  2. 同じ行をする他のトランザクションがロックを待つように行をロックする
  3. 新しい値th列で。

その新しい番号を使用すると、新しいチケットを挿入することができます。トランザクションがコミットされている場合は、cust_numbersテーブルのロックも解除されるため、「番号を待っている」他のトランザクションを続行できます。

2つのステップ(更新。&挿入)を単一のストアドファンクションにラップすることで、この背後にあるロジックを集中化することができます。あなたのアプリケーションは、チケット番号の生成方法を知らずにselect insert_ticket(...)にしかコールしません。

また、新しい顧客が作成されたときに自動的にcust_numbersテーブルに行を挿入するために、顧客テーブルにトリガーを作成することもできます。

これの欠点は、あなたが効果的に同じ顧客のための新しいチケットを挿入されたトランザクションをシリアライズということです。システム内のインサートの数に応じて、パフォーマンスの問題が発生する可能性があります。

編集
本の別の欠点は、あなたがが、例えばあれば問題につながる可能性がある方法でチケットを挿入するためにを余儀なくされていないこと、です新しい開発者はこれについて忘れてしまいます。

+0

優秀、ありがとう!しかし、A)このロックはテーブル全体に適用されるのでしょうか?あるいは、1つの行をロックすることはできますか?私は2000人のクライアントすべてが、より多くのチケットを作成する前に、クライアントXからのこのロックが解放されるのを待たなければならないと思っています(2秒間に2つの同時チケットを想像すると)B)何が起こりますか?パス)、ロックされている間に2番目の接続がこの番号を問い合わせるとしますか? re: "トランザクションをシリアライズする" - 以前のチケットのトランザクションが新しいトランザクションの前に完了しなければならない場合、どのようなパフォーマンスの問題になるでしょうか?それは大きな問題ですか?私は高いボリュームを持っています。 – orokusaki

+2

@orokusaki:私が説明したように、その顧客の行だけがロックされます。 2番目の接続は、最初のコミットが完了するまで待機します。はい、これはあなたのパフォーマンスを低下させます。しかし、あなたはこのような数字を得ることができます(遅くて安全です)。あるいは、シーケンスを使って速くすることができます。しかし、それらはすべての顧客にわたってユニークになるでしょう。あなたはもっと重要なことを決める必要があります –

2

お客様ごとにシーケンスを作成し、列の値をnextval('name_of_the_sequence')に設定することができます。これは実際にはどのようにserialの作品です。あなたの場合の唯一の違いは、列にデフォルト値を使用せず、複数のシーケンスを持つことです。

PL/Pgsqlプロシージャを使用して、新しい行を挿入するときに正しい順序を選択することができます。

+0

これを行うために2000列が必要であるという問題以外にも、シリアルフィールドを使用する際の根本的な問題は、ロールバックされたトランザクションが 'nextval'が返す数値を使い切るということです。 1002,1005,1007などと比較して、実際のシーケンスを維持する(すなわち、直列カラムの値はカーディナリティの正確な尺度ではない)。 – orokusaki

+0

私の答えをお読みください。私はシリアル列や異なる列については話しませんでした。シーケンスを別々に作成し、 'nextval()'を使用して値を取得することができます – ThiefMaster

+0

@ThiefMaster、トリガー(挿入された顧客)でシーケンスを作成してから動的SQLに値を選択する必要がありますか? (私はPostgresのnoobですので、これが可能かどうかはわかりませんが、おそらくOPにはこのようなことを行う自動方法が必要です) –

関連する問題