2012-03-15 3 views
1

エラーをチェック、ユーザー名はすでに"select * ..."をやった後、結果セット内の行数を取得することにより、テーブルに存在する場合、私がチェックする必要がありますし、私は行の数がゼロに等しい、次にユーザー名を挿入しますか?
または、ユーザー名を表に挿入するだけです。すでに存在する場合は、エラーがスローされ、それが捕捉されます。
注:ユーザー名は優れている上記の二つの主キー

やってはいるのですか?- 例外を投げるやPostgreSQLに接続されている私のサーバーでは、事前に

答えて

4

あなたはとにかくそれをしなければならないというだけの理由「キャッチしてみてください-と例外」の方法を行う必要があります。

あなたが最初にチェックした場合、ユーザーは自分のチェックがそれを見つけられませんでしたにもかかわらず、テーブルになります。その場合にはあなたのチェックとあなたのインサートとの間、そのユーザのために行を挿入誰かを止めるものは何もありません。

(誰もが暫定的にそのユーザーを挿入できないように)トランザクションのいくつかの並べ替え内のチェックアンドインサートを実行することができるという短いです。非例外が機能することは確信できません。

そして、多くのDBMSは、」トランザクションのサポートを提供しますが、私はあなたがもちろん

、アプリケーションがそのように設計されている場合:-)挿入するためには至っていない行をロックする任意のを知りませんあなたのプロセスだけがユーザを挿入する(そしてシリアライズされる)方法では、チェック・ファースト・メソッドを使うことができます。しかし、私はあなたがスケールアップするなら、それが再訪する必要があるという事実に豊富なコメントを入れています。

+0

は、2つのシナリオがあります:1)ユーザー名が長いデータベースであるためであるとselectを使用してチェックすることができ、かつ2)2つのスレッドが競合状態が発生し、同じユーザー名のために競合しています。私は競争条件は適切なロッキング(必要に応じてアプリケーション側で)によって回避されるべきだと主張し、その状況(1)はまったく例外的ではない。 – aioobe

+0

@paxdiablo:ユーザー名を確認せずに例外のみを使用すると、より速くなりますか?それでは、デフォルトのチェックメカニズムとして例外を使用しないように全員がアドバイスしているのはなぜですか?万一のチェックでデータベースに2回アクセスすると(ユーザー名とその他の情報を確認するために1回) – Ashwin

+0

@ user1139023、新しいユーザーにアプリケーションのボトルネックが挿入されますか? – aioobe

2

普通のコンセンサスは、制御フロー構築物ではなく、例外的にの場合にのみ例外を使用することです。私の考えでは、珍しいユースケースではなく、有効であると見なされるべきであると思われるユーザ名を使用しようとしています。言い換えれば

、私は最初、既存のユーザー名をチェックします。

しかし、@ paxdiabloは、Webサーバーなどのマルチスレッド環境にある場合は、ロック方式を追加するか、try/catch方式を使用する必要があります(2つのスレッドが競合する可能性がある同じユーザー名を追加します)。 しかし、の状況は、例外的なケースとは思われません。

関連質問(すべて同じ結論で、非例外的なケースのための例外を使用しないでください):

+0

例外的になる前にそれは珍しいのですか?これを数量化することは可能ですか?重複したユーザーをデータベースに挿入するのはどのくらい一般的ですか? 1000で1? – EJP

+1

多くの設計上の決定と同様に、実際には何も正しいことも間違いもありません。ここであなたのためのヒントです:あなたは状況を例外*と呼べますか? (これは、あるクラスが別のクラスを継承するかどうかを決めるときに、*が哺乳類の魚であるかを尋ねることに似ています) – aioobe

0

私はそのような場合に例外をキャッチすることは例外コンセプトを悪用していると言います。もしあなたがそれを前にチェックできるなら、それをチェックしなければなりません。

+1

なぜそれを言うのですか?ユーザー名が存在するかどうかをチェックするのではなく、チェックメカニズムとして例外を直接使用するほうが速くなります(データベースをアクセスする必要があります)。 – Ashwin

0

プログラムのフローを制御するために例外を使用しないでください。それはexcpetionalの場合ではない場合は、例外を避けるためのベストプラクティスです。

+1

あなたの理由を述べてください。 – Ashwin

+0

私はこの声明が完全に無意味であると感じます。例外的なケースではない場合、例外は何ですか? – EJP

+0

