Microsoft Access 掲示板

SQL ServerからADOで取得したRecordsetの扱いについてご教示いただけないでしょうか。

2 コメント
views
4 フォロー

この6月からAccessを始めて生産管理アプリの作成に挑戦しております。
現在、社内ファイルサーバーにあるAccessファイルをバックエンドとし、
リンクテーブルで接続しています。
利用者は当日の生産目標や実績が一覧になった帳票フォームを操作画面とし、
フォームのロード時にVBAで生成したSQL文をレコードソースにセットする連結フォームの形で実現していました。

今回SQL Serverへの移行のため、コードの見直しをしております。
現状では、ADO接続で取得したRecordsetをフォームにセットすることで、
リンクテーブル時と同様に当日の案件を表示することができるようになりました。
しかし、動作が異なる箇所が多く非常に悩んでおります。

まだまだSQLもVBAも始めたばかりで、分からないことだらけの中、
自分1人で検索しながら答えを出すことに行き詰まってまいりました。
皆様アドバイスをいただけないでしょうか。

  1. ADOで取得したレコードセットをフォームにセットしても、
    リンクテーブルから取得していた時のように連結フィールドへのデータ入力が
    そのままがDBに反映されることはない?(スナップショットの様な動作?)
    ⇒ 入力されたフィールドに対して、更新ボタンやイベントでカレントレコードに対して更新クエリを作成し、
    ADOでDBに投げる形で更新する、といった動作のイメージでしょうか?
    パススルークエリも検証してみましたが、同様にスナップショットとして扱われるのでしょうか。

     レコードセット取得文の一部抜粋(SQL等は割愛します)

     With adoRs
         .ActiveConnection = adoCn
         .Source = strSQL
         .LockType = adLockOptimistic
         .CursorType = adOpenKeyset
         .CursorLocation = adUseClient
         .Open
     End With
         
     Set adoRs.ActiveConnection = Nothing 
         
     Set t_form.Recordset = adoRs
    
  2. 従来のリンクテーブルで動作したしていたFilterを、
    ADOで取得したフォームのレコードセットに適応しても画面に反映されない。
    例)

    Me.Recordset.Filter = '内容'
    

    ⇒ 取得したレコードから更に絞り込んで表示したい場合は、
    改めてADOでSQLを投げてレコードセットを更新するべき?

  3. リンクテーブルは遅いからADOを使えという知見が多く見られるため、
    今回がんばって連結フォームの機能をADOで実現しようとしているが、
    SQL Serverへのリンクテーブルを作成し、従来の様にレコードソースにSQL文を入れる形でもよいのでしょうか?
    リンクテーブルは、VBAからDSNレスでの作成ができました。
    (想定されるユーザー数は現状では多くて20~30かと思います。増える可能性は大いにあります。)

以上となります。
どうぞよろしくお願い申し上げます。

UME
作成: 2022/08/30 (火) 22:14:37
通報 ...
1

なかなか、回答が付かないですね。
私自身は、SQL Serverでの経験がないので、適切な回答はできないので、
感想的な回答ですので、参考程度に。

3. リンクテーブルは遅いからADOを使えという知見が多く見られるため、

そのようなソースのリンクがあれば紹介してもらえますか。

個人的には、リンクテーブルだから遅いのではなく、リンクテーブルと連結したフォームだと、常に更新可能なレコードセットとしてサーバーデータと常時接続された状態だから、遅いのだと思います。

ですので、ADOのレコードセットを開いて、フォームと連結させたら、結局、上記と同じ状態なので、速度的にはたいしてかわらないと推測しています。

もし、リンクテーブルと連結したフォームより、ADOのレコードセットと連結したフォームの方が速度的に有利であるという信頼できるソースがあるなら知りたいです。

1. ADOで取得したレコードセットをフォームにセットしても、
リンクテーブルから取得していた時のように連結フィールドへのデータ入力が
そのままがDBに反映されることはない?(スナップショットの様な動作?)

下記によると要件さえ満たしいれば、更新可能になるようです。

ADO レコードセットにフォームを連結します | Microsoft Docs

2. 従来のリンクテーブルで動作したしていたFilterを、
ADOで取得したフォームのレコードセットに適応しても画面に反映されない。

フォームのそのようなプロパティはレコードソースで接続していることが前提のようで、Recordsetプロパティで連結した場合はほとんど使えないようです。RecordsetのFilterプロパティを使うか、SQLでレコードセットを再取得して、それと連結させるという処理が必要なようです。

ということで、あくまで、想像ですが、
連結フォームのような使い勝手は難しいと思います。

結局、速度改善したいなら、
パススルークエリで、必要なデータのみ取得して、これは閲覧のみ、更新する場合は、別に非連結フォームで更新したものを、更新クエリ、あるいは追加クエリでデータ更新することになるのではと思っています。

2

リンクテーブルから取得していた時のように連結フィールドへのデータ入力が
そのままがDBに反映されることはない?

Office VBA Reference: Bind a form to an ADO Recordset

・そのレコードセットが ADO を介して更新可能であること。

