- - PR -
ユニークキー生成
投稿者 | 投稿内容 | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2005-05-12 15:21
返信遅くなって申し訳ございません。
Jittaさん、きくちゃんさんありがとうございます。 > ADO.NET の場合、開いている Connection オブジェクト = セッション と考えて > 良かったと思います。 > つまり、.Open() 〜 .Close() の間が 1 セッションだったはずで、変にインスタンス > を共有とかしてない限り問題ないと思います。 そうだったのか。。。なるほどです。 現在のセッション内で登録された最後のレコードのIDを取得するという事が やっと分かりました。だから、同時にレコードを追加しても大丈夫なんですね。 > #と言いつつも、一応、実証実験とかやって裏は取って下さいね。 今まで実証実験を行っていました。今のところ大丈夫みたいです。 一応時間差(sleep)なども使っていろんなパターン試しましたけど 特に問題ありませんでした。 ほんとにいろんな情報ありがとうございました。一応これで誰かからの 受け答えも出来そうです。 | ||||||||||||||||||||||||||||||||||||||||||||
|
投稿日時: 2005-05-12 19:17
時間を入れるとユニークキーになるというのはデータベース設計として間違い。ふつー、getdate() や sysdate は SQL ステートメント実行中で常に同じ値を持つので、insert into ... select ... など 1ステートメントで複数行挿入するときに困ります。
| ||||||||||||||||||||||||||||||||||||||||||||
|
投稿日時: 2005-05-13 09:30
どもです。がるです。
んっと…多分私宛だと思うので、少しだけ。
このあたりの「正しい/間違い」は、きちんとした定義を基に会話をしたいのですが 如何でしょうか?
とりあえず「"ふつー"ってなんですか?」とかいう突っ込みもあるのですが。 # 普通とか常識とか。SEにとっては「致命的に危険な」単語だと思っておりますので :-P そこからやると議論が複雑になるので、とりあえず 「何らかの規約もしくは制限事項において "getdate() や sysdate は SQL ステートメント実行中で常に同じ値を持つ" は常に真である」 と仮定します。 この場合、未記入さんのおっしゃる 「時間を入れるとユニークキーになるというのはデータベース設計として間違い」 というのは、 ・SQL構文中で時間を取得、ユニークキーを作成 という条件を満たしたときに、確かにおっしゃるとおり 「1ステートメントで複数行挿入するときに困ります。」 というお話が成り立ちます。 ただ、私が提案しているのは ・別途プログラム中でユニークキーを作成する 方法ですので、その場合、おっしゃるような問題は発生しないように 思われるのですが如何でしょうか? というか、私の提案ですとそもそも ・1SQLステートメントで複数行のINSERT って発生しないですし :-P # insert into ... select ... 構文の時は大抵「すでにあるキー」を用いますし このあたりは、私が常々思っているのですが ・SQLで可能な限りいろいろなことをさせる ・プログラムで可能な限り処理をして、SQLには単純なことしかやらせない というスタンスの差なのかなぁ、と思っております。 私が見ている限りでは、俗に「データベース屋さん」という方は前者を、 プログラマさんは後者を選択しやすい傾向にあると思います。 # っつか、うがった見方をすると。 # データベース屋さんはSQLを「スプリクト言語」寄りにみなし、 # プログラマは「1命令文」寄りにみなしているように思われてみたり :-P 興味のある反論としては ・**が理由でやっぱり時間を使ってのユニークキーはNG とかいうラインでしょうか? 盛り上がりそうなら別スレッド立てます :D | ||||||||||||||||||||||||||||||||||||||||||||
|
投稿日時: 2005-05-13 10:26
日時を元にプライマリーキーを生成すると言うのは、やっぱり賛同しかねますよね。
日時を元にキーを生成した場合、同じキーが生成される可能性が常にあります。その場合どうするつもりなんでしょう?一意制約エラーが発生したら、エラーが発生しなくなるまで繰り返し挿入を続けるつもりなんでしょうか? 例えば、コンピューターにとって時間は必ずしもシーケンシャルに進むものではありません。ActiveDirectoryやNTPを使って時刻同期を行っていた場合、時間が数秒戻る事もありますよね。どうするんでしょうか? OSや言語、データベースによって時刻の分解能なんてまちまちですよね。必要な分解能を得られる方法を、勘違いせずに実装する自身なんて、私にはありません。 サーバーの負荷が高いときには、同時刻で挿入してしまう可能性が高くなると思うんですが、どうするのでしょうか? 私なら実直にシーケンス番号を生成するための仕組みを作るけどなぁ・・・。 [ メッセージ編集済み 編集者: 甕星 編集日時 2005-05-13 10:28 ] | ||||||||||||||||||||||||||||||||||||||||||||
|
投稿日時: 2005-05-13 10:27
時刻を使ったユニークキーだと危険な場合もあるかと。
・システムのクロック刻みが大きい場合。 昔使っていたNEC PC9821+Win95環境では、素の状態ではミリ秒単位が一定値になっていた記憶があります。OSパッチを当てると直るのですが。(って、これだと一種のバグですね。) これは極端な場合としても、他のシステムでもマイクロ秒の一桁目までの刻みがあるとは限りませんので、同じ時刻を返す可能性は否定出来ないと思います。 ・マルチCPUの場合。 これもシステム依存だと思いますが、マルチCPU、マルチスレッドの場合、もしかしたらそれぞれのCPUで処理しているスレッドが同じ時刻を取得してしまうかもしれません。 ・NTP等で時刻修正をしている場合。 時刻を進める方向に修正できるなら影響はないと思いますが、時刻を戻す方向に修正する場合は、同じ時刻が2度発生する可能性がありますよね? 以上、かなり頻繁に時刻を取得しない限りは大丈夫な場合が多いとは思いますが、絶対に安全と言うものでもないと思います。 | ||||||||||||||||||||||||||||||||||||||||||||
|
投稿日時: 2005-05-13 10:30
私もよく時間をキーにしますが、郵便局のATMで偶然キーが重複して
障害が起きたニュースを記憶しています。 同じ口座番号に対して全く同じマイクロ秒に操作を行ったとかで、 怖いなーと思いました。 (今ソースを探したのですが見つからない・・・夢???) | ||||||||||||||||||||||||||||||||||||||||||||
|
投稿日時: 2005-05-13 11:12
どもです。がるです。
早速のコメントありがとうございます。 で。一応前提なのですが。前述したコードをもう一度添付します。
で、ちょいと反論というか、質問というか、なのですが。
んっと。 「同じキーが生成される可能性が常にあります。」 無論ありえます。ただ、「同一プロセスID内(必要に応じてthread毎にもユニークにthread IDが取得できます)で同一のキーが生成される可能性」は単純に低いです。 で、「一意制約エラーが発生したら、エラーが発生しなくなるまで繰り返し挿入を続けるつもりなんでしょうか?」はyesです。
最終的には「INSERTでチェック」してます。
時刻は別に分解する必要はなくて、エポック秒が取れればよいです。なので、そういった点では言語の制約をあまり感じてないです。 OSについては…ぶっちゃけ、Windowsは除外してます :-P 個人的には「シーケンシャルな番号を取得する」ロジックのほうが、DBMSの差異を感じてしまい、ちっとしんどいなぁと思ってたりします。 # 自力で「シーケンシャルテーブル作ってインクリして取得してその辺をトラン処理して」って作っちゃえばいいのですが :-P # 「せっかくDBにその機能あるしなぁ」とか思って使って直前で「ごめんDBMS変えていい?」とか爆弾を投げられることも多々あるので…結構怖いです。 # 環境が悪い? ごもっとも :-P
この辺は興味もあってテストをしたのですが。 数百プロセスで数千thead/1プロセス、の環境でしば〜らく(1時間ほど)テストしてたのですが(LinuxとSoralis。Linuxは1プロセスあたり1000thread程度が限界なので、プロセスを数千のオーダーでテスト)。 結果は「一度も重複せず」でした。 まぁ、手法としては「外から袋を叩いて中身を判定」なのですが。
多分確実に一つの「よい方法」だと思うのですが。 DBとのトラフィックをつい気にしてしまうので、「プログラム内部で片付く」方法を採用した、ってのが理由でしょうか?
この辺のOSの差異はやっぱり気にはしてますねぇ。 なので、Windows環境で…と言われたら、別手段に走るか、最低でも「耐久テスト」はしてみたいところです。 ちなみに、マイクロ秒の1桁での刻みは…多分、Linuxでもちゃんとしてないと思います :-P でも、そこまでではなくても、とりあえず1msが計れる程度の制度なら十分問題なく動くと思います。
これは「キーにプロセスIDと、必要に応じてthread IDを使う」ので、特に問題ないです。
これはyesです。なので、INSERTのエラーチェックは必須ですね。 実はこれを考えて「さらにキーに乱数を…」とか考えてたことも在るのですが :-P 元々「ある程度お手軽に」という前提のものなので、そこまでは作りこんでないですね。
これが正解なんだと思います。 まぁ、なので「INSERTチェック」で危ない可能性を拾ってるのですが :-P
怖いですねぇ。 そういう意味では、やはり「最後の念のためチェック」の受け皿は 必須なんだろうと思います。 | ||||||||||||||||||||||||||||||||||||||||||||
|
投稿日時: 2005-05-13 12:29
データベースを単なるストレージとしてしか使用しない・できないプログラマが増えていますね。一意制約も外部参照制約も全部プログラムでチェックしているからいいんだっ!っていう人がたまにいるけど、私はそういった意見には賛成できない。 なるべくデータベースの機能を使用して安全性を高めるべきです。がるがるさんの制限付き提案は、該当テーブルを操作するすべてのクライアントプログラムがルールに従わなくてはならないという厳しい縛りがつきます。そのプログラムを使ってるだけなら問題は起きないかもしれませんが、通常運用外の保守作業で SQL*PLUS や クエリアナライザで直接データを流し込む要件が発生したときに困りませんか? (私の今までの経験から) 作りこまれた専用プログラム(入力画面?)だけで、すべてのデータを登録できることは非常に稀だと思っています。初期データのインポートだけでなく、運用開始後でも 専用プログラムを用いない様々なデータの流し込みが発生することは多々あります。そういったときに特定のプログラムに実装されたチェック機能が必要になっていると手作業ができなくて非常に困るものです。 それに、SQL Server にも Oracle にも 連番を採番する機構が備わっているのに、それを使わず、独自のユーザーコードで一意性を確保するロジックを実装する積極的な理由が私には思いつきません。 # 自分で何でも組もうとするのは、バカなプログラマ。 # 他(ミドルウェア)でできることは端折って楽するのが、ふつーのプログラマ。 |