noteプログラミング関連2012-06-07人工キー(主キー自動生成)について、おさらいdatabase主キー(プライマリキー)を自動生成する方法について、おさらいしてみました。自然キーが持てない場合にシステム内部で生成するキーのような意味です。(一部、Wikipediaを参考にしています。)自然キーの例としては、社員番号やURIなどのIDがあります。人工キーは、外部から意味のあるキーが渡されないようなケース、たとえばログのような情報は、キーにできそうな情報はタイムスタンプくらいしかありません。ところが、主キーは一意=ユニークであることが確実でなければならず、タイムスタンプだけでは一意性に難があります。ユニークなキーを生成する一般的な方法として、アトミックに連番を生成(シーケンス)グローバルな一意IDの生成(UUID,GUID)O/Rマッパーの機能を使うなどがあります。これを標準SQLでどのRDBMSでも同じようにできたら理想的。現実は、標準SQL規格のSQL:2003でやっとID型が導入されたものの、対応状況は芳しくなく、どの製品でも同じようにとはいかないようです。アトミックに連番を生成(シーケンス)アトミックに連番を生成する、安全かつ簡単な方法は、シーケンスを利用することです。テーブル列と同期させるのも容易です。RDBMS製品によって操作が異なるのが難点です。ログを登録するテーブルを作ることにしましょう。このテーブルTABLE1の仕様は次のとおり。現在のタイムスタンプを"YYYYMMDDHH24MISS"にフォーマットした文字列(14桁)。日付の範囲は、西暦0〜9999年。TIMESTAMPでも構わないが桁を揃えたいので文字列で。範囲は0〜9999、CYCLIC。それでは、いくつかのRDBMSで*2試していきます。PostgreSQLでは、連番型という自動連番サポートがあります。これは、シーケンス+デフォルト値+インデックスをまとめて作成してくれる機能*3です。SEQUENCE"は仕様を揃えるためなので、無くてもかまいません。)SERIAL型が連番型です。SERIAL型を使わずに書くと、こうなります。Derby)にも、自動連番型があります。('MSG');(JavaDBは日付の文字列変換が面倒なのでTIMESTAMPにしています。)前述のドキュメントによると、連番はシステム表のSYS.SYSCOLUMNSで管理されているようです。Oracleでは、列のDEFAULTにシーケンスの払出し(NEXTVAL)を使用できない仕様(バージョン11.2時点)のため、INSERT文に書くしかない*4ようです。'MSG');他にも、例えばMS-Accessでは、「オートナンバー型」という自動連番型があります。Identifier、日本語では汎用一意識別子と表記されます。UUIDは、分散システム上でどこかが統制を取らずとも、一意に特定可能な識別子の作成を目的としており、UUIDは重複や偶然の一致が起こりえないと確信して用いることができる。WikipediaDBに限らず、分散システムにおいて、ID管理のための集中管理の仕掛け(例えばシーケンス)が不要、というのが特徴です。PostgreSQLでは、バージョン8.3からUUID型をサポートしています。生成関数については、コアモジュールには含まれていないようです。uuid-osspという追加モジュールを入れれば使えるようになるようです(未確認)。Derby)では、UUID1(time-based)を文字列で返すメソッドを作って、それをSQL関数に設定するのが良さそうです。SYSIBM.SYSDUMMY1;Firebirdでは、2.1からGEN_UUIDがサポートされたようです。FAQ)2.5からは、UUID_TO_CHAR関数も使えるようになったようです。RDB$DATABASE;Oracleでは、SYS_GUID関数を使います。結果はRAW(16)で返されます。(K1));実装詳細は不明ですが、連続して実行すると6バイト目(UUIDで言うところのtime_midの下位バイト)がカウントアップされました。SQLServerやAccessでは、データベースの分散などで使われるレプリケーションID型というものがあり、これにGUID(UUIDのMS実装)が用いられています。UUID生成関数はさまざまな言語で提供されていますので、互換性のないSQLで操作するよりも、CHAR(32)ないしCHAR(36)の列に文字列としてアプリケーション側で追加するようにしたほうが楽かもしれません。...とは言ったものの、実装の細かいところは挙動が全然違ったりするので、異なるUUID実装を使って同じマシン上でUUID1を取得するのは避けたほうが良さそうです。O/RマッパーおそらくほとんどのO/Rマッパーで(EJBのEntityBeanでさえ)、ID生成をサポートしているようです。ID生成属性を指定すると、自動でやってくれたり選択可能な生成方式の候補−例えば、シーケンス方式、UUID方式、等々−を出してくれたりします。O/Rマッパーによっては、独自の生成方式(有名なところでは、Hibernateのhiloとか?)もあります。まとめそれぞれの長所と短所をざっと挙げてみます。長所:選択肢が多い、O/Rマッパーの中で"ID発行"としてラッピングされている短所:O/Rマッパーごとに異なる、O/Rマッパーの外で使えないO/Rマッパーを使っているなら、O/Rマッパーの機能を使うのが良さそうです。オンラインとバッチの両方で使われるケースや、異なるシステム間でテーブルを共有したりするケースなどの場合のように、O/Rマッパーを使えないケースもあるかと思います。*1:「主キー」も「自然キー」も「人工キー」も、ふだん使わない言葉なので違和感があります。*2:それにしても某社製品ばかりに。これにMy〜も入れたら大変なことに?*3:バージョンによって詳細は異なります。*4:トリガを使えば実現できますが、いずれにしても互換性がありません。(Java)Firefoxの履歴を書き出すツールをJavaで書いてみました。(2)プロフィールid:argiusはてなブログをはじめよう!argiusさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。