- PR -

ADOからストアドへパラメータを渡す時にエラー

1
投稿者投稿内容
perche
会議室デビュー日: 2006/06/28
投稿数: 4
投稿日時: 2006-06-30 15:27
初めての投稿です。宜しくお願いします。

ADOからストアドへパラメータを渡したいのですが、実行時エラー3265「要求された名前、または序数に対応する項目がコレクションで見つかりません」が出てしまいます。
トランザクション開始後、ストアドAを実行し、次にストアドBを実行しようとすると上記のエラーが発生します。
トランザクション処理を省けば解消されるのですが、省かずに解決する方法がわかりません。
また、.Parameters.Countをチェックしてみると「0」が返ってきます。
何をどうすればいいのでしょうか?

ADO側
--------
'トランザクション開始
saCn.BeginTrans

'ストアドA実行
With cmd
.CommandText = "usp_Search_Kouho"
.CommandType = adCmdStoredProc
.Parameters.Refresh
.Parameters(1) = frm!cmb_OLDCD <--問題なし
End With

Set rst = cmd.Execute

If cmd.Parameters("@ERR").Value <> 0 Then
rst.Close: Set rst = Nothing
GoTo UspErr
End If

'ストアドB実行
With cmd
.CommandText = "usp_Search_Universe"
.CommandType = adCmdStoredProc
.Parameters.Refresh
.Parameters(1) = frm!cmb_OLDCD <--ここでエラー
End With

Set rs = cmd.Execute

If cmd.Parameters("@ERR").Value <> 0 Then
rst.Close: Set rst = Nothing
GoTo UspErr
End If

  以下、処理が続く

ストアドB
----------

Alter PROCEDURE usp_Search_Universe
@OLDCD varchar(4),
@ERR int OUTPUT
AS
SELECT TOUROKU,ANALYSTCODE,ANALYST
FROM T_Universe
WHERE OLDCD = @OLDCD

IF @@ERROR<>0
SET @ERR = @@ERROR
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-06-30 18:18
perche さん、こんにちは。

引用:

percheさんの書き込み (2006-06-30 15:27) より:

また、.Parameters.Countをチェックしてみると「0」が返ってきます。
(snip)
.Parameters(1) = frm!cmb_OLDCD <--ここでエラー


.Parameters.Append([object]) ではないのでしょうか?
私としましては、むしろ 「ストアド プロシージャ A」 が問題ないのが謎です。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
TLC
大ベテラン
会議室デビュー日: 2005/05/31
投稿数: 152
お住まい・勤務地: 東京都
投稿日時: 2006-07-01 00:26
引用:

percheさんの書き込み (2006-06-30 15:27) より:

ADOからストアドへパラメータを渡したいのですが、実行時エラー3265「要求された名前、または序数に対応する項目がコレクションで見つかりません」が出てしまいます。
トランザクション開始後、ストアドAを実行し、次にストアドBを実行しようとすると上記のエラーが発生します。
トランザクション処理を省けば解消されるのですが、省かずに解決する方法がわかりません。



お疲れ様です。
TimberLandChapel です。

パラメーターの作成と Refresh の問題ではなく,
単に ADO でのトランザクションの使用法に関する問題です。

以下のソースで試してもらうとわかりやすいのですが,
「手動または分散トランザクション モードのため、新規接続を作成できません。」
というエラーが発生するはずです。

ADO だと仕様だと思うんですが,
2重に SELECT 接続を開けません。

perche さんのストアドの名称からして,選択を行うクエリですよね?

そもそも,トランザクション内で,
更新を伴わないデータの取得を行う必要性はないはずです。
ロックとの要件を整理して,
「トランザクションの最短化」を行ってください。

■改善方法
半分以上はリファクタリングの話になりますが,

1.コマンドオブジェクトの使い回しはしない
2.トランザクション内では純粋にデータの変更のみを送信する
3.トランザクション外ですべての更新に必要な情報を整えておく

という風にコードを改善してください。

↓多少書いています。
http://timberlandchapel.com/adotips.html

[CODE]-----
Private Sub Test()
  On Error GoTo Catch
  Dim connection As New ADODB.connection
  connection.ConnectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Test;Data Source=METATRON\SANDALPHON;"

  Dim command As New ADODB.command
  Dim secondCommand As New ADODB.command
  Dim recordSet As New ADODB.recordSet

  connection.Open
  connection.BeginTrans

  With command
    .ActiveConnection = connection
    .CommandText = "usp_Test1"
    .CommandType = adCmdStoredProc
    .Parameters.Refresh
    .Parameters(1) = 1
  End With

  With secondCommand
    .ActiveConnection = connection
    .CommandText = "usp_Test2"
    .CommandType = adCmdStoredProc
    .Parameters.Refresh
    .Parameters(1) = 1
  End With

  Set recordSet = command.Execute

  If command.Parameters("@ERR").Value <> 0 Then
    Err.Raise vbObjectError + 1001
  End If

  Set recordSet = secondCommand.Execute

  If command.Parameters("@ERR").Value <> 0 Then
    Err.Raise vbObjectError + 1002
  End If

  connection.CommitTrans
Finally:
  If Not recordSet Is Nothing Then
    If recordSet.State <> adStateClosed Then recordSet.Close
    Set recordSet = Nothing
  End If

  If Not command Is Nothing Then
    Set command = Nothing
  End If

  If Not secondCommand Is Nothing Then
    Set secondCommand = Nothing
  End If

  If Not connection Is Nothing Then
    If connection.State <> adStateClosed Then connection.Close
    Set connection = Nothing
  End If

  Exit Sub
Catch:
  connection.RollbackTrans
  GoTo Finally
End Sub
[CODE]-----

----------
TimberLandChapel
http://blogs.timberlandchapel.com/blogs/timberlandchapel/
perche
会議室デビュー日: 2006/06/28
投稿数: 4
投稿日時: 2006-07-03 11:39
レスありがとうございます。返答が遅くなりました。

お二人の回答を拝見する前に、カーソルの問題なのではという記述を見つけ、
saCn.CursorLocation = adUseClient
Set cmd.ActiveConnection = saCn
とすることで、一応解決はしました。

ただ、TLCさんのアドバイスにあるようなことには沿っていませんよね。
トランザクションは初めてまともに取り組んでいるところで、右往左往しているのが
現状です。とても難しく感じています。
TLCさんからのアドバイスを元にトランザクションを開始する位置を変更してみたのですが、
同様のエラーとなってしまいました。

実際のコードは、「以下、処理が続く」とあるようにもっと長いものです。
ストアドAとBは確かに更新系のものではないので、トランザクション外としました。
次に更新系のストアドCを実行するので、この直前でトランザクションを開始するように
したのですが、ここでエラーとなりました。

以下はストアドCの内容です。
DELETEする前にSELECT COUNT(*)〜等があるからエラーとなるのでしょうか?
もう少しお付き合い願えたらと思います。

Alter PROCEDURE usp_MK_Zenkai_Data
@ERR int OUTPUT
AS
SET @ERR = @@ERROR
IF @ERR=0
BEGIN
--テーブルの初期化
IF (SELECT COUNT(*) FROM WT_Zenkai_Data)>0
BEGIN
DELETE FROM WT_Zenkai_Data
--エラー値を取得
SET @ERR = @@ERROR
END
END

IF @ERR=0
BEGIN
--実績データと予想データをひとつのテーブルに挿入する
INSERT INTO WT_Zenkai_Data
SELECT * FROM v_Zenkai_Data
--エラー値を取得
SET @ERR = @@ERROR
END

IF @ERR<>0
PRINT 'エラーが発生しました: エラー番号' + CONVERT(CHAR,@ERR)


TLC
大ベテラン
会議室デビュー日: 2005/05/31
投稿数: 152
お住まい・勤務地: 東京都
投稿日時: 2006-07-05 13:00
引用:

percheさんの書き込み (2006-07-03 11:39) より:

TLCさんからのアドバイスを元にトランザクションを開始する位置を変更してみたのですが、
同様のエラーとなってしまいました。

実際のコードは、「以下、処理が続く」とあるようにもっと長いものです。
ストアドAとBは確かに更新系のものではないので、トランザクション外としました。
次に更新系のストアドCを実行するので、この直前でトランザクションを開始するように
したのですが、ここでエラーとなりました。

以下はストアドCの内容です。
DELETEする前にSELECT COUNT(*)〜等があるからエラーとなるのでしょうか?




お疲れ様です。
TimberLandChapel です。

設計のスタイルの違いが大きく出ていますので,なかなか難しいところだとは思うのですが
(さらにそのスタイル自体にも賛否両論ありますから)

直近では,
EXISTS を使用することで,結果セットを返さないデータの存在判定を行うことで,
一部解決できると思います。

さて,ここからは議論になるところだと思いますが,

トランザクションで行う更新は,
その更新が実行される前の状態のみに依存するはずです。
これは,トランザクションの原子性を確保するために非常に重要です。

とすると,
その UPDATE 文を発行するべきか否かという判断は,
トランザクション開始前にすべてとることができるということになります。

なので,自分は
■トランザクション用のコマンドを組み立てる前に
■すべての必要な判定条件をローカル側にキャッシュする
■必要な更新を同時実行制御をかけながらトランザクション化する

とします。
このためには,やはりCRUIDレベルからどこで何が判定され,更新されるべきなのかを
チューニングすることになると思います。

----------
TimberLandChapel
http://blogs.timberlandchapel.com/blogs/timberlandchapel/
perche
会議室デビュー日: 2006/06/28
投稿数: 4
投稿日時: 2006-07-06 09:27
TLCさん、レスありがとうございます。

いろいろ試行錯誤した結果、下記のような方法に落ち着きました。
1)ストアドAとBは存在確認のみなので、ストアドを使わずレコードセットを作成
2)ストアドCの直前でトランザクションを開始する
3)ロックをかけることで対処できる部分は、トランザクション外へ出しました。

これでエラーが出ることもなく、実行できるようになりました。
その他、TLCさんのアドバイス通り、ストアド実行前に条件判断を行うように心がけました。
時間的な制約もあり、全面見直しというわけにはいきませんでしたが、それでも当初の記述
と比べると大分短時間でのトランザクションで済むようになったと思います。

トランザクションとロックがどうもごちゃごちゃになってしまって、余計に混乱したようです。
結果、何でもトランザクションの中にいれこんでしまうような形になっていたりしました。
その辺りも自分なりに整理してみました。

お時間さいていただき、ありがとうございました。
今後ともよろしくお願い致します。

1

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