A Function that returns a Recordset (1 Viewer)

keirnus

Registered User.
Local time
Today, 12:51
Joined
Aug 12, 2008
Messages
99
Hello,

I'm into DB access now.
I have a SELECT Query code here:

Code:
Public Sub DBSelectUpdateListTable()
    
    Dim db As DAO.Database
    Dim rs As DAO.Recordset
    
    Set db = CurrentDb
    Set rs = db.OpenRecordset("SELECT ActivityID FROM " & DB_UPDATE_LIST & " ORDER BY ActivityID")
    
    While Not rs.EOF
        
        rs.MoveFirst
        GatherDataHere = rs![ActivityID]
        rs.MoveNext
        
    Wend
    
    rs.Close
    db.Close
End Sub

I want this function to be exclusively for DB access only.
So, I want to get the gathered recordset data out from this function.

What is the best way to do that? :confused:

I was thinking of return function but I don't know what to use for a Recordset. :eek:
 
I'm not understanding why you need this, but if you want a function to return a recordset just do this:

Note: Aircode!
Code:
Public Function Foo() As Recordset
...
End Function

Then whenever you call the function, the code would look like this:

Code:
Private Sub bar()

Dim iter as DAO.Recordset

Set iter=foo()

....

End Sub

HTH.
 
I'm not understanding why you need this, but if you want a function to return a recordset just do this:

Note: Aircode!
Code:
Public Function Foo() As Recordset
...
End Function

Then whenever you call the function, the code would look like this:

Code:
Private Sub bar()
 
Dim iter as DAO.Recordset
 
Set iter=foo()
 
....
 
End Sub

HTH.

Oh, that does the job. :)

I just want the function to be exclusively for DB access only.
When the data is gathered in that function, I want it to be saved
into something and pass it by the function.

I will just be calling that function whenerver I need to search data
(using SELECT Query) from an Access DB.

Is it a good implementation?
Do I need to pass the whole Recordset or pass something else?

My implementation is kinda not good. :(
 
I'm afraid I will have to ask you to elaborate more on "exclusively for DB access only" as I'm not sure what it is supposed to mean.

Furthermore, I'm not clear why this has to be done in code when it would be simpler to do with a bound form...

Maybe if you explained just a bit more about your business needs and what you want to do with data, then we can help with providing you the most simple and effective solution for your particular needs instead of giving you code that answers your question but isn't the best solution...
 
I'm afraid I will have to ask you to elaborate more on "exclusively for DB access only" as I'm not sure what it is supposed to mean.

Furthermore, I'm not clear why this has to be done in code when it would be simpler to do with a bound form...

Maybe if you explained just a bit more about your business needs and what you want to do with data, then we can help with providing you the most simple and effective solution for your particular needs instead of giving you code that answers your question but isn't the best solution...

Ooops, sorry if I wasn't able to give more information.
My bad.

"Exclusively for DB access only" means it access the DB
on that function only.

I was thinking of passing an array by that function which retrieves DB data instead.

I just need to get DB data using SELECT * Query and display them in my customized GUI.
 
Sounds to me like you are trying to apply a three tier internet security protocol into access....
 
"Exclusively for DB access only" means it access the DB on that function only.

I was thinking of passing an array by that function which retrieves DB data instead.

I just need to get DB data using SELECT * Query and display them in my customized GUI.

I'm still fuzzy on this. I was expecting a response among those lines:

"The database will be accessed via internet and I need to secure the access."

"I want to check if users has the permissions first before retrieving the data."

"I do not want to allow users to edit the tables directly, rather using my GUIs instead."

"I am using Visual Basic 6 as a front end and need to retrieve data from an Access database."


or something in similar vein....

Did that clarify the question?
 
First, let's understand what a recordset is.

It is a data structure that points somewhere in a record stream associated with a record source such as a table or query. It is a POINTER to something, a placeholder.

You dont' return the actual record this way. I.e. there is no copyout of the buffer contents. We talk about a recordset as though it were really a set of records - but it is not the set. It is a selection mechanism for the IMPLIED set.

Passing a recordset is perfectly fine as long as you realize that you are passing a pointer and descriptive structure for the REAL set of records.

Now... I'm with Banana and others on the "why" of doing this. But as a toolsmith, I've had to write some pretty bizarre code sometimes as a way of compartmentalizing some action, so that I could ASSURE that some rule was being followed before I actually touched the entity in question. And that's not uncommon. So I won't criticize for the choice. If I have any criticism at all, it is that you appear to not understand at a verbal level WHY you have made the choice. And that betrays a sense of confusion.

Let's be honest - when it comes to problem-solving, all of the regulars here tend to want to give you the BEST answer, not the narrowly constructed "correct" answer to the question. So we get nosey and ask questions. But there is an ulterior motive. We learn not only by doing but by seeing problems and learning how others approach them. Some problems are "ordinary" but every now and then we get a problem that lets us learn something new. So of COURSE we have to explore anything we don't understand.

Back to your issue. If you cannot articulate WHY you did something, then perhaps you did it for the wrong reason. And that is what we are picking up in this thread.
 
I'm considering doing this but it does not seemed a recommended solution & I'm confused why this is not a more common question; I'm obviously missing something.

I have a common precompiled select query (as it's used so often to maximise speed) to obtain different values assigning the result to a recordset. Dependant on where I use that query I want to return different values. Atm I am writing a different function for each different field I want returned. This is getting messy. It seems it would be better to return the recordset itself. So presumably the recordset is instantiated in the caller & the object reference disposes of itself once the return value is returned?

What would be the recommended procedure for this? I don't want to go defining & opening the recordset where I currently have the call, as that would be several lines; opposed to a single line.
 
Last edited:
@dalski - Tacking onto an old thread is certainly permitted, but you might get more answers by starting a new thread and including a link to it. You have passed the "create a link" limit so should be able to just find the link at the top of its page, then do a copy/paste on it, leaving the copy of the link in your new post. There are other ways, but that is the traditional way for copying web links.

The problem with precompiled recordsets is that at first glance, they are always the same and always return the same records. However, there is a loophole of sorts.

You say you want to obtain different results from the same recordset depending on from where you use it. The loophole that might help you is that if you work with query parameters, you retain any pre-optimized nature of the recordset. What you do is define some parameters that can affect the WHERE clause that is part of the query. Before you actually use the query, you store values in the query's parameters. THEN you use the query. You will always select the same fields but by using parameters to make a dynamic filter, you can choose different sub-sets of the main query you describe.

Note that you DO NOT get to change everything in the query by parameters. For instance, you can change the value of a "...WHERE X = param0" by putting different values in param0 (the parameter). But you can't change that to "...WHERE X IN (SELECT Y FROM Table2 WHERE Z= param0) ..." because that would materially alter the precompilation. Here's a starting point.


Here's a VBA-oriented article.

 
I'm considering doing this but it does not seemed a recommended solution & I'm confused why this is not a more common question; I'm obviously missing something.
The normal solution simply works with the recordset. Not sure why you feel the need to move a step away from that and you haven't told us.

A query returns the same set of columns each time it is executed. A saved query is static. ie, it's structure is not changing. That is why saving querydefs is more efficient. They are compiled and the execution plan is saved and reused. If you change anything about the structure, the saved querydef is discarded and a new one must be created. You cannot change structure on the fly. If you are talking about a parameter, that is different. You can create a query that takes an argument, customerID for example, and when you feed it a customerID, it returns THAT record only.

There is no benefit to your desired approach. Each time you want different fields returned, you create a different query. The queries are not the same and I don't understand why you think they are. Ditch the functions and use separate queries. Depending on how many variations of this select query you need, you might just build it on the fly rather than create a bunch of almost identical querydefs.
 
Thanks Doc_Man, Pat.
If you are talking about a parameter, that is different. You can create a query that takes an argument, customerID for example, and when you feed it a customerID, it returns THAT record only.
Correct, that's what I'm up to; a query parameter executed from a function & from your great book recommendation using a saved query for the benefit you mentioned. But in my little mind I have the benefit of only a single line of code calling the function to return the result & in complex procedures to my little mind it is less superfluous & more concise to what I'm actually doing in the more advanced procedures; with lots of code. These query results are used in several places. But the downside is I'm now generating lots of queries & it's getting difficult to see exactly what does what; despite sticking to naming conventions & trying to minimize their use.

Ditch the functions and use separate queries. Depending on how many variations of this select query you need, you might just build it on the fly rather than create a bunch of almost identical querydefs.
I was doing this originally, then i discovered Parameter Queries, thinking they would make it more concise.

The normal solution simply works with the recordset. Not sure why you feel the need to move a step away from that and you haven't told us.
Ok understood, it seems one trade-off for another, I either get a bloated Navigation Pane or bloated code with recordset declarations & finding records... I'll also have duplicate code doing the same thing in several places, pretty near identical to the astute e.g. you used of looking up a certain customerID. This is why i thought a function for that specific purpose would be best. BUT I'm now getting several variations of that function; returning CustomerID, returning CustomerAddress... So I thought best to return the recordset itself.
 
Last edited:
Ok understood, it seems one trade-off for another, I either get a bloated Navigation Pane or bloated code with recordset declarations & finding records... I certainly want to use the recommended method so thank you.
If every query returns different fields, then every query is different whether they take a parameter or not. I don't have a clue what you're doing with the functions. Only you see the Nav pain (sic). The users never see it. Use a rational naming standard for your queries so you know what they are being used for.

I'm pretty sure that rather than creating a bunch of parameter queries that each return a single different field, most people would use dLookup()'s. Technically less efficient but obvious and doesn't create bloat. And that leads to a design question because it sounds like your table contains a repeating group and that is very poor practice.
 
Thanks Pat, no the benefit is I have a single parametized query, which returns several different fields. This parametized query gets executed in many different procedures, many times. Sometimes it returns the same fields, sometimes not. In my little mind this reduces many different queries, performing the same lookup. But it would be nice to return different fields from that parametized query, but I get what you're saying so thank you.
I'm pretty sure that rather than creating a bunch of parameter queries that each return a single different field, most people would use dLookup()'s. Technically less efficient but obvious and doesn't create bloat. And that leads to a design question because it sounds like your table contains a repeating group and that is very poor practice.
Not getting self-defensive here so don't misconstrue my comments & you'd have know way of knowing this. I don't think I have any repeating groups. This query is based on another query to reduce the recordset size & I'm organizing hierarchial data with 10-different levels. With god knows how many different permutations.
My other option is recursion, but as we all know is resource-heavy. So I'm using queries to lookup values & structuring the logic to avoid recursion & using SQL to maximize speed, hence maybe why you think it is a bad design. I take the advice of experts like yourself very seriously & try to implement every piece of guidance I get; hence me trying to ascertain that I'm on a good path, thanks again.
 
So presumably the recordset is instantiated in the caller & the object reference disposes of itself once the return value is returned?
The question is not very clear, for what you want to do. But sounds like you want to get some values from a recordset and then disconnect the recordset. That will not do it because you create another pointer to the instance in the calling code.

Are you returning one record with lots of fields? If so a type can make things a lot easier to read and manage
Code:
Type ResourceRecord
  Description As String
  BuyRate As Currency
  DiscountPercent As Double
  ResourceType As String
  UnitUse As String
End Type

Public Function GetResource(ResourceID As Long) As ResourceRecord
  Dim rs As DAO.Recordset
  Set rs = CurrentDb.OpenRecordset("Select * from ResourcesQ where ResID = " & ResourceID)
  If Not rs.EOF Then
    With GetResource
      .Description = rs!Description
      .BuyRate = rs!BuyRate
      .DiscountPercent = rs!ResDiscount_Percent
      .ResourceType = rs!ResType
      .UnitUse = rs!UnitUsed
    End With
  Else
    MsgBox "Bad or no record", vbInformation
  End If
  rs.Close
End Function

Public Sub Test()
  Dim RR As ResourceRecord
  RR = GetResource(134)
  Debug.Print "ResID 134 " & RR.Description & " Type: " & RR.ResourceType & " Discount: " & RR.DiscountPercent & " BuyRate: " & RR.BuyRate & " UnitUse " & RR.UnitUse
End Sub
Code:
ResID 134 Diesel Type: Material Discount: 0 BuyRate: 1.19 UnitUse Ltrs
 
Last edited:
But like @Pat Hartman suggests, most of my DBs have tons of Domain wrappers. Sometimes hundreds. Very simple and do one thing.

You pass in an ID and get back a value.

Code:
Public Function GetPathLength(pathID As Variant) As Long
 If Not IsNull(pathID) Then GetPathLength = Nz(DLookup("length", "qryPathLengths", "pathid_FK = " & pathID), 0)
End Function
Public Function GetPathDuration(pathID As Variant) As Long
 If Not IsNull(pathID) Then GetPathDuration = Nz(DLookup("TotalDuration", "qryPathSummary", "pathid = " & pathID), 0)
End Function
Public Function GetPiggyBackWait(pathID As Variant) As Long
 'both driver and passenger wait times
 If Not IsNull(pathID) Then GetPiggyBackWait = Nz(DLookup("waitTime", "qryWaitTimeOnPath", "pathid_FK = " & pathID), 0)
End Function

Public Function GetNumberOfTripsOnPath(pathID As Variant) As Integer
  If Not IsNull(pathID) Then GetNumberOfTripsOnPath = DCount("*", "tblPathTrips", "pathid_FK = " & pathID)
End Function
Public Function GetEndingTrip(pathID As Variant) As Long
  Dim strSql As String
  Dim rs As DAO.Recordset
  If Not IsNull(pathID) Then
    strSql = "Select top 1 TripID from qryPathTrips where PathID_FK = " & pathID & " order by TripID Desc"
    Set rs = CurrentDb.OpenRecordset(strSql)
    If Not rs.EOF Then GetEndingTrip = rs!tripID
  End If
End Function
Public Function GetEndingEdge(pathID As Variant) As Long
  Dim strSql As String
  Dim rs As DAO.Recordset
  If Not IsNull(pathID) Then
    strSql = "Select top 1 EdgeID from qryPathTrips where PathID_FK = " & pathID & " order by TripID Desc"
    Set rs = CurrentDb.OpenRecordset(strSql)
    If Not rs.EOF Then GetEndingEdge = rs!EdgeID
  End If
End Function
Public Function GetEndingVertex(pathID As Variant) As Long
  Dim endingEdge As Long
  Dim strSql As String
  Dim rs As DAO.Recordset
  If Not IsNull(pathID) Then endingEdge = GetEndingEdge(pathID)
  If Not endingEdge = 0 Then
     strSql = "Select top 1 EndVertex from qryPathTrips where PathID_FK = " & pathID & " order by TripID Desc"
     'Debug.Print strSql & " problem"
     Set rs = CurrentDb.OpenRecordset(strSql)
     If Not rs.EOF Then GetEndingVertex = rs!EndVertex
  End If
End Function
Public Function GetDriverFromPath(pathID As Variant) As Long
  If Not IsNull(pathID) Then
    GetDriverFromPath = Nz(DLookup("driverID", "tblcarShippingPaths", "pathID = " & pathID), 0)
  End If
End Function
Public Function GetPiggyBackDriver(pathID As Variant, EdgeID As Variant) As Long
  Dim CurrentDriver As Long
  If Not IsNull(pathID) And Not IsNull(EdgeID) Then
    CurrentDriver = GetDriverFromPath(pathID)
    If CurrentDriver <> 0 Then GetPiggyBackDriver = Nz(DLookup("driverID", "qrySelectPiggyback", "edgeID_FK = " & EdgeID & " AND driverID <> " & CurrentDriver), 0)
  End If
End Function
Public Function GetPiggyBackTrip(pathID As Variant, EdgeID As Variant) As Long
  Dim CurrentDriver As Long
  If Not IsNull(pathID) And Not IsNull(EdgeID) Then
    CurrentDriver = GetDriverFromPath(pathID)
    If CurrentDriver <> 0 Then GetPiggyBackTrip = Nz(DLookup("tripID", "qrySelectPiggyback", "edgeID_FK = " & EdgeID & " AND driverID <> " & CurrentDriver), 0)
  End If
End Function
Public Function GetNumberAvailablePiggybacks(pathID As Variant, EdgeID As Variant) As Long
  Dim CurrentDriver As Long
  If Not IsNull(pathID) And Not IsNull(EdgeID) Then
    CurrentDriver = GetDriverFromPath(pathID)
    If CurrentDriver <> 0 Then GetNumberAvailablePiggybacks = DCount("*", "qrySelectPiggyback", "edgeID_FK = " & EdgeID & " AND driverID <> " & CurrentDriver)
  End If
End Function
Public Function GetTotalDeliveredFromPath(pathID As Variant) As Long
  If Not IsNull(pathID) Then
    GetTotalDeliveredFromPath = Nz(DLookup("SumOfCarsShipped", "qryTotalShippedPiggyBackByPath", "pathID_FK = " & pathID), 0)
  End If
End Function
Public Function GetTotalPiggyBacksFromPath(pathID As Variant) As Long
  If Not IsNull(pathID) Then
    GetTotalPiggyBacksFromPath = Nz(DLookup("SumOfPiggyback", "qryTotalShippedPiggyBackByPath", "pathID_FK = " & pathID), 0)
  End If
End Function
Public Function GetTotalPassengersFromPath(pathID As Variant) As Long
  If Not IsNull(pathID) Then
    GetTotalPassengersFromPath = DCount("*", "qryPiggyBackData", "DriverPath = " & pathID)
  End If
End Function
Public Function GetNextDriver() As Long
  GetNextDriver = Nz(DLookup("driverID", "qryNextDriver"), 0)
End Function
Public Function GetStartVertexFromEdge(EdgeID As Long) As Long
  GetStartVertexFromEdge = Nz(DLookup("startVertex", "tblCarShippingEdges", "EdgeID = " & EdgeID))
End Function
Public Function GetEndVertexFromEdge(EdgeID As Long) As Long
  GetEndVertexFromEdge = Nz(DLookup("EndVertex", "tblCarShippingEdges", "EdgeID = " & EdgeID))
End Function
 
If you are filling variables from table columns like the following

Description = MyTableDesc(LookUpID)
Location= MyTableLocation(LookUpID)
..
SomeOtherField = MyTableSomeOtherField(LookUpID)

Consider using a class to fill and return values from your query.

With New MyTableClass
.LookupID = LookupID
Description = .Description
Location = .Location
..
SomeOtherField = .SomeOtherField
End With
 
Last edited:
Unless you're going to be using each of those functions multiple times, seems like overkill.
 

Users who are viewing this thread

Back
Top Bottom