@EJP、*例外ではない場合、例外とは何ですか?*例外は、例外的でない場合にスローされる可能性があります。私はあなたがこれに同意すると確信しています。おそらくちょっと誤解していたり​​、ここで答えを誤解したりしています。いずれにしても、[こちら](http://meta.stackexchange.com/questions/2451/why-do-you-cast-downvotes-on-answers)答えを落とす適切な理由のコミュニティビュー。 – aioobe

-1

正しいことは、あなたのケースでの例外処理ではなく、自動的に増分される主キーを使用することです。ユーザーがクエリを介して行わ存在するのではなく、例外を使用するため

PostgreSQL Autoincrement

+0

ところで、行数をカウントする選択カウント(*)要求があります。これは、各行ごとに要求し、それらを数えるよりはるかに高速です。しかし、自動インクリメントのプライマリキーは、より良いオプションです – Snicolas

0

私はチェックを好むだろう。間もなく、「ユーザーが存在する」エラーのロジックがビジネスルールになる可能性があります。 (よくあなたはSQLでこのようなルールを書くことができますが、それはまったく異なる世界です)

または単にテーブルにユーザ名を挿入してください。既に存在する場合は、 それは捕まえることができるエラーを投げるでしょう

問題は他にも多くの理由があります。とにかくそれらを処理する必要があります。

+0

はい私は例外を処理します。しかし、例外を使用する方が、ユーザー名が存在するかどうか最初に確認してから挿入する方が速いでしょう。パフォーマンスの問題が発生するたびに例外をスローしますか? – Ashwin

1

この場合、回答はであり、ではありません。エラーを引き起こすことも、事前にチェックすることもありません。よく、とにかく。

INSERT INTO users(username, col1) 
SELECT 'max', 'val1' 
WHERE NOT EXISTS (SELECT * FROM users WHERE username = 'max') 

彼はすでに存在していない場合にのみ、この新しいユーザを挿入します。同時に、より安全な、より速く -
単純に処理することができます。 PostgreSQLは、すでに存在しているかどうかによって、コマンドステータスを0 rows affectedまたは1 row affectedに設定します。いずれにしても、この声明の後にはそこにいます。

あなたが戻って答えをしたい場合:

INSERT INTO users(username, col1) 
SELECT 'max', 'val1' 
WHERE NOT EXISTS (SELECT * FROM users WHERE username = 'max') 
RETURNING username; 

がまだ存在していなかった場合にのみ、これは、ユーザ名を返します。
ただし、操作は、並行処理の多くを持っているので、もし、このようなテーブルのロックを取得、アトミックではありません:これはまだしても非常に低い場合、失敗する可能性があること

BEGIN; 
LOCK TABLE users IN SHARE MODE; 

INSERT INTO users(username, col1) 
SELECT 'max', 'val1' 
WHERE NOT EXISTS (SELECT * FROM users WHERE username = 'max') 
RETURNING username; 

COMMIT; 

注 - インスタンスの場合別のトランザクションがテーブルをロックし、何らかのエラーのため永遠にブロックします。
だから、間違いなく、エラーケースを処理するコードが必要です。データベースやアプリケーションに問題がなければ、決して発生しないはずです。

0
あなたはの線に沿って回答の多くの例外が制御用ではありません」と「例外は、プログラムの流れを制御するために使用すべきではありません」を取得します

フロー"。確かにあなたはすでにいくつか持っています。あなたは、私のように、これらのステートメントが完全に無意味であると感じるかもしれません。例外doはプログラムの流れを制御し、例外をスローするようにAPIが設計されている場合は、それに応じて使用するしかありません。 はその要点です。それを投げるメソッドを呼び出すときに、EOSを検出する他の方法はありません。

この場合、異なる原則が適用されます。テストしてから設定すると、タイミングウィンドウが導入され、その間に後続のセットが失敗する可能性があります。セットされた操作で例外がスローされる可能性がある場合は、それをコードする必要があります。これらの状況では、セットを実行し、それに応じて例外を処理する必要があります。あなたの操作はアトミックなので、同じコードを2回書く必要はありません。一般に、リソースが利用可能かどうかを検出する最も信頼性の高い方法は、リソースを使用しようとすること(ネットワークサーバーに接続することを検討することです)であり、操作が失敗するかどうかを検出する最も信頼できる方法は、すなわち、それが一意のキーであるデータ構造に値を挿入する)。

「プログラムのフローを制御するために例外を使用するべきではない」というルールはもともと狭いコンテキストから来ていました。つまり、同じメソッド内で捕捉する例外GOTOの一種。しかし、この業界では非常に一般的であるように、当初のモチベーションは、私が愚かな、オウムのような反復としてしか記述できないものに賛成して完全に忘れ去られてきました。

+0

スロー例外にはパフォーマンスの問題がありますか?その場合、私は例外を投げるのを避けるべきです。 – Ashwin

+0

これがなぜ落とされたのか分かりませんが、それは理にかなっているので、私はそれをupvotingしています。ルールを誤って適用する理由は、何人かが理解していないにもかかわらず、ジャンプやブレークを繰り返すことはなく、関数から複数のリターンポイントを持つことはありません。これらのコードではコードを読むことができない場合でも、彼らはあなたがオーバーエンジニアリング 'while'ステートメントを避けるために得ることができるごみよりもさらにもっと読みやすいです:-) – paxdiablo

0

あなたは、任意のチェックを行わずに、直接ユーザー名を挿入し、同じユーザー名を仮定した場合、データベースに存在し、その時点で、あなたは、例外が発生しますそれ以外の場合は、データベースで正常にレコードを挿入します。標準的なコーディング慣行(私の見地から)で、まず一意性をチェックし、データベースにユーザー名が存在しない場合は、データベースにレコードを挿入してください。

関連する問題