☛ ADO Recordset 對象

ADO Recordset 是一種非常通用的對象。在絕大多數情況下,通過執行 Command 或直接通過其 Open 方法來填充該對象。Open_ADO_Recordset 說明了 Recordset 對象可以非常輕鬆地打開 Access 表,如下所示:

Public Sub Open_ADO_Recordset()

    Dim adRs As ADODB.Recordset

    Set adRs = New ADO.Recordset

    adRs.Open "SELECT * FROM tblCustomers;",_
    CurrentProject.Connection

    Debug.Print adRs.GetString

    adRs.Close
    Set adRs = Nothing

End Sub

上面的示例中,使用了一個 SQL 語句來從 tblCustomer 表中選擇記錄,並填充記錄集。該 SQL 語句可包含 WHER 或 ORDER BY 子句以對選定的數據進行篩選和排序。

編寫此過程的一種替代方式是使用單個語句分配 ActiveConnection 屬性,如下所示:

Public Sub Open_ADO_Rs_Connection()
  
    Dim adRs As ADODB.Recordset
  
    Set adRs = New ADODB.Recordset
    adRs.ActiveConnection = CurrentProject.Connection
  
    adRs.Open "SELECT * FROM tblCustomers;"
  
    Debug.Print adRs.GetString
  
    adRs.Close
    Set adRs = Nothing
  
End Sub

很多開發人員傾向於使用 ADO_Rs_Connection 中的方法,因爲它可以更輕鬆地顯示對 Recordset 對象執行了什麼操作以及設置其屬性的位置。儘管這些非常小的過程可以輕鬆地理解,但對於查找對某種對象 ( 例如 adRs ) 的所有引用的大型代碼段,理解起來可能會比較難,特別是在 VBA 語句非常長並且很複雜的情況下。

與其他 ADO 對象一樣,必須對 Recordset 對象進行聲明和實例化。就像 Command 對象一樣,如果使用 Open 方法來填充 Recordset 對象,則必須將一個打開的連接以參數形式提供給 Open 方法。

⑴ 導航記錄集

如果只能打開和關閉記錄集,或者只能通過 GetString 方法使用記錄集中的數據,那麼記錄集並沒有太多的用處。根據上下文,記錄集一詞會表示多種不同的內容,如下所述:

✦ 查詢返回的數據行。

✦ 綁定到 Access 窗體的數據。

✦ 作爲 ADO 操作的結果填充了數據的對象。

但是,在上述所有情況中,記錄集都是包含數據行列的一種數據結構。當然,行代表記錄,而列代表欄位。

Access 提供的用於導航記錄集的方式非常重要。當以數據表的形式查看表或查詢結果時,你可以使用垂直和水平滾動條或箭頭鍵在記錄集的數據表視圖中上下左右移動。因此,ADO Recordset 對象支持用於在記錄集包含的記錄中移動的方法也就不足爲奇了。

下面的過程 ( RecordsetNavigation ) 演示了基本的 ADO 記錄集導航方法:

Public Sub RecordsetNavigation()
    
    Dim adRs As ADODB.Recordset
    
    Set adRs = New ADODB.Recordset
    adRs.ActiveConnection = CurrentProject.Connection
    
    adRs.CursorType = adOpenStatic
    adRs.Open "SELECT * FROM tblCustomers;"
    
    Debug.Print adRs!CustomerID, adRs!Company
    
    adRs.MoveNext
    Debug.Print adRs!CustomerID, adRs!Company
    
    adRs.MoveLast
    Debug.Print adRs!CustomerID, adRs!Company
    
    adRs.MovePrevious
    Debug.Print adRs!CustomerID, adRs!Company
    
    adRs.MoveFirst
    Debug.Print adRs.Fields("CustomerID").Value, _
      adRs.Fields("Company").Value
    
    adRs.Close
    Set adRs = Nothing

End Sub

該過程首先打開一個填充了來自 tblCustomers 表的數據的 Recordset 對象,它會立即顯示最前面的記錄中的 CustomerID 和 Company;然後一次在記錄集中移動一些行,顯示移動過程中經歷的每條記錄的 CustomerID 和 Company。最後返回到第一條記錄,並顯示其數據。RecordsetNavigation 生成的輸出如下圖所示:

很明顯,這僅僅是一個沒有什麼具體意義的示例,目的是演示導航 ADO 記錄集是多麼輕鬆。作爲開發人員,你可以隨意處理記錄集中的任何記錄,根據需要上下移動行。

Access 記錄集支持當前記錄指標的概念,一次只有記錄集中的一條記錄是當前記錄。當對某個記錄集進行更改或者導航瀏覽其行時,代碼僅會影響當前記錄。

RecordsetNavigation 過程也演示兩種用於引用記錄中的單個欄位的方法,分別是使用感嘆號 ( ! ) 和 Fields 集合。在移動到一行後,各個字段將作爲記錄集的成員被引用。Access 一次僅對一條記錄進行操作,因此,對某個欄位的任何引用都會求解爲當前記錄的欄位。

⑵ 瞭解 CursorType

在 RecordsetNavigation 過程中,請注意記錄集的 CursorType 屬性。在該示例中,該屬性設置爲 adOpenStatic。CursorType 存在多種可用的設置,adOpenStatic 指的是使用靜態類型光標打開記錄集,Access 使用光標跟蹤記錄集中的當前記錄。靜態光標意味着記錄集中的數據是靜態的,不能向記錄集中添加新記錄。如果記錄集的用途是審查基礎表中的數據並且不需要添加新記錄,那麼靜態光標是理想的選擇。下列是 CursorType 幾種類型的值及效果:

① adOpenDynamic:動態光標支持所有導航方式,並且記錄集完全可以編輯,可以添加記錄,並且可以編輯現有的記錄。其他用戶所作的更改會反映在當前位於內存的記錄集中。

② adOpenForwardOnly:記錄集作爲基礎數據的靜態副本打開,不能添加新記錄。記錄集也不會反映其他用戶對基礎表所作的更改。最重要的是,只有 MoveNext 和 MoveLast 方法對記錄集有效。

③ adOpenKeyset:支持所有導航方法,並且記錄可以編輯。但是,其他用戶添加或刪除的記錄不可見。

④ adOpenStatic:打開靜態記錄集,該記錄集中不顯示其他用戶對基礎表所作的更改。它與只進光標類似,不過所有導航方法都是有效的。

每種類型的光標都對記錄集中包含的數據具有一種特定的效果。例如:對於用戶期望能夠在其中前後移動的數據,肯定不希望使用只進光標。絕大多數情況下,只進記錄集用於批量操作的形式更新記錄 ( 如:更新大量記錄中的郵政編碼或稅率 )。

另一方面,對於諸如在記錄集中掃描更新之類的簡單任務,使用動態光標 ( adOpenDynamic ) 沒有太大意義。動態光標會跟蹤當前用戶所作的更改以及基礎表中發生的更改。因此,相比於更簡單的只進光標,動態光標速度更緩慢,並且需要更多的內存和 CPU 週期。

⑶ 檢測記錄結尾或開頭

MovePrevious 和 MoveNext 方法可將當前記錄指標在記錄集中移動一行。如果指標位於最前面或最後面的一條記錄,這些方法會將指標移出記錄集的開頭或結尾,而不會引發錯誤。當導航記錄集時,需要確保當前記錄指標位於有效記錄上,然後再引用數據或對記錄執行某種操作。

ADO Recordset 對象支持兩種布林屬性,即 EOF 和 BOF,分別表示當前記錄指標位於記錄集的結尾或開頭的情況 ( EOF 和 BOF 分別是 end of file ( 文件結尾 ) 和 beginning of file ( 文件開頭 ) 的縮寫 )。當記錄指標位於有效記錄上時,EOF 和 BOF 都是 False。當記錄指標超出記錄結尾時,EOF 爲 True,當記錄指標超出記錄開頭時,BOF 爲 True。而當記錄集不包含任何記錄時,EOF 和 BOF 則同時爲 True。

Use_EOF_BOF 過程演示了在 ADO Recordset 對象中使用 EOF 和 BOF 的情況,如下所示:

Public Sub Use_EOF_BOF()
    
    Dim adRs As ADODB.Recordset
    
    Set adRs = New ADODB.Recordset
    adRs.ActiveConnection = CurrentProject.Connection
    adRs.CursorType = adOpenStatic
    
    adRs.Open "SELECT * FROM tblCustomers " _
        & "WHERE State = 'NY' " _
        & "ORDER BY Company;"
    
    Debug.Print "RecordCount: " & adRs.RecordCount
    
    If adRs.BOF And adRs.EOF Then
        Debug.Print "No records to process"
        Exit Sub
    End If
    
    Do Until adRs.EOF
        Debug.Print adRs!Company
        adRs.MoveNext
    Loop
    
    adRs.MoveLast
    
    Do Until adRs.BOF
        Debug.Print adRs!Company
        adRs.MovePrevious
    Loop
    
    adRs.Close
    Set adRs = Nothing

End Sub

⑷ 對記錄進行計數

通常情況下,在開始執行可能需要很長時間的操作之前,瞭解記錄集中包含多少條記錄會非常有用。如果不瞭解記錄集中的記錄數,用戶可能會不明智地選擇條件,返回過多的記錄,無法高效地進行處理。你可能想要在處理大記錄集前,讓代碼向用戶發出警告。幸運的是,ADO Recordset 對象提供了一個 RecordCount 屬性,可以準確地告訴你記錄集中存在多少條記錄。下面的 UseRecordCount 子例程在處理記錄集時,使用 RecordCount 屬性來顯示總記錄數:

Public Sub UseRecordCount()
  
    Dim adRs As ADODB.Recordset
    Dim lCnt As Long
  
    Set adRs = New ADODB.Recordset
    adRs.ActiveConnection = CurrentProject.Connection
    adRs.CursorType = adOpenStatic
  
    adRs.Open "SELECT * FROM tblCustomers;"
  
    Do While Not adRs.EOF
       lCnt = lCnt + 1
       Debug.Print "Record " & lCnt & " of " & adRs.RecordCount
       adRs.MoveNext
    Loop
  
    adRs.Close
    Set adRs = Nothing
  
End Sub

RecordCount 屬性對於只進記錄集無效。請注意,在該代碼中,CursorType 被設置爲 adOpenStatic。如果將其設置爲 adOpenForwardOnly,RecordCount 屬性將設置爲 -1,並且當記錄集位於內存中時不會發生改變。

通過 RecordCount 可以非常方便地確定某個記錄集中是否包含任何記錄。對於 RecordCount 來說,唯一的問題在於,在較大的記錄集上,RecordCount 會對性能產生影響。實際上,Recordset 對象會真實地計算其包含的記錄數量,在計數過程完成前執行將一直停止。

要檢測空記錄集,一種更快捷的方式是確定 EOF 和 BOF 是否同時爲 True,如下所示:

If adRs.BOF And adRs.EOF Then
   Debug.Print "No records to process"
   Exit Sub
End If

如果 BOF 和 EOF 同時爲 True,光標將同時位於第一條記錄前和最後一條記錄後。只有記錄集中不包含任何記錄時纔會出現這種情況。