VBScriptでバッチを作っていたのですが、今回もドはまりしたので、こちらに残しておきます。
identityが設定されたテーブルへのINSERT
これ自体はよくあるケースですね。テーブルのID的な列にidentityを設定しておけば、行追加の際にデータベース側が自動で連番を振ってくれる為、アプリケーション側では採番を意識しなくても良くなり、システムの設計次第では大変便利です。
ただ、ADOを使って、採番された番号を行追加と同時に取得し、その番号をレコードセットに戻し、その番号を使って別テーブルの行追加や更新を同一トランザクション内でやろうとしてはまりました。
例えばINSERTを実行して、その直後に
SELECT @@identity
等で自動採番された値は取れます。ただ、INSERTの実行とSELECTの実行タイミングを限りなく同時に近づけても完全な同時にはならないため、別のプロセスやセッションで同時にINSERTが実行された場合に、SELECTで取れた番号は自分自身がINSERTした際に生成された番号では無い可能性があります。
ネットで調べたところ、INSERT文の後に ;(セミコロン)で繋げて SELECT @@identity を一塊のSQL文としてまとめて実行すれば良いとのこと。↓こんな感じ
INSERT INTO テーブル名 VALUES (値1,値2,値3);SELECT CAST(SCOPE_IDENTITY() AS int) 最大値
なんだこんな簡単な話かと実装しようとしたところ、「セミコロンの後ろのSELECT結果はどう受けとればいいんだ??」と固まってしまいました。
取り敢えず以下のように実装し、実行結果をRecordsetへ戻してみたところ、INSERTは成功しており、行が追加されているのは確認できました。じゃあSELECT結果も取れているか確認すると、
Set objRs_Max = objCon.Execute(query)
Msgbox objRs_Max(“最大値”).Value
値が取れない…、エラーにはならないけど、空みたいです。
色々調べたところ、SQL文をセミコロンで区切って実行していることで、データベースとしては二つのSQLを順番に実行し、それぞれの実行結果を戻してきており、それを一つのRecordsetオブジェクト変数に格納した状態になっている様子。
その場合は以下のように取り出す必要があるみたい。
'前処理の記述は省略 'まずSQL文字列が入っている変数:query実行し、objRs_Maxに代入します。 Set objRs_Max = objCon.Execute(query) 'objRs_Maxに入っているレコードセットの一つめはINSERT実行分の結果が '入っており、SELECT実行分の結果は次のレコードセットに入っている為、 '以下のように記述します。 Set objRs_Max = objRs_Max.NextRecordset 'これで無事SCOPE_IDENTITY()の値が取り出せました。 MaxSeqNo = objRs_Max("最大値").Value
上記のサンプルコードにあるように、RecordsetオブジェクトのNextRecordsetメソッドで次のレコードセットにアクセス出来るようになるみたいです。
今回の記事は以上です。皆様のご参考になりましたら幸いです。