I think I've come to understand the passing of a recordset. It is always passed by reference. What I have a question about is the destruction of the object.
I have a function that acquires a recordset from Access. I call that function from a given routine, passing the SQL statement to it and getting the recordset result from it. See the code sample below.
At the end of the calling routine I close the recordset and set the object to Nothing. What I'm not clear on is whether something needs to be done to destroy the recordset object in the called function. Since the recordset is passed ByRef, I'm inclined to believe that closing the recordset and setting it to Nothing from the calling routine also destroys it in the called function. What's more, there's a part of me that thinks that the local-level variable in the called function gets destroyed upon completion of the function, but I realize that an auto-destroyed variable is different than an object, which must get destroyed manually even at the local level.
So the question is, do I have to destroy the function recordset object somehow, or does destroying the recordset object at the end of the calling routine destroy both instances? Using the code example below, does the GetReadOnlyRecords recordset get destroyed when rstSample gets destroyed, or do I have to do something else to destroy it also?
Another reason I'm asking is because, in addition to the fact that I've wondered the above already, from another function I also call the GetReadOnlyRecords function, but I don't actually need the recordset. (See: Private Function ChkDataNew() As Boolean). In there I only need to know if the resulting recordset contained any records, so instead of creating an instance of a recordset in the calling function, I simply say "If GetReadOnlyRecords(strSQL).RecordCount>0 Then". So in that case I don't have a recordset to destroy in the calling function, which suggests that nothing closes/sets-to-Nothing the original recordset in the GetReadOnlyRecords function. Obviously I could bring in the recordset to the calling function, but it didn't seem necessary since I could get all the information I needed right in the "If" statement. So if destroying the recordset from the calling function does destroy it in the called function, am I then required to create a recordset in the calling function just so I can destroy it, even though I don't otherwise need it there?
Option Explicit
'create a module-level, Public reference to a Connection object
Public objConn As ADODB.Connection
'create a string constant with the dB path
Private Const strDBPath As String = "C:\MyPath.accdb"
Private Const strConString = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & strDBPath & ";" & _
"Persist Security Info=False;"
Public Sub OpenDB()
'create a new instance of the Connection object
Set objConn = New ADODB.Connection
objConn.Open strConString
End Sub
Public Sub CloseDB()
objConn.Close
Set objConn = Nothing
End Sub
Public Function GetReadOnlyRecords(strSQL As String) As Recordset
'this routine presumes the Database already has an Open Connection
'we can't risk possibly opening it twice
Dim rstTMP As ADODB.Recordset
Set rstTMP = New ADODB.Recordset
rstTMP.Open strSQL, objConn, adOpenStatic, adLockReadOnly, adCmdText
Set GetReadOnlyRecords = rstTMP
End Function
Private Sub UpdateData()
Dim rstSample As ADODB.Recordset
Set rstSample = New ADODB.Recordset
OpenDB
strSQL = "SELECT * FROM tblExample"
Set rstSample = GetReadOnlyRecords(strSQL)
rstSample.DoStuff
'Destroy the Recordset object
rstDrawings.Close
Set rstDrawings = Nothing
CloseDB
End Sub
Private Function ChkDataNew() As Boolean
Dim dtNewDate As Date
Dim strSQL As String
ChkDataNew = False
Set dtNewDate = wsMySheet.Range("QDate").Value
strSQL = "SELECT tblExample.[Date] FROM tblExample WHERE tblExample.[Date] = " & dtNewDate
OpenDB
If GetReadOnlyRecords(strSQL).RecordCount > 0 Then
ChkDataNew = True
End If
CloseDB
End Function