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

質問
-
Access2003のadpファイルから、MSDE2000へ接続するシステムを開発しております。
ここで、以下のように、子ストアドプロシージャを2つ起動するストアドプロシージャがあるとします。
CREATE PROCEDURE [親ストアドプロシージャ]
AS
BEGIN
declare @IntErrorCode intexecute @IntErrorCode=子ストアドプロシージャ1
if @IntErrorCode<>0
return @IntErrorCodeexecute @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年9月13日 0:58
回答
-
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. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう -
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. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう -
「行数」とはあなたの言葉で言えば「レコード件数」であり、"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. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう -
…当方で、
USE master
GO
CREATE DATABASE DB4
GOUSE DB4
GOCREATE TABLE T1(
[KEY] int NOT NULL, CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED([KEY] ASC)
)
GOCREATE 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
GOCREATE 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
GOCREATE PROCEDURE uspN2 AS
BEGIN
INSERT INTO T4([KEY])
SELECT [KEY] FROM T1
END
GOCREATE PROCEDURE uspN3 AS
BEGIN
INSERT INTO T5([KEY])
SELECT [KEY] FROM T1
END
GOCREATE PROCEDURE uspR AS
BEGIN
DECLARE @iErr int
SET @iErr = 0SET 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:58
すべての返信
-
まず、無料で調べられる範囲は質問者さんも回答者さんもあまり変わらないため、調べた範囲を書いておくとよろしいかと。
また、どのようなストアドを書いていますか?
普通に実行してもまず再現しないとおもいます。
実際、Access 2003と SQL Server 2000の組み合わせでは、エラーが出ます。
#イルカさんが以下のメッセージを吐きますMicrosoft Offiecc Access
PRIMARY KEY 違反、制約 'PK_TestUsers2': オブジェクト 'TestUsers2' には重複したキーは挿入できません。質問を立て直しても、堂々巡りしそうな予感です。
ともかく再現させないと何とも言えないです。単に、ストアドプロシージャ側で例外を抑制する方法がまずいのか、TRANSACTIONの切り方がまずいのか、Try Catchがまずいだけの様な気もしないくはないです。
環境に依存する問題である可能性を考えるなら、SQL Serverの設定なども必要かもしれません。 -
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 intend の直前に、 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へのコピーが(キー重複などの)エラーの時に、エラー処理ルーチンへ遷移し、エラーコード、エラーメッセージの取得を行いたいのです。←最初の投稿に、こうは書いていませんでした…。)
…こういった返信で、よろしいでしょうか?
-
>(ただ、私がそれに対して「返信」してしまうと、その方向で話が進んでしまうようですが…。(それでも御返事致します。))
質問者さんの返信を前提にしないと回答しようがないため、返信は必要と存じます。
> と書いておいでですが、これは、実際に何らかの環境を作成して、実機で出たメッセージなのでしょうか?
>それとも、「たぶんこんなメッセージが出るのだろう。」ということで、手で打ち込んだメッセージなのでしょうか?上記で、Access 2003とSQL Server 2000でエラーが出ると書いている通りです。
ただ、Copy & Pasteしたものではないため、手打ちです。残念ながら、同じエラーを再現できる環境を整えることができませんでした、申し訳ございません
-
それでは、私の再現環境を提起します。
サーバー側は、スレッド
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 ExplicitPrivate Sub cmd_exec_Click()
On Error GoTo Err_cmd_exec_ClickDim 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 に対して、このようなメッセージが表示されてほしいのです。) -
…
http://msdn.microsoft.com/ja-jp/library/cc364152.aspx
にある、エラー処理を参考に、先程のVBAイベントプロシージャを、Option Compare Database
Option ExplicitPrivate Sub cmd_exec_Click()
On Error GoTo Err_cmd_exec_ClickDim 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 の時のエラーは、エラー処理ルーチンへ飛ばないのか?これは私には未だもって不明です。
御存じの方、教えて頂ければ幸いです。 -
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. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう -
…ここでM_Lewis さんからのこんなすごい返信があるなんて!
感謝感激です。>そして想像通り最初のコマンド実行が成功した時点でランタイムの Err オブジェクトには Err.Number = 0 がセットされてしまうのでランタイムからはエラーをハンドルできないようです。
結論としては、こんなところなのでしょうね。
了解致しました。
(でも、何でこんな海外の、「SAS」なんていうのを御存知?(私が知らないだけ?)…再現環境構築のT-SQLが、全て半角文字だったので、「海外と直接やり取りしている人なのだろうなぁ~。」とは思っていました。)いろいろありがとうございました。
-
…対処方のVBAイベントプロシージャを整理しました。
どなたかのお役にたてれば幸いです。
(追加、削除は当初のものとの差分を示します。)Option Compare Database
Option ExplicitPrivate Sub cmd_exec_Click()
On Error GoTo Err_cmd_exec_ClickDim 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 が使えず、エラーメッセージがストアド内で取得できなかったので、当フォーラムに投稿した次第であります。(どのテーブルでエラーが出たかによって処理が変わる要件です。) -
…その後、検討致しましたところ、とりあえず私が開発しているシステムの(運用後の、ユーザーから見た)業務処理のストアドは、全部VBAで起動することにして、この問題の対処をするつもりですが、
将来そのシステムが運用に入った後、障害の調査などで、ついうっかりデータベースウィンドウからストアドを直接起動しかねません。
その場合、本来出るべきエラーが表示されないので、大変危険に感じています。何か、対処方法はないでしょうか?
(当方にある、Access2010β版でも、再現しました。(エラーが出ない。)…このような処理系で残している何らかの理由があるのでしょうか?(複数のエラーが出る可能性があるから?)) -
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. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう -
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
を見ましたが、「『行数』が何を意味しているのかがよくわからなかった。」で止まっていました。←未だにそうです。
…どうもマイクロソフトの文章は、正確を期するためなのか、素人(私は素人ではないつもりですが…。)には、わかりにくい文章が多いように思います。)「副作用」がなければ、こちらの方式も検討したいのですが…。
-
追伸です。
「以前あったこれに似たような件で、(RecordsAffected で)レコード件数を取得しようとした時」には、
http://www.accessmonster.com/uwe/forum.aspx/access-adp-sqlserver/125/rowcount-recordsaffected
で、SET NOCOUNT にたどり着きました。 -
「行数」とはあなたの言葉で言えば「レコード件数」であり、"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. 回答が得られたら、もらった回答の [回答済み] ボタンをクリックしてスレッドを回答済みにしましょう -
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からも起動できないので、本末転倒ですし…。
-
ちなみに、
http://technet.microsoft.com/ja-jp/library/cc748720.aspx
の15.4.10 の後半に、「行数」に関する説明がありました。
(この説明なら、(既にM_Lewis さんに説明してもらってある程度理解した後ですが、)
http://msdn.microsoft.com/ja-jp/library/ms189837.aspx
の意味がわかります。) -
…結局のところ、私の開発しているシステムでは、
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 にすると、①が消えて、②だけが返る?) -
…ところで、この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 を設定し直さないといけない。いかがでしょうか?
-
…当方で、
USE master
GO
CREATE DATABASE DB4
GOUSE DB4
GOCREATE TABLE T1(
[KEY] int NOT NULL, CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED([KEY] ASC)
)
GOCREATE 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
GOCREATE 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
GOCREATE PROCEDURE uspN2 AS
BEGIN
INSERT INTO T4([KEY])
SELECT [KEY] FROM T1
END
GOCREATE PROCEDURE uspN3 AS
BEGIN
INSERT INTO T5([KEY])
SELECT [KEY] FROM T1
END
GOCREATE PROCEDURE uspR AS
BEGIN
DECLARE @iErr int
SET @iErr = 0SET 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:58