none
[VBScript・ADOの問題]SQLServer(MSDE)ストアドで、レコード追加子ストアドを2つ起動する時、2番目のストアドのエラーが、Accessのadpで表示されない(取得できない)。 RRS feed

  • 質問

  • Access2003のadpファイルから、MSDE2000へ接続するシステムを開発しております。

    ここで、以下のように、子ストアドプロシージャを2つ起動するストアドプロシージャがあるとします。

    CREATE PROCEDURE [親ストアドプロシージャ]
    AS
     BEGIN
      declare @IntErrorCode int

      execute @IntErrorCode=子ストアドプロシージャ1

      if @IntErrorCode<>0
       return @IntErrorCode

      execute @IntErrorCode=子ストアドプロシージャ2

      return @IntErrorCode
     end

    子ストアドプロシージャ1、子ストアドプロシージャ2は、それぞれ、
     テーブルA→テーブルB
     テーブルA→テーブルC
    にコピー(INSERT … SELECT … のパターン)するストアドプロシージャとします。
    (各テーブルには、それぞれ主キーが設定されているとします。)

    この時、子ストアドプロシージャ1 で、例えばキー重複エラーが発生する場合は、親ストアドプロシージャ を(adpのデーターベースウィンドウから)起動してもそのエラーが出るのですが、
    子ストアドプロシージャ2 のみで、同様にキー重複エラーが発生する場合、(同様にadpのデーターベースウィンドウから)親ストアドプロシージャ を起動すると、正常終了のように表示されます。
    (要件としては、この時にも子ストアドプロシージャ2で発生したエラーの内容を表示してほしいと思っています。)

    こうなる理由、要件を満たす対処方、参考文献などを教えて頂ければ幸いです。
    (雰囲気としては、常に1件目(この場合は子ストアドプロシージャ1)の結果を返しているのかな?という感じです。)

    追伸:ここで、
    ・親ストアドプロシージャ(の中の子ストアドプロシージャ1と子ストアドプロシージャ2をまとめたもの)をトランザクションにするか?
    というのがありますが、今回の件に関しては、当方の要件ではそれはあまり重要ではなさそうですが、とりあえず、
    「トランザクションにする。」
    としておきます。ただ、前にあった似たような別件では、「エラーの時にスキップ」したかったので、できれば「トランザクションにしない。」の場合も教えて頂ければありがたいです。
    よろしくお願いします。

    (…標題が明確でなかったため(?)に、議論がおかしな方向へ行ってしまった(「入口」で引っかかって、私の質問の回答に全くなっていない。)ので、スレッドを立て直ししました。元のスレッド…http://social.technet.microsoft.com/Forums/ja-JP/sqlserverja/thread/9c1b2f49-7ce6-416f-aacb-6b5a892c5f6c)

    2010年8月30日 4:24

回答

  • Access がなくても問題を起こせますね。

    T-SQL スクリプト

    USE master
    GO
    CREATE DATABASE DB1
    GO
    
    USE DB1
    GO
    
    CREATE TABLE T1(
    	[KEY] int NOT NULL, CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO
    
    CREATE PROCEDURE usp1 AS
    BEGIN
    	BEGIN TRAN
    		INSERT INTO T1 VALUES(1)
    		INSERT INTO T1 VALUES(1)
    	ROLLBACK TRAN
    END
    GO
    
    /*
    USE master
    GO
    ALTER DATABASE DB1 SET SINGLE_USER WITH ROLLBACK IMMEDIATE
    GO
    DROP DATABASE DB1
    GO
    */

    VBScript

    Option Explicit
    
    ' 接続文字列は適宜書き換えること
    Const cConnectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=DB1;Data Source=(local)"
    
    Dim cn, cmd
    Dim RecordsAffected
    
    Set cn = CreateObject("ADODB.Connection")
    Set cmd = CreateObject("ADODB.Command")
    
    cn.ConnectionString = cConnectionString
    cn.Open
    
    cmd.ActiveConnection = cn
    cmd.CommandType = 4	' adCmdStoredProc 
    cmd.CommandText = "usp1"
    
    cmd.Execute RecordsAffected
    
    T-SQL スクリプトを実行してスキーマを準備し、VBScript を実行すると問題が起きます。
    問題は「ストアドプロシージャを実行した時、データベースでキー重複エラーが起きているのに、VBScript ランタイム上でエラーにならない」ことで、期待する動作は「VBScript でエラーになること」です。

    問題としては ADO にカテゴライズされる問題だと思います。

    参考: http://support.sas.com/documentation/tools/oledb/app_error_objects.htm

    ↑のページをざっとみたところでは、ADO でコマンドを実行した時にはエラーが複数返されることがあるため、Connection.Errors コレクションの中身を見てエラーを判断すること、そして想像通り最初のコマンド実行が成功した時点でランタイムの Err オブジェクトには Err.Number = 0 がセットされてしまうのでランタイムからはエラーをハンドルできないようです。


    Please mark the thread as Answered when an answer resolves your problem. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう
    2010年8月31日 8:07
  • Access 2010 での話になりますが、SET NOCOUNT ON にしたらどうなりますか。つまり、前の私の投稿での usp1 を次のように記述します。

    USE DB1
    GO
    
    DROP PROCEDURE usp1
    GO
    
    CREATE PROCEDURE usp1 AS
    BEGIN
    	SET NOCOUNT ON
    	
    	BEGIN TRAN
    		INSERT INTO T1 VALUES(1)
    		INSERT INTO T1 VALUES(1)
    	ROLLBACK TRAN
    	
    	SET NOCOUNT OFF
    END
    GO
    
    


    Please mark the thread as Answered when an answer resolves your problem. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう
    2010年9月3日 2:26
  • 「行数」とはあなたの言葉で言えば「レコード件数」であり、"records affected" です。

    Books Online にも SET NOCOUNT の効果・意味を理解するためにサンプル データベースに対して実行できる SQL スクリプトが載っていますが、次のようなスクリプトを実行すれば理解できるのではないでしょうか。

    /*
    USE master
    GO
    ALTER DATABASE DB1 SET SINGLE_USER WITH ROLLBACK IMMEDIATE
    GO
    DROP DATABASE DB1
    GO
    */
    USE master
    GO
    CREATE DATABASE DB1
    GO
    USE DB1
    GO
    
    CREATE TABLE T1 (C1 int)
    GO
    
    PRINT '===== Run queries with NOCOUNT OFF (default) ====='
    INSERT INTO T1 VALUES(1)
    INSERT INTO T1 VALUES(2)
    INSERT INTO T1 VALUES(3)
    SELECT * FROM T1
    TRUNCATE TABLE T1
    
    PRINT '===== Run queries with NOCOUNT ON ====='
    SET NOCOUNT ON
    INSERT INTO T1 VALUES(1)
    INSERT INTO T1 VALUES(2)
    INSERT INTO T1 VALUES(3)
    SELECT * FROM T1
    TRUNCATE TABLE T1
    SET NOCOUNT OFF
    
    

     

    副作用はもちろんあります。Books Online に書かれているように、クライアントはクエリやストアド プロシージャを実行することにより影響を受けた行数を知ることができません。それを取得しようとすると常に -1 が返されます。


    Please mark the thread as Answered when an answer resolves your problem. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう
    2010年9月4日 1:11
  • …当方で、

    USE master
    GO
    CREATE DATABASE DB4
    GO

    USE DB4
    GO

    CREATE TABLE T1(
     [KEY] int NOT NULL, CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO

    CREATE TABLE T2(
     [KEY] int NOT NULL, CONSTRAINT PK_T2 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO


    CREATE TABLE T3(
     [KEY] int NOT NULL, CONSTRAINT PK_T3 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO


    CREATE TABLE T4(
     [KEY] int NOT NULL, CONSTRAINT PK_T4 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO


    CREATE TABLE T5(
     [KEY] int NOT NULL, CONSTRAINT PK_T5 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO


    CREATE PROCEDURE uspN0 AS
    BEGIN
     INSERT INTO T1 VALUES(1)
    END
    GO

    CREATE PROCEDURE uspN1 AS
    BEGIN
     SET NOCOUNT ON
     INSERT INTO T2([KEY])
      SELECT [KEY] FROM T1
     INSERT INTO T3([KEY])
      SELECT [KEY] FROM T1
     SET NOCOUNT OFF
    END
    GO

    CREATE PROCEDURE uspN2 AS
    BEGIN
     INSERT INTO T4([KEY])
      SELECT [KEY] FROM T1
    END
    GO

    CREATE PROCEDURE uspN3 AS
    BEGIN
     INSERT INTO T5([KEY])
      SELECT [KEY] FROM T1
    END
    GO

    CREATE PROCEDURE uspR AS
    BEGIN
     DECLARE @iErr int
     SET @iErr = 0

     SET NOCOUNT ON
     
     EXEC @iErr = uspN1
     
     IF @iErr <> 0
      GOTO FINISH
     
     EXEC @iErr = uspN2
     
     IF @iErr <> 0
      GOTO FINISH
     
     EXEC @iErr = uspN3
     
     FINISH:
     SET NOCOUNT OFF
     RETURN @iErr
    END

    を行い、(以下、全てデータベースウィンドウで、)
    ①uspN0 で初期データを設定し、
    ②uspR で一旦全テーブルにデータをコピーし、
    ③T2 のレコードを削除し、uspR を行うと、(もちろん)「PRIMARY KEY 違反、制約 'PK_T3': オブジェクト 'T3' には重複したキーは挿入できません。」となります。
    ④ここでT2、T3、T4 のレコードを削除し、uspR を行うと…「PRIMARY KEY 違反、制約 'PK_T5': オブジェクト 'T5' には重複したキーは挿入できません。」となりました。
    →「SET NOCOUNT がネストに対応している。」ようですね。

    2010年9月7日 4:23

すべての返信

  • まず、無料で調べられる範囲は質問者さんも回答者さんもあまり変わらないため、調べた範囲を書いておくとよろしいかと。

    また、どのようなストアドを書いていますか?
    普通に実行してもまず再現しないとおもいます。
    実際、Access 2003と SQL Server 2000の組み合わせでは、エラーが出ます。
    #イルカさんが以下のメッセージを吐きます

    Microsoft Offiecc Access
    PRIMARY KEY 違反、制約 'PK_TestUsers2': オブジェクト 'TestUsers2' には重複したキーは挿入できません。

    質問を立て直しても、堂々巡りしそうな予感です。
    ともかく再現させないと何とも言えないです。

    単に、ストアドプロシージャ側で例外を抑制する方法がまずいのか、TRANSACTIONの切り方がまずいのか、Try Catchがまずいだけの様な気もしないくはないです。
    環境に依存する問題である可能性を考えるなら、SQL Serverの設定なども必要かもしれません。

     

    2010年8月30日 6:55
  • Chukiさん、返信ありがとうございます。
    回答の前段のお話のようですが、(うまく表現できませんが、)いい方向へ持って行こう(=解決に導く)という御意見のようで、ありがたく思います。
    (ただ、私がそれに対して「返信」してしまうと、その方向で話が進んでしまうようですが…。(それでも御返事致します。))

    1.
    >また、どのようなストアドを書いていますか?

    …スレッド
    http://social.technet.microsoft.com/Forums/ja-JP/sqlserverja/thread/9c1b2f49-7ce6-416f-aacb-6b5a892c5f6c
    の、
    システムエンジニア(つまり私。)の、2010年8月27日 2:59 (…GMT?)
    にある、T-SQL を、議論のたたき台としてよいかと思います。
    (但し、当初実際に発生したものを当てはめると、uspNested1、uspNested2 で、「RETURN 0」は、行っていません。←デザインビューで作成しました。)

    2.
    ところで、
    >Microsoft Offiecc Access
    >PRIMARY KEY 違反、制約 'PK_TestUsers2': オブジェクト 'TestUsers2' には重複したキーは挿入できません。

    と書いておいでですが、これは、実際に何らかの環境を作成して、実機で出たメッセージなのでしょうか?
    それとも、「たぶんこんなメッセージが出るのだろう。」ということで、手で打ち込んだメッセージなのでしょうか?

    3.
    また、
    >Try Catchがまずいだけの様な気もしないくはないです。

    とも書いておいでですが、Try Catch は、2005以降で実装されたと思いますが…。

    4.
    で、
    >まず、無料で調べられる範囲は質問者さんも回答者さんもあまり変わらないため、調べた範囲を書いておくとよろしいかと。

    ですが、
    ①Access2003のadpで、当初実際に発生したものでは、
     command オブジェクトの、execute メソッド実行で、@return_value に-4 が返るところまでは確認しております。
     (それでも、エラー処理ルーチンへは遷移しません。)

    ②当初実際に発生したストアドプロシージャで、上記「1.」のスレッドの例に当てはめると、uspNested2 に該当するところに、
     begin の直後に、 declare @IntErrorCode int

     end の直前に、  select @IntErrorCode=@@ERROR
        return @IntErrorCode
     を追加すると、command オブジェクトの、execute メソッド実行で、@return_value に 2627 が返るところまでは確認しております。
     (それでも、エラー処理ルーチンへは遷移しません。)

    …上記「1.」のスレッドの例に当てはめて、 T2 のテーブルを削除せずに、command オブジェクトの、execute メソッド実行で、uspRoot を起動すると、もちろんエラー処理ルーチンに遷移し、
    「PRIMARY KEY 違反、制約 'PK_T2': オブジェクト 'T2' には重複したキーは挿入できません。」
    というエラーメッセージが取得できます。

    (要件としては、adp のcommand オブジェクトの、execute メソッド実行で、uspRoot を起動し、T2へのコピーが正常、T3へのコピーが(キー重複などの)エラーの時に、エラー処理ルーチンへ遷移し、エラーコード、エラーメッセージの取得を行いたいのです。←最初の投稿に、こうは書いていませんでした…。)

    …こういった返信で、よろしいでしょうか?

    2010年8月30日 8:16
  • >(ただ、私がそれに対して「返信」してしまうと、その方向で話が進んでしまうようですが…。(それでも御返事致します。))

    質問者さんの返信を前提にしないと回答しようがないため、返信は必要と存じます。

    > と書いておいでですが、これは、実際に何らかの環境を作成して、実機で出たメッセージなのでしょうか?
    >それとも、「たぶんこんなメッセージが出るのだろう。」ということで、手で打ち込んだメッセージなのでしょうか?

    上記で、Access 2003とSQL Server 2000でエラーが出ると書いている通りです。
    ただ、Copy & Pasteしたものではないため、手打ちです。

    残念ながら、同じエラーを再現できる環境を整えることができませんでした、申し訳ございません

    2010年8月30日 11:15
  • それでは、私の再現環境を提起します。

    サーバー側は、スレッド
    http://social.technet.microsoft.com/Forums/ja-JP/sqlserverja/thread/9c1b2f49-7ce6-416f-aacb-6b5a892c5f6c
    の、
    システムエンジニア(つまり私。)の、2010年8月27日 2:59 (…GMT?)
    にある、T-SQL で良いと思います。(今の所こちらでは、uspNested1、uspNested2 で、「RETURN 0」があってもなくても再現します。)
    (再掲した方が良いと思われる方がおられましたら、お伝え下さい。)

    クライアント側は、Access(出来れば2003…既定のファイル形式は、Access2000です。)のadpで、フォームと、その上にコマンドボタン(名前…cmd_exec)を作成し、そのコマンドボタンの「クリック時」のイベントプロシージャで、(一部宣言セクションも表記してあります。)

    Option Compare Database
    Option Explicit

    Private Sub cmd_exec_Click()
    On Error GoTo Err_cmd_exec_Click

        Dim cm As New ADODB.Command
        Dim RecordsAffected As Long
       
        cm.ActiveConnection = CurrentProject.Connection
        cm.CommandType = adCmdStoredProc
        cm.CommandText = "uspRoot"
       
        cm.Execute RecordsAffected
       
        MsgBox "正常終了しました。"
       
    Exit_cmd_exec_Click:

        Exit Sub
       
    Err_cmd_exec_Click:
       
        MsgBox Err.Number & vbCrLf & Err.Description
        Resume Exit_cmd_exec_Click
    End Sub

    と記述します。(このストアドプロシージャならば、connectionオブジェクトでも実行できますが、実際の環境ではパラメータを設定しておりますので、ここでもcommandオブジェクトにしました。もちろん、commandオブジェクトでも再現します。)

    そして、uspNested0 を起動させて、T1 に初期データを設定した後、フォームのcmd_execボタンをクリックします。
    (この時点では、「正常終了しました。」と表示されます。)
    次に、T2 の全(と言っても1つですが…。)レコードを削除します。

    そして、ここからが本題ですが、ここで、フォームのcmd_execボタンをクリックすると、T3 で重複エラーとなるのに、
     「正常終了しました。」
    と表示されます。
    (参考までに、もう一度フォームのcmd_execボタンをクリックすると、「本題」でT2 が追加されているので、
     「-2147217873
      PRIMARY KEY 違反、制約 'PK_T2': オブジェクト 'T2' には重複したキーは挿入できません。」
     と表示されます。←「本題」の時に、T3 に対して、このようなメッセージが表示されてほしいのです。)

    2010年8月31日 1:00

  • http://msdn.microsoft.com/ja-jp/library/cc364152.aspx
    にある、エラー処理を参考に、先程のVBAイベントプロシージャを、

    Option Compare Database
    Option Explicit

    Private Sub cmd_exec_Click()
    On Error GoTo Err_cmd_exec_Click

        Dim cm As New ADODB.Command
        Dim RecordsAffected As Long
      
        Dim Err As ADODB.Error                                  '追加
       
        cm.ActiveConnection = CurrentProject.Connection
        cm.CommandType = adCmdStoredProc
        cm.CommandText = "uspRoot"
       
        cm.Execute RecordsAffected
       
        If cm.ActiveConnection.Errors.Count > 0 Then            '追加
            For Each Err In cm.ActiveConnection.Errors          '追加
             MsgBox "Error number: " & Err.Number & vbCr & _
                Err.Description                                 '追加
            Next Err                                            '追加
        End If                                                  '追加
       
    '    MsgBox "正常終了しました。"                            削除
       
    Exit_cmd_exec_Click:

        Exit Sub
       
    Err_cmd_exec_Click:
       
        For Each Err In cm.ActiveConnection.Errors              '追加
         MsgBox "Error number: " & Err.Number & vbCr & _
                Err.Description                                 '追加
        Next Err                                                '追加
    '    MsgBox Err.Number & vbCrLf & Err.Description           削除
        Resume Exit_cmd_exec_Click
    End Sub

    とすると、T3 の時のエラーも(cm.Execute RecordsAffected のすぐ下の処理で)取得することができました。
    とりあえず、この方式を元に、カスタマイズして実装していこうと思います。
    M_Lewis さん、Chuki さん、いろいろお手数をお掛けいたしました。

    ただ、何で T3 の時のエラーは、エラー処理ルーチンへ飛ばないのか?これは私には未だもって不明です。
    御存じの方、教えて頂ければ幸いです。

    2010年8月31日 6:17
  • Access がなくても問題を起こせますね。

    T-SQL スクリプト

    USE master
    GO
    CREATE DATABASE DB1
    GO
    
    USE DB1
    GO
    
    CREATE TABLE T1(
    	[KEY] int NOT NULL, CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO
    
    CREATE PROCEDURE usp1 AS
    BEGIN
    	BEGIN TRAN
    		INSERT INTO T1 VALUES(1)
    		INSERT INTO T1 VALUES(1)
    	ROLLBACK TRAN
    END
    GO
    
    /*
    USE master
    GO
    ALTER DATABASE DB1 SET SINGLE_USER WITH ROLLBACK IMMEDIATE
    GO
    DROP DATABASE DB1
    GO
    */

    VBScript

    Option Explicit
    
    ' 接続文字列は適宜書き換えること
    Const cConnectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=DB1;Data Source=(local)"
    
    Dim cn, cmd
    Dim RecordsAffected
    
    Set cn = CreateObject("ADODB.Connection")
    Set cmd = CreateObject("ADODB.Command")
    
    cn.ConnectionString = cConnectionString
    cn.Open
    
    cmd.ActiveConnection = cn
    cmd.CommandType = 4	' adCmdStoredProc 
    cmd.CommandText = "usp1"
    
    cmd.Execute RecordsAffected
    
    T-SQL スクリプトを実行してスキーマを準備し、VBScript を実行すると問題が起きます。
    問題は「ストアドプロシージャを実行した時、データベースでキー重複エラーが起きているのに、VBScript ランタイム上でエラーにならない」ことで、期待する動作は「VBScript でエラーになること」です。

    問題としては ADO にカテゴライズされる問題だと思います。

    参考: http://support.sas.com/documentation/tools/oledb/app_error_objects.htm

    ↑のページをざっとみたところでは、ADO でコマンドを実行した時にはエラーが複数返されることがあるため、Connection.Errors コレクションの中身を見てエラーを判断すること、そして想像通り最初のコマンド実行が成功した時点でランタイムの Err オブジェクトには Err.Number = 0 がセットされてしまうのでランタイムからはエラーをハンドルできないようです。


    Please mark the thread as Answered when an answer resolves your problem. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう
    2010年8月31日 8:07
  • …ここでM_Lewis さんからのこんなすごい返信があるなんて!
    感謝感激です。

    >そして想像通り最初のコマンド実行が成功した時点でランタイムの Err オブジェクトには Err.Number = 0 がセットされてしまうのでランタイムからはエラーをハンドルできないようです。

    結論としては、こんなところなのでしょうね。
    了解致しました。
    (でも、何でこんな海外の、「SAS」なんていうのを御存知?(私が知らないだけ?)…再現環境構築のT-SQLが、全て半角文字だったので、「海外と直接やり取りしている人なのだろうなぁ~。」とは思っていました。)

    いろいろありがとうございました。

    2010年8月31日 8:48
  • …対処方のVBAイベントプロシージャを整理しました。
    どなたかのお役にたてれば幸いです。
    (追加、削除は当初のものとの差分を示します。)

    Option Compare Database
    Option Explicit

    Private Sub cmd_exec_Click()
    On Error GoTo Err_cmd_exec_Click

        Dim cm As New ADODB.Command
        Dim RecordsAffected As Long
          
        cm.ActiveConnection = CurrentProject.Connection
        cm.CommandType = adCmdStoredProc
        cm.CommandText = "uspRoot"
       
        cm.Execute RecordsAffected
       
        If cm.ActiveConnection.Errors.Count > 0 Then            '追加
            With cm.ActiveConnection.Errors(0)                  '追加
                Call err.Raise(.Number, .Source, .Description, .HelpFile, .HelpContext)              '追加
            End With                                            '追加
        End If                                                  '追加
        
        MsgBox "正常終了しました。"
       
    Exit_cmd_exec_Click:

        Exit Sub
       
    Err_cmd_exec_Click:
       
        MsgBox err.Number & vbCrLf & err.Description
        Resume Exit_cmd_exec_Click
    End Sub


    なお、当初、
    >(雰囲気としては、常に1件目(この場合は子ストアドプロシージャ1)の結果を返しているのかな?という感じです。)
    と気づいたのは、以前あったこれに似たような件で、(RecordsAffected で)レコード件数を取得しようとした時に、その時の要件では、再現環境作成 T-SQL で言う、uspNested1 と uspNested2 の合計が欲しかったのですが、どうも uspNested1 の結果を返しているようで、その時は仕方なくストアドの中で、@@ROWCOUNT を合計する処理を組んだのですが、今回のケースでは、「MSDE2000」なので、try-catch が使えず、エラーメッセージがストアド内で取得できなかったので、当フォーラムに投稿した次第であります。(どのテーブルでエラーが出たかによって処理が変わる要件です。)

    2010年9月1日 2:22
  • …その後、検討致しましたところ、とりあえず私が開発しているシステムの(運用後の、ユーザーから見た)業務処理のストアドは、全部VBAで起動することにして、この問題の対処をするつもりですが、
    将来そのシステムが運用に入った後、障害の調査などで、ついうっかりデータベースウィンドウからストアドを直接起動しかねません。
    その場合、本来出るべきエラーが表示されないので、大変危険に感じています。

    何か、対処方法はないでしょうか?
    (当方にある、Access2010β版でも、再現しました。(エラーが出ない。)…このような処理系で残している何らかの理由があるのでしょうか?(複数のエラーが出る可能性があるから?))

    2010年9月3日 1:24
  • Access 2010 での話になりますが、SET NOCOUNT ON にしたらどうなりますか。つまり、前の私の投稿での usp1 を次のように記述します。

    USE DB1
    GO
    
    DROP PROCEDURE usp1
    GO
    
    CREATE PROCEDURE usp1 AS
    BEGIN
    	SET NOCOUNT ON
    	
    	BEGIN TRAN
    		INSERT INTO T1 VALUES(1)
    		INSERT INTO T1 VALUES(1)
    	ROLLBACK TRAN
    	
    	SET NOCOUNT OFF
    END
    GO
    
    


    Please mark the thread as Answered when an answer resolves your problem. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう
    2010年9月3日 2:26
  • M_Lewis さん、度々の返信、ありがとうございます。

    さて、SET NOCOUNT のON/OFF を入れた結果、2つ前の私の投稿(システムエンジニア 2010年9月1日 2:22)で、

        If cm.ActiveConnection.Errors.Count > 0 Then            '追加
            With cm.ActiveConnection.Errors(0)                  '追加
                Call err.Raise(.Number, .Source, .Description, .HelpFile, .HelpContext)              '追加
            End With                                            '追加
        End If                                                  '追加

    を外して、usp1 で実行させたところ、(Access2003でです。)
     「-2147217873
      PRIMARY KEY 違反、制約 'PK_T1': オブジェクト 'T1' には重複したキーは挿入できません。」
    となりました。
    (usp1 で、SET NOCOUNT のON/OFF を外すと、「正常終了しました。」となりました。)

    一見OKそうなのですが、この、「SET NOCOUNT」って、何なのでしょうか?
    (以前、やはり2つ前の私の投稿で言う、「以前あったこれに似たような件で、(RecordsAffected で)レコード件数を取得しようとした時」に、
    http://msdn.microsoft.com/ja-jp/library/ms189837.aspx
    を見ましたが、「『行数』が何を意味しているのかがよくわからなかった。」で止まっていました。←未だにそうです。
    …どうもマイクロソフトの文章は、正確を期するためなのか、素人(私は素人ではないつもりですが…。)には、わかりにくい文章が多いように思います。)

    「副作用」がなければ、こちらの方式も検討したいのですが…。

    2010年9月3日 8:18
  • 追伸です。
    「以前あったこれに似たような件で、(RecordsAffected で)レコード件数を取得しようとした時」には、
    http://www.accessmonster.com/uwe/forum.aspx/access-adp-sqlserver/125/rowcount-recordsaffected
    で、SET NOCOUNT にたどり着きました。

     

    2010年9月3日 8:36
  • 「行数」とはあなたの言葉で言えば「レコード件数」であり、"records affected" です。

    Books Online にも SET NOCOUNT の効果・意味を理解するためにサンプル データベースに対して実行できる SQL スクリプトが載っていますが、次のようなスクリプトを実行すれば理解できるのではないでしょうか。

    /*
    USE master
    GO
    ALTER DATABASE DB1 SET SINGLE_USER WITH ROLLBACK IMMEDIATE
    GO
    DROP DATABASE DB1
    GO
    */
    USE master
    GO
    CREATE DATABASE DB1
    GO
    USE DB1
    GO
    
    CREATE TABLE T1 (C1 int)
    GO
    
    PRINT '===== Run queries with NOCOUNT OFF (default) ====='
    INSERT INTO T1 VALUES(1)
    INSERT INTO T1 VALUES(2)
    INSERT INTO T1 VALUES(3)
    SELECT * FROM T1
    TRUNCATE TABLE T1
    
    PRINT '===== Run queries with NOCOUNT ON ====='
    SET NOCOUNT ON
    INSERT INTO T1 VALUES(1)
    INSERT INTO T1 VALUES(2)
    INSERT INTO T1 VALUES(3)
    SELECT * FROM T1
    TRUNCATE TABLE T1
    SET NOCOUNT OFF
    
    

     

    副作用はもちろんあります。Books Online に書かれているように、クライアントはクエリやストアド プロシージャを実行することにより影響を受けた行数を知ることができません。それを取得しようとすると常に -1 が返されます。


    Please mark the thread as Answered when an answer resolves your problem. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう
    2010年9月4日 1:11
  • M_Lewis さん、手取り足取り教えて頂いて、感謝にたえません。

    さて、SET NOCOUNT の、M_Lewis さんの例、MSDEの例を共に実機で確認しました。
    なるほど、ON にすると、「(○ 件処理されました)」(○は数値)が表示されませんね。
    また、VBAから、

    ALTER PROCEDURE usp1 AS
    BEGIN
     SET NOCOUNT ON 
     
     BEGIN TRAN
      INSERT INTO T1 VALUES(1)
      INSERT INTO T1 VALUES(2)
     COMMIT TRAN
     
     SET NOCOUNT OFF 
    END

    を実行し、RecordsAffected を確認したところ、確かに-1となりました。

    当方の開発しているシステムでは、RecordsAffected を拾っているところもありますので、ストアドに無条件にSET NOCOUNT のON/OFF を入れるわけにもいかないようです。

    ストアドのExecute権限をDenyにすれば、データベースウィンドウに表示されないので、そこから起動することはできないとも考えましたが、それだとVBAからも起動できないので、本末転倒ですし…。

    2010年9月6日 2:56
  • ちなみに、
    http://technet.microsoft.com/ja-jp/library/cc748720.aspx
    の15.4.10 の後半に、「行数」に関する説明がありました。
    (この説明なら、(既にM_Lewis さんに説明してもらってある程度理解した後ですが、)
    http://msdn.microsoft.com/ja-jp/library/ms189837.aspx
    の意味がわかります。)

    2010年9月6日 4:24
  • …結局のところ、私の開発しているシステムでは、
      RecordsAffected を取得するストアド
    と、
      複数のステートメントがあるストアド
    は、背反のようですので(そうですよね。「複数のステートメントがあるストアド」のRecordsAffected は、1つ目のストアドのが返されるようですので。)、後者に対して、SET NOCOUNT のON/OFF を入れるのかなと考えています。

    でも、何で、SET NOCOUNT をON にすると、2つ目のストアドのエラーメッセージが出るようになるのでしょうか?
    (エラーメッセージは、DONE_IN_PROC メッセージ上に乗ってるのではないのでしょうか?)

    …adpは、最初に返されるメッセージだけを表示するのですかね。(議論が堂々巡りのような気も…。)
     (SET NOCOUNT がOFF(デフォルト)の時、
       ①1つ目のストアドのOKのDONE_IN_PROC メッセージが返る。
       ②2つ目のストアドのエラーの何とかメッセージが返る。
      で、ON にすると、①が消えて、②だけが返る?)

     

    2010年9月6日 5:05
  • …ところで、このSET NOCOUNT って、ネストに対応しているのでしょうか?

    具体的には、スレッド
    http://social.technet.microsoft.com/Forums/ja-JP/sqlserverja/thread/9c1b2f49-7ce6-416f-aacb-6b5a892c5f6c
    の、システムエンジニア 2010年8月27日 2:59 にある、T-SQL で、さらに uspNested1 で、複数のステートメントがあったとします。
    (「そして、もう一つ、uspRoot から、3番目に、単一ステートメントのuspNested3 を起動するとします。」←この条件を入れないと問題が成立しない?)
    そして、uspRoot、uspNested1の先頭と末尾に、それぞれ SET NOCOUNT ON、SET NOCOUNT OFF を設定するとすると、
    ①SET NOCOUNT がネストに対応している。→これでよい。
    ②SET NOCOUNT がネストに対応していない。→uspNested1 の末尾でSET NOCOUNT OFF されてしまうので、uspRootで、uspNested1 をexec した後に、再度SET NOCOUNT ON を設定し直さないといけない。

    いかがでしょうか?

    2010年9月7日 0:52
  • …当方で、

    USE master
    GO
    CREATE DATABASE DB4
    GO

    USE DB4
    GO

    CREATE TABLE T1(
     [KEY] int NOT NULL, CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO

    CREATE TABLE T2(
     [KEY] int NOT NULL, CONSTRAINT PK_T2 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO


    CREATE TABLE T3(
     [KEY] int NOT NULL, CONSTRAINT PK_T3 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO


    CREATE TABLE T4(
     [KEY] int NOT NULL, CONSTRAINT PK_T4 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO


    CREATE TABLE T5(
     [KEY] int NOT NULL, CONSTRAINT PK_T5 PRIMARY KEY CLUSTERED([KEY] ASC)
    )
    GO


    CREATE PROCEDURE uspN0 AS
    BEGIN
     INSERT INTO T1 VALUES(1)
    END
    GO

    CREATE PROCEDURE uspN1 AS
    BEGIN
     SET NOCOUNT ON
     INSERT INTO T2([KEY])
      SELECT [KEY] FROM T1
     INSERT INTO T3([KEY])
      SELECT [KEY] FROM T1
     SET NOCOUNT OFF
    END
    GO

    CREATE PROCEDURE uspN2 AS
    BEGIN
     INSERT INTO T4([KEY])
      SELECT [KEY] FROM T1
    END
    GO

    CREATE PROCEDURE uspN3 AS
    BEGIN
     INSERT INTO T5([KEY])
      SELECT [KEY] FROM T1
    END
    GO

    CREATE PROCEDURE uspR AS
    BEGIN
     DECLARE @iErr int
     SET @iErr = 0

     SET NOCOUNT ON
     
     EXEC @iErr = uspN1
     
     IF @iErr <> 0
      GOTO FINISH
     
     EXEC @iErr = uspN2
     
     IF @iErr <> 0
      GOTO FINISH
     
     EXEC @iErr = uspN3
     
     FINISH:
     SET NOCOUNT OFF
     RETURN @iErr
    END

    を行い、(以下、全てデータベースウィンドウで、)
    ①uspN0 で初期データを設定し、
    ②uspR で一旦全テーブルにデータをコピーし、
    ③T2 のレコードを削除し、uspR を行うと、(もちろん)「PRIMARY KEY 違反、制約 'PK_T3': オブジェクト 'T3' には重複したキーは挿入できません。」となります。
    ④ここでT2、T3、T4 のレコードを削除し、uspR を行うと…「PRIMARY KEY 違反、制約 'PK_T5': オブジェクト 'T5' には重複したキーは挿入できません。」となりました。
    →「SET NOCOUNT がネストに対応している。」ようですね。

    2010年9月7日 4:23
  • …前述のT-SQLスクリプトは、慌てて作ったため、(特にuspN1 の)考慮が甘かったですね。
    試しに、
    ⑤T3 のレコードを削除し、uspR を行うと、(もちろん)「PRIMARY KEY 違反、制約 'PK_T2': オブジェクト 'T2' には重複したキーは挿入できません。」となりますが、T3 を見ると、データがコピーされています。
    実際には、uspN1 に、uspR のようなエラー処理を入れたり、トランザクションにしたりするのでしょうね。

    失礼致しました。

    2010年9月7日 4:54