1
votes

In my project I have a table1 that has an ID and a text field. table2 has a multi-valued field multifield wherein I can select multiple values from table (via lookup).

Now, I have query1 where I want to pull the value from multifield - not multifield.value and pass it to a VBA function, like this: IDsToNames: table2.multifield. Problem is that VBA gives me this error:

The multi-valued field 'table2.multifield` is not valied in the expression IDsToNames(table2.multifield)

Now, I've tried with IDsToNames([table2].[multifield]) with the same results.

Here is my query's SQL:

SELECT table2.Title, table2.multifield, IDstoNames(table2.multifield) AS FieldNames
FROM table2;

If I remove the IDsToNames function from the SQL, then table2.multifield by itself will return the IDs like: 5, 4, 1 properly. I'm trying to fetch the second column of table1 instead of the first one that includes the IDs. So i figured I'd try passing that field to a function and perform a string split and them look them up with dlookup in a loop. But I can't get the field data into the function.

Is there a way to do this?

1
It doesn't work that way. Study this: Using multivalued fields in queries.Gustav

1 Answers

1
votes

Is there a way to pass a multivalued field directly to a VBA function from within an SQL statement? No, regrettably.

However, there are various alternative methods that you can implement to get the list of values stored in the field. It's easy to ramble on about the pros and cons of Access multivalued fields. I'm not going to do that in detail, but it is worth stating that the primary benefit of a multivalue field is the convenience and apparent simplicity of the Access interface being able to automatically list and allow selection of multiple values of a one-to-many relationship. Trying to mimic that behavior in Access can be very awkward and cumbersome (and often impossible). Much of the implementation details for the multivalued fields are "hidden" (i.e. not well documented or are not exposed to the standard SQL or VBA programming interfaces). This includes the ability to pass the mutlivalued field to a VBA function from within an SQL statement. Regardless of how intuitive the intended behavior seems, Access will not simply pass the same concatenated list of values that it displays to another function. But there are still times when one simply wants the list of values, made accessible in a simple list. The information linked to by Gustav is useful and should be well understood for querying multivalued fields, but it still does not address other perfectly reasonable actions required for multiple values. Hopefully the following pointers are useful.

  1. If the values are needed within a standard SQL statement, I suggest passing the primary key value(s) to a VBA function. Then have the VBA function look up the record and retrieve the multivalued-field values using DAO recordsets.
    • Because this will call the VBA function for every row, this can be (very) slow. It is possible to optimize the function using various techniques, like opening a static recordset object. Further details are beyond the scope of this answer.
    • Since you're already in code at this point and can structure VBA and queries however you want, the most efficient query will circumvent the multivalued-field itself and use standard SQL joins to get what you need. For instance, if you want to get all of the related user names, then open and enumerate the following recordset to build your list of names:

 sSQL = "SELECT table2.key, table2.multifield.value, UserTable.Username " & _
     " FROM UserTable INNER JOIN table2 ON UserTable.ID = table2.multifield.Value" & _
     " WHERE (table2.key = [KeyParameter])"
 Set qry = CurrentDb.CreateQueryDef(, sSQL)
 qry.Parameters("KeyParameter") = keyPassedToFunction
 Set rs = qry.OpenRecordset

  1. If the SQL query can/will be opened as a DAO recordset in a code context and you still need to retrieve the multivalued-field as a single field, there is a way to enumerate the multivalued-field in VBA.
    • If the code ends up repeatedly opening and closing multiple recordsets, especially in multiple nested loops, it is likely that you could improve efficiency by restructuring the SQL using appropriate joins and changing the data processing order.
    • Rant: As you might notice, it is somewhat inconsistent that the underlying recordset object of an SQL statement does indeed return an object which can be enumerated in code, although the Access SQL engine refuses to pass such an object to a VBA function. The SQL engine already deals with boxing and unboxing data into the VBA variant type, so it seems reasonable that when implementing the multivalue fields, they could have had the SQL engine simply box the multivalued recordset object and passed it to a VBA function to be handled similar to the following code... so the original attempt in the question was not unreasonable.
    • The following code snippet illustrates that the multivalue field is returned as a DAO.Field object containing a DAO.Recordset2 object:

Dim rs as Recordset2
Set rs = CurrentDB.OpenRecordset("SELECT table2.multifield ... FROM ...")

Dim sList As String
sList = ""
If TypeOf rs![multifield] Is Recordset2 Then
    Dim rsMVF As Recordset2
    Set rsMVF = rs![multifield]

    Dim bFirst As Boolean
    bFirst = True

    rsMVF.MoveFirst
    Do Until rsMVF.EOF
        If bFirst Then
            sList = rsMVF.Fields(0)
            bFirst = False
        Else
            sList = sList & "," & rs.Fields(0)
        End If
        rsMVF.MoveNext
    Loop
    '* DO NOT CLOSE the Recordset based on the Multivalue field.
    '*   Access will close it automatically.
End If
'* sList will contain comma-separated list of values