むらぶろ - .NETって面白い -

ボタンをかけ違えたらまずはボタンをはずす。そしてひとつづつかけていく。
投稿数 - 24, コメント - 191, トラックバック - 25

SQL Server によるIDの生成

インスパイア元はtatsumihrさんのBlogです。
最近感動したこと
このエントリを元にけろさんと話をしていたら、流れで「SQL ServerのIdentityについて纏めなさい」と命ぜられてしまいました。
ですので、また自分なりに纏めてみたいと思います。


IDENTITYプロパティ

SQL Serverに一意のIDを生成させる方法として、列にIDENTITYプロパティを指定する方法があります。
IDENTITYプロパティには次の特徴があります。

  • ひとつのテーブルのひとつの列にしか指定できません
  • 指定する列のデータ型はint型bigint型smallint型tinyint型decimal型numeric型に限ります。
  • (設定により変更可能ですが)基本的に値のセットは出来ません

IDENTITYを指定した列に値を挿入したい場合は、IDENTITY_INSERTプロパティをONに設定します。
この設定は同一セッション内でのみ有効です。


新しく付与されたIDの取得方法

IDENTITYプロパティにより新しく生成されたIDを取得する方法には次の3つの手段があります。

  • @@IDENTITY
  • SCOPE_IDENTITY
  • IDENT_CURRENT

いずれも、生成されたIDを取得すると言う意味では同様のものではありますが、それぞれに特徴があります。

  • @@IDENTITYはそれを指定したセッション内任意のスコープにて最後に生成されたIDを返します。
  • SCOPE_IDENTITYはそれを指定したスコープ内およびセッション内で最後に生成されたIDを返します。
  • IDENT_CURRENTは任意のスコープ任意のセッションにおいて特定テーブルに対して最後に生成されたIDを返します。

一見すると同じものを返す様に見えますが、「テーブルに新しく行を追加した結果、生成されたIDを取得したい」場合には注意が必要です。
例えば、@@IDENTITYは任意のスコープ(モジュールやストアドプロシージャなどの単位)で生成されたIDを返すので、トリガによりIDENTITYプロパティを指定したテーブルへの挿入をしていた場合には、トリガにより生成されたIDを返してしまいます。

20070807-1.jpg

また、IDENT_CURRENTはテーブルを限定しますがセッションを限定しないので、対象テーブルを更新するトランザクションが同時実行された場合に意図しないIDを返す恐れがあります。さらにスコープを限定しないので、トリガにより自身のテーブルに列を挿入する場合にも意図しないIDを返す恐れもあります。

20070807-2.jpg

従い、「テーブルに新しく行を追加した結果、生成されたIDを取得したい」と言う要件に対する解決策としてはSCOPE_IDENTITYを使うことが最善と言えます。
SCOPE_IDENTITYを取得する際は、SELECT SCOPE_IDENTITY で取得できます。


IDの生成にnewid関数を利用する方法

一意の値をSQL Serverに生成させる方法として、他にnewid関数を利用する方法もあります。
newid関数は、uniqueidentifier型の一意な値(GUID)を生成する関数です。
このnewid関数をuniqueidentifier型の列の規定値にすると、値が指定されなかった場合に新しいGUIDを生成してくれます。
データ型も違いますのでIDENTITYプロパティとはまったく異なるものですが、一意の値を生成すると言う目的に対しては有効です。
IDの生成にnewidを使用する場合の特徴は次の通りです。

  • newid関数自体はひとつのテーブルの複数の列で指定可能です。
  • 規定値にnewidを指定できる列のデータ型はuniqueidentifierです。
  • newidを規定値に指定していても、値のセットは可能です。

IDの生成にnewid関数を利用する事の利点と欠点

newidを規定値に指定していてもデフォルトで値の挿入が可能です。
しかもIDENTITY列と異なり、プログラム側からSystem.Guid.NewGuid()メソッドで挿入するIDを生成できるので、SCOPE_IDENTITY等を利用して事後にIDを取得する必要はありません。
もちろん、欠点もあります。
業務要件により「挿入された順序の把握が必要な場合」には、何らかの別項目が必要となります。
また、Guid.NewGuidで生成されたIDは理論上2^128分の1の確立で重複しますので、この確立が無視できない場合は相応の重複チェックが必要となります。


最後に

けろさん、こんな感じで纏めてみましたが如何でしょうか。
そうそう、もう宿題はご勘弁頂きたく...

投稿日時 : 2007年8月7日 15:39

フィードバック

# re: SQL Server によるIDの生成

さすがだぁ~。キレイにまとまってます。

SELECT @@IDENTITY

を使わないで、

SELECT SCOPE_IDENTITY()

を使いましょう!と言うものの、なかなかうまく説明できないんですよね。

それから、GUIDって、2^128分の1でバッティングするんですね。
ま、よほどのことが無い限りバッティングしないでしょうけど、大量データを扱うシステムで
GUIDを使っている場合は、存在チェックがいるかもしれませんね。
もし、バッティングした場合は、再度、newidまたはSystem.Guid.NewGuid()で
再生成して、存在チェック?(笑)
2007/08/07 15:54 | けろ

# re: SQL Server によるIDの生成

>そうそう、もう宿題はご勘弁頂きたく...

そんなこ言わずに、今後もお願いしますw (わかりやすいので...)
なんたって、ASP.NETの本質をこのけろ様に教えてくれた師匠ですからね。
LINQ版、IDENTITYについては、けろ様の方で調べて纏めておきますw
(そのうち、Blogに結果を掲載しておきます)

2007/08/07 17:17 | けろ

# re: SQL Server によるIDの生成

非常にわかりやすく、タメになりました。
そういう違いがあったのですね。
IDENT_CURRENTとnewidは知りませんでしたし。
とりあえず、今のプロジェクトではSCOPE_IDENTITYしか使ってないから問題ない、と。。。。
2007/08/07 19:37 | いしだ

# re: SQL Server によるIDの生成

そうそうSCOPE_IDENTITY()なんだよなぁと思ってソースを見返してみると.NET2.0のTableAdapterはちゃんとSCOPE_IDENTITY()を使うコードを吐いてました。昔は書き換えてた記憶があるんですが気のせい??
2007/08/07 20:30 | 黒龍

# re: SQL Server によるIDの生成

きれいにまとめていただいたようで、ありがとうございます!
私の無知がみなさんの議論の元になったようで、結果オーライということで。(笑)

それはそうと、私のblogの記事からむらさんのこの記事へリンクを張っても良いでしょうか?
2007/08/07 22:21 | tatsumihr

# re: SQL Server によるIDの生成

> 黒龍さん
> TableAdapterはちゃんとSCOPE_IDENTITY()を使うコードを吐いてました
ごめんなさい、デザイナでTableAdapterを作成してみたのですが確認出来ませんでした...

> tatsumihrさん
もちろん結構です!こんな記事で良ければ、是非。
ところでこの記事では触れておりませんが、OUTPUT句をご存知ですか?
INSERTしたIDを取得するのであれば、INSERT文にOUTPUT INSERTED.IDを追加してもIDは取得できます。
この件もあわせて纏めればよかったと猛烈に後悔...orz
2007/08/08 10:01 | むら

# re: SQL Server によるIDの生成

>> TableAdapterはちゃんとSCOPE_IDENTITY()を使うコードを吐いてました
>ごめんなさい、デザイナでTableAdapterを作成してみたのですが確認出来ませんでした...
構成ウィザードの詳細オプションの
「INSERT、UPDATE、およびDELETEステートメントの生成」と
「データテーブルの更新」
にチェックを入れればInsertコマンドに下記のものが生成されると思います。
INSERT INTO [dbo].[TestTable] ([name]) VALUES (@name);
SELECT id, name FROM TestTable WHERE (id = SCOPE_IDENTITY())
SQLServerでないと無理だとは思いますが。
2007/08/10 19:04 | 黒龍

# re: SQL Server によるIDの生成

> 黒龍さん
確認できました!
おっしゃるチェックを入れて生成しては失敗を4回ほど繰り返し、「なんで出来ないんだろう!?」と悩むこと30分...
Updateメソッドが生成されていないことを発見して原因が分かりました。
”idの主キー設定が外れていた”のでした...orz

お恥ずかしい限りです。ありがとうございました。

#でも何でOUTPUT句を使ったコマンドを生成してくれないんだろう...
2007/08/13 9:31 | むら

# Visual Studio 2005のTableAdapterとSQLのIDENTITY

Visual Studio 2005のTableAdapterとSQLのIDENTITY

#  IDENT_CURRENT を2つのトランザクションで同時実行したときに一方に異なるIDが取得されてしまう

IDENT_CURRENT を2つのトランザクションで同時実行したときに一方に異なるIDが取得されてしまう

# re: SQL Server によるIDの生成

>でも何でOUTPUT句を使ったコマンドを生成してくれないんだろう...

の答えは、SQL Server2000も対応だからでしたね。
http://blogs.wankuma.com/mymio/archive/2007/08/25/91833.aspx

一応、訪れてる方が多いようなので、掲載しておきますね
2008/01/06 13:07 | けろ-みお

コメントの投稿

タイトル  
名前  
URL
コメント