・そのレコードセットに一意なインデックス(テーブルの主キーなど)が含まれていること。

基本的には上記の条件を満たしているか否か次第。

SQL等は割愛します

なので、レコードセットを開く際にどのような SQL 文を実行しているかも
無関係ではありません。

従来のリンクテーブルで動作したしていたFilterを、
ADOで取得したフォームのレコードセットに適応しても画面に反映されない。

Me.Recordset.Filter = '内容'

ADO.Recordset オブジェクトと連結されたフォームにおいて、
そのフォームの Recordset プロパティを介して Filter プロパティを
設定するのは無効です。

リンクテーブルは遅いからADOを使えという知見が多く見られるため、
今回がんばって連結フォームの機能をADOで実現しようとしているが、
SQL Serverへのリンクテーブルを作成し、従来の様にレコードソースに
SQL文を入れる形でもよいのでしょうか?

そのテーブルやフォームの使用目的によるとしか。

想定されるユーザー数は現状では多くて20~30かと思います。増える可能性は大いにあります。

マルチユーザーアクセスを前提としているのであれば、少なくとも
SQL Server のテーブルに格納されている全てのレコードを、
編集目的のためにまるごと呼び出して表示するような処理は
極力避けた方がよいでしょう。
(トランザクションや排他ロックなどの制御を行なう必要があるなら
なおのこと)

Option Compare Database
Option Explicit

Private adoCn As ADODB.Connection
Private adoRs  As ADODB.Recordset

Private Const ServerName  As String = "ServerName"
Private Const UserName    As String = "UserName"
Private Const Password    As String = "PassWord"
Private Const CatalogName As String = "DatabaseName"
Private Const TableName As String = "TableName"

'フォームの[開く時]イベント
Private Sub Form_Open(Cancel As Integer)
On Error GoTo Err_Form_Open

    Me.Painting = False

    Set adoCn = New ADODB.Connection
    
    With adoCn
        'SQL Server Native Client をプロバイダーとする場合
        .ConnectionString = "Provider=SQLNCLI11;" & _
                            "Data Source=" & ServerName & ";" & _
                            "User Id=" & UserName & ";" & _
                            "Password=" & Password & ";" & _
                            "Initial Catalog=" & CatalogName & ";" & _
                            "DataTypeCompatibility=80"
        .CursorLocation = adUseClient
        .CommandTimeout = 1
        .Open
    End With
    
    Dim strSQL As String
    strSQL = "SELECT * FROM [dbo].[" & TableName & "] ORDER BY [ID];"
    
    Set adoRs = New ADODB.Recordset
    
    With adoRs
        Set .ActiveConnection = adoCn
        .Source = strSQL
        .CursorLocation = adUseClient
        .CursorType = adOpenKeyset
        .LockType = adLockOptimistic
        .Open
    End With
    
    Set Me.Recordset = adoRs
    
Exit_Form_Open:
    
    Me.Painting = True
    
    Exit Sub

Err_Form_Open:
    
    Dim ErrText As String
    ErrText = "実行時エラー " & Err.Number & ": " & Err.Description
    Debug.Print ErrText
    MsgBox ErrText, vbCritical, Me.Name & ".Form_Open"
    
    Call ReleaseObjects
    Cancel = True

End Sub

'フォームの[読み込み解除時]イベント
Private Sub Form_Unload(Cancel As Integer)
    
    Call ReleaseObjects

End Sub

'コマンドボタン[cmdExecFilter]の[クリック時]イベント
Private Sub cmdExecFilter_Click()

    '編集中のカレントレコードを保存する
    If Me.Dirty = True Then
        DoCmd.RunCommand acCmdSaveRecord
    End If
    
    Dim strCriteria As String
    
    '非連結テキストボックス[txtFilter]の値が
    '数値データに変換可能である場合
    If IsNumeric(Me![txtFilter].Value) Then
        'フィールド[ID]の値が、非連結テキストボックス[txtFilter]の値を
        '整数値に変換した結果と等しいレコードを抽出する条件式
        strCriteria = "[ID]=" & CLng(Me![txtFilter].Value)
    End If
    
    'フィルターを適用
    adoRs.Filter = strCriteria
    
    'レコードセットを再連結
    Me.Painting = False
    Set Me.Recordset = Nothing
    Set Me.Recordset = adoRs
    Me.Painting = True

End Sub

'コマンドボタン[cmdClose]の[クリック時]イベント
Private Sub cmdClose_Click()

    'このフォームを閉じる
    DoCmd.Close acForm, Me.Name, acSaveNo

End Sub

'オブジェクトの解放処理
Private Sub ReleaseObjects()
On Error Resume Next

    If Not adoRs Is Nothing Then
        If adoRs.State = adStateOpen Then
            adoRs.Close
        End If
        Set adoRs = Nothing
    End If
    
    If Not adoCn Is Nothing Then
        If adoCn.State = adStateOpen Then
            adoCn.Close
        End If
        Set adoCn = Nothing
    End If

End Sub

とりあえずの例としては、上記のようなコードが挙げられますが。