- PR -

SCOPE_IDENTITY取得について

投稿者投稿内容
C#初心者
会議室デビュー日: 2008/11/13
投稿数: 11
投稿日時: 2009-02-05 18:29
開発言語:VC#2005
DB:SQLserver2005

いつもお世話になっています。
つい先日もお世話になったのですが、SCOPE_IDENTITYについて質問があります。
過去の書き込み等を参考にしたのですが分かりませんでした・。

テーブルに新規に行を追加し、その行の主キーでもあるIDENTITYの取得の仕方が分からず困っています。
そのIDENTITYの値を元に別テーブルにレコードを追加したいと考えています。
参考書等を参考にコードを書いてみたのですが、エラーが出てしまいます。

コード:
sqlCmd.CommandText = "INSERT INTO dbo.tblUketuke([顧客番号],[受付日時],[日付],[機関コード])"
                   + " VALUES (@Kno,@Uketime,@Hizukei,@Iryoco);"
                   + "SELECT @OrgID = SCOPE_IDENTITY()";

  sqlCmd.Parameters.Add("@Kno", SqlDbType.NVarChar).Value = strNo;
  sqlCmd.Parameters.Add("@Uketime", SqlDbType.DateTime).Value = strDataTime;
  sqlCmd.Parameters.Add("@Hizuke", SqlDbType.DateTime).Value = newUkeRow[3];
  sqlCmd.Parameters.Add("@Iryoco", SqlDbType.VarChar).Value = newUkeRow[6];

  sqlCmd.Parameters.Add("@OrgID", SqlDbType.Int).Direction = ParameterDirection.Output;
  newProdID = (Int32)sqlCmd.Parameters["@OrgID"].Value;

  da.InsertCommand = sqlCmd;
 
このあとにテーブルをUPDATEしています。


現在上記の様なコードを作っていて、newProdID = (Int32)sqlCmd.Parameters["@OrgID"].Value;の
部分でエラー”NullReferenceException がキャッチされました。オブジェクト参照がオブジェクト インスタンスに設定されていません。”が
出ています。

確かに”@OrgID”には値は入っていなくNULLでした。
何がおかしいのでしょうか?
SQL文自体が違うのでしょうか?

申し訳ありませんが教えて下さい。
宜しくお願い致します。
セラフ
ベテラン
会議室デビュー日: 2005/12/01
投稿数: 95
お住まい・勤務地: 東北の顔の形といえば
投稿日時: 2009-02-05 20:09
SqlCommandにSQL文とパラメータの設定している所まではOKだと思いますが、結果を取得するにはSQL文の発行が必要なのではないでしょうか?

SQL文を発行してからだと、
コード:
newProdID = (Int32)sqlCmd.Parameters["@OrgID"].Value;


もうまくいくのではないかな〜っと思います。

ExecuteNonQuery とか ExecuteScalar あたりのヘルプ見てもらうといいかもです。

試しては無いので、はずしてたらごめんなさい。
C#初心者
会議室デビュー日: 2008/11/13
投稿数: 11
投稿日時: 2009-02-06 09:40
セラフ様回答ありがとうございます。
早速、ExecuteNonQuery、ExecuteScalarを調べExecuteScalarを使用してみました。

コード:
sqlCmd.CommandText = "INSERT INTO dbo.tblUketuke([患者番号],[受付日時],[調剤日],[回],[進捗],[医療機関コード])"
                  + " VALUES (@Kno,@Uketime,@Chozai,@Kai,@Shin,@Iryoco);"
                  + "SELECT @OrgID = SCOPE_IDENTITY()";

sqlCmd.Parameters.Add("@OrgID", SqlDbType.Int).Direction = ParameterDirection.Output;
sqlCmd.ExecuteScalar(); ← これを追加しました
newProdID = (Int32)sqlCmd.Parameters["@OrgID"].Value;

da.InsertCommand = sqlCmd;

da.Update(ds, "tblUke");



この様に変更しテストしてみたところ、IDENTITY値は取得できましたが、テーブルに同じ内容が
書き込まれてしまいました。
sqlCmd.ExecuteScalar();を実行した後でテーブルに書き込み、そしてda.Update(ds, "tblUke");の実行後にもう1回書き込み。

おそらくsqlCmd.ExecuteScalar()を実行した後でda.InsertCommand = sqlCmd;を
したせいだとは思うのですが、これをda.Update(ds, "tblUke");を
した時のみデータベースを更新するのはできないのでしょうか?

質問ばかりで申し訳ありませんが、お知恵をお貸し下さい。
宜しくお願い致します。
会議室デビュー日: 2005/02/09
投稿数: 18
投稿日時: 2009-02-06 10:22
IDENTITY の値は、SQL(INSERT)を実行した後でないと取得できません。

最初のコードでは、SQL実行前にIDENTITY値を取得しているのでエラーですね。
2回目のコードは、SQL実行 → 値取得 → SQL実行 していますね。

単純に、SQL実行 → IDENTITY値取得 するだけで良いのでは。
C#初心者
会議室デビュー日: 2008/11/13
投稿数: 11
投稿日時: 2009-02-06 10:42
た様回答ありがとうございます。
やはり二回SQL文を実行しているせいなのですか。

引用:
最初のコードでは、SQL実行前にIDENTITY値を取得しているのでエラーですね。
2回目のコードは、SQL実行 → 値取得 → SQL実行 していますね。

単純に、SQL実行 → IDENTITY値取得 するだけで良いのでは。


これでいきますと
引用:
da.InsertCommand = sqlCmd;
da.Update(ds, "tblUke");


で行っているSqlDataAdapterでデータベース更新は不要という事なのでしょうか?

引用:
sqlCmd.ExecuteScalar();


この段階でデータベースも更新されているのは問題ないのでしょうか?
まだまだ初心者で、da.InsertCommand→da.Update この流れでデータベースを更新すると思っている者ですから・・。

質問ばかりで申し訳ありませんが、宜しくお願い致します。
会議室デビュー日: 2005/02/09
投稿数: 18
投稿日時: 2009-02-06 11:48
SqlDataAdapter.Update
SqlCommand.Execute系
どちらでもSQLを実行できますが、
INSERTコマンドを実行するのであれば SqlCommand ですかね。
DataSetを使用した読み込み・更新の場合は、SqlDataAdapter。

今回はOUTPUTパラメータでIDENTITY値を取得しているので、
SqlCommand.ExecuteNonQuery() の実行でいいと思いますよ。

今後の為に、ExecuteNonQuery・ExecuteScalarの違い等も
理解しておいたほうがいいと思います。
C#初心者
会議室デビュー日: 2008/11/13
投稿数: 11
投稿日時: 2009-02-06 18:38
た様回答ありがとうございます。

ご指摘の通りにExecuteNonQuery()を実行し、SqlDataAdapterのUpdateはやめました。
すると、思い通りの事ができる様になりました。

でもちょっと気になる所があります。

引用:
SqlDataAdapter.Update
SqlCommand.Execute系
どちらでもSQLを実行できますが、
INSERTコマンドを実行するのであれば SqlCommand ですかね。
DataSetを使用した読み込み・更新の場合は、SqlDataAdapter。


この部分なのですが、実はプログラム上では両方使っています。

コード:
 SqlDataAdapter da = new SqlDataAdapter(selectStr, connStr);
 da.TableMappings.Add("Table", "tblKihon");
 da.TableMappings.Add("Table1", "tblHoken");
 da.TableMappings.Add("Table2", "tblUKe");
 DataSet ds = new DataSet();
 da.Fill(ds);
 DataTable dt = ds.Tables["tblKihon"];
 DataRow newKihonRow = dt.NewRow();
 DataTable dt2 = ds.Tables["tblHoken"];
 DataRow newHokenRow = dt2.NewRow();
 SqlConnection conn = new SqlConnection(connStr);
 SqlCommand sqlCmd = conn.CreateCommand()


複数のテーブルをDatasetで取り込みDataRowに新しい行を追加し、データベースを追加・更新しています。
そのため、今まで、
コード:
da.InsertCommand(UpdateCommand) = sqlCmd;
da.Update(ds, "tblKihon");


でデータベース追加・更新していました。
このコードを使うにあたって、dt.Rows.Addも使用していました。
しかし、ExecuteNonQuery()を使う事により上記のコードは不要となっています。

以上の様に現状しているのですが、これが正しいのか間違っているのかえさえ分かっていません。
もし、指摘内容があるのなら教えて頂けないでしょうか?
初心者の質問の為、意味不明な点があるかとは思いますが、宜しくお願い致します。
turutosiya
常連さん
会議室デビュー日: 2003/06/10
投稿数: 49
お住まい・勤務地: 東京都
投稿日時: 2009-02-07 13:41
データの追加・更新をどの方法で行うかによりますが

1. DataAdapterを経由で行う。ex. DataAdapter.Update()
2. INSERT/UPDATEを自力で実行する。ex. SqlCommand.ExecuteNonQuery()等


DataSetありきのプログラムを書かれているならば、1.のほうがベターでしょう。

DataAdapter.Update()メソッドではInsertCommandプロパティ,UpdateCommandプロパティ,DeleteCommandプロパティに設定されたSqlCommandが内部で実行されます。


DataSet/DataAdapterの役割について整理されるとスッキリして行くと思います。

スキルアップ/キャリアアップ(JOB@IT)