前提・実現したいこと
Access VBA からSQL Serverの[M_取引先マスタ]にテーブルロックを掛けて、フォームに入力があった内容を新規追加したく思っています。
発生している問題・エラーメッセージ
下記ロジックを実行すると、2つ目のSQL文実行時(M_取引先マスタのロックSQL)に
【実行時エラー '3705' オブジェクトが開いている場合は、操作は許可されません。】
とエラーになります。
SELECT COUNTのSQL文をコメントアウトすると、INSERT文の行で同エラーになります。
別ロジックで[DELETE]-->[INSERT]している部分では問題無く動作しているため、コピペするかたちで作ったのですが、SQL文実行の都度クローズしなくてはいけないのでしょうか?
該当のソースコード
Public CN_SQL As New ADODB.Connection
Public RS_SQL As New ADODB.Recordset
' -----------------------------------------------------------------------------
' Connectionオブジェクトを生成
' -----------------------------------------------------------------------------
Public Sub DB_Connect()
Dim ConnectionString As String
Dim sDBSever As String
Dim sDBName As String
Dim sLoginID As String
Dim sPassWD As String
sDBSever = DLookup("[データ]", "M_システム", "[ID] = 'SvName'") ' SQL Serverが稼働しているサーバー名
sDBName = DLookup("[データ]", "M_システム", "[ID] = 'DbName'") ' SQL ServerのDB名
sLoginID = DLookup("[データ]", "M_システム", "[ID] = 'Login'") ' sa
sPassWD = DLookup("[データ]", "M_システム", "[ID] = 'Pass'")' saのパスワード
' 接続文字列
ConnectionString = "Provider=SQLOLEDB;Data Source=" & sDBSever & _
";Initial Catalog=" & sDBName & _
";Connect Timeout=15" & _
";user id=" & sLoginID & _
";password=" & sPassWD
' 接続
CN_SQL.Open ConnectionString
End Sub
' -----------------------------------------------------------------------------
' データベースへの接続を解除する
' -----------------------------------------------------------------------------
Public Sub DB_DISCONNECT()
CN_SQL.Close
Set CN_SQL = Nothing
End Sub
' -----------------------------------------------------------------------------
' 引数のSQL文を実行し、ADODB.Recordsetを返す
' -----------------------------------------------------------------------------
Public Function DB_EXECUTE(s_SQL As String, CursorType, LockType, b_FLG As Boolean) As ADODB.Recordset
b_FLG = True
' タイムアウト設定 (15分)
CN_SQL.CommandTimeout = 60 * 15
' 処理された行数を示すメッセージが結果セットの一部として返されないようにする
CN_SQL.execute ("SET NOCOUNT ON")
' 警告メッセージが結果セットの一部として返されないようにする
CN_SQL.execute ("SET ANSI_WARNINGS OFF")
' オーバーフローおよび0除算時にはNULLを返す
CN_SQL.execute ("SET ARITHABORT OFF")
RS_SQL.CursorType = CursorType
RS_SQL.LockType = LockType
RS_SQL.Open s_SQL, CN_SQL
'RS_SQL.Open s_SQL, CN_SQL, CursorType, LockType
Do
' レコードの操作ができるオブジェクト若しくは次のRecordSetがとれず、コネクションが空になった場合終了
If RS_SQL.State = adStateOpen Or RS_SQL.ActiveConnection Is Nothing Then
Exit Do
End If
Set RS_SQL = RS_SQL.NextRecordset()
Loop
Set DB_EXECUTE = RS_SQL
' 設定OFF
CN_SQL.execute ("SET NOCOUNT OFF")
CN_SQL.execute ("SET ANSI_WARNINGS ON")
CN_SQL.execute ("SET ARITHABORT ON")
End Function
' -----------------------------------------------------------------------------
' トランザクションを開始する
' -----------------------------------------------------------------------------
Public Sub BeginTransaction()
CN_SQL.BeginTrans
End Sub
' -----------------------------------------------------------------------------
' トランザクションをコミットする
' -----------------------------------------------------------------------------
Public Sub CommitTransaction()
CN_SQL.CommitTrans
End Sub
' -----------------------------------------------------------------------------
' トランザクションをロールバックする
' -----------------------------------------------------------------------------
Public Sub RollbackTransaction()
CN_SQL.RollbackTrans
End Sub
' -----------------------------------------------------------------------------
' F_取引先登録のコード
' -----------------------------------------------------------------------------
'DB接続
Call DB_CONNECT
'SQL文生成
s_SQL = ""
s_SQL = "SELECT COUNT(*) AS REC_CNT "
s_SQL = s_SQL & "FROM M_取引先マスタ "
s_SQL = s_SQL & "WHERE 取引先CD LIKE '201804%';"
i_Seq = REC_CNT + 1
'SQL文実行
RS_SQL.Open s_SQL, CN_SQL, adOpenStatic, adLockReadOnly
'トランザクション開始
Call BEGINTRANSACTION
'SQL文生成
s_SQL = ""
s_SQL = "SELECT * FROM M_取引先マスタ WITH (TABLOCK, HOLDLOCK);"
'SQL文実行
'RS_SQL.Open s_SQL, CN_SQL, adOpenKeyset, adLockPessimistic
s_SQL = ""
s_SQL = "INSERT INTO M_取引先マスタ (取引先CD, 取引先名, 取引先住所, 更新日時) "
s_SQL = s_SQL & "VALUES ( "
s_SQL = s_SQL & "'" & s_SysDate & String(3 - Len(CStr(i_Seq)), "0") & CStr(i_Seq) & "', "
s_SQL = s_SQL & "N'" & Forms!F_取引先登録.txt_取引先名.Value & "', "
s_SQL = s_SQL & "N'" & Forms!F_取引先登録.txt_取引先住所.Value & "', "
s_SQL = s_SQL & "'" & Format(Now(), "yyyy/mm/dd hh:mm:ss") & "' "
s_SQL = s_SQL & ");"
'SQL文実行
RS_SQL.Open s_SQL, CN_SQL, adOpenKeyset, adLockPessimistic
'SQL文コミット
Call COMMITTRANSACTION
'接続を閉じる
Call DB_DISCONNECT
' -----------------------------------------------------------------------------
' 以下は[DELETE]-->[INSERT]しているロジックです
' -----------------------------------------------------------------------------
'----- DB接続 -----
Call DB_Connect
'----- トランザクション開始 -----
Call BeginTransaction
'該当データをDELETE --> INSERT
s_SQL = "DELETE FROM T_案件テーブル WHERE ID = '" & KEY & "';"
'SQL文実行
RS_SQL.Open s_SQL, CN_SQL, adOpenKeyset, adLockPessimistic
s_SQL = "INSERT INTO T_案件テーブル (ID, FLD1, FLD2, FLD3) "
s_SQL = s_SQL & "VALUES ( , , , );"
'SQL文実行
RS_SQL.Open s_SQL, CN_SQL, adOpenKeyset, adLockPessimistic
'----- DBコミット -----
Call CommitTransaction
'----- 接続を閉じる -----
Call DB_DISCONNECT
試したこと
補足情報(FW/ツールのバージョンなど)
Access2010
SQL Server2014
-
クリップを取り消します
-
質問の評価を上げたことを取り消します
-
質問の評価を下げたことを取り消します
checkベストアンサー
0
RS_SQL.Openで毎回セッションを開いているんじゃないですか?
別セッションなら当然排他されますので。
追記
SELECT COUNTのSQL文をコメントアウトすると、INSERT文の行で同エラーになります。
insert って openじゃなくて、executeじゃなかった?
まあ、本題とは関係ないですけど。
追加されたコードを見るにセッションは引き継いでいるように見えますね。
Call BEGINTRANSACTION
の前でCOMMITするとどうなります?
やっぱり、openでの排他モードが関係しているような気がします。
selectでテーブル単位の排他を行っていますけど、openは基本カーソルなのでレコード単位までしかありません。
executeに変えてみてはどうでしょうか。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 87.88%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