Reputation: 140
I am running a query using a regular expression function on a field where a row may contain one or more matches but I cannot get Access to return any matches except either the first one of the collection or the last one (appears random to me).
Sample Data:
tbl_1 (queried table)
row_1 abc1234567890 some text
row_2 abc1234567890 abc3459998887 some text
row_3 abc9991234567 abc8883456789 abc7778888664 some text
tbl_2 (currently returned results)
row_1 abc1234567890
row_2 abc1234567890
row_3 abc7778888664
tbl_2 (ideal returned results)
row_1 abc1234567890
row_2 abc1234567890
row_3 abc3459998887
row_4 abc9991234567
row_5 abc8883456789
row_6 abc7778888664
Here is my Access VBA code:
Public Function OrderMatch(field As String)
Dim regx As New RegExp
Dim foundMatches As MatchCollection
Dim foundMatch As match
regx.IgnoreCase = True
regx.Global = True
regx.Multiline = True
regx.Pattern = "\b[A-Za-z]{2,3}\d{10,12}\b"
Set foundMatches = regx.Execute(field)
If regx.Test(field) Then
For Each foundMatch In foundMatches
OrderMatch = foundMatch.Value
Next
End If
End Function
My SQL code:
SELECT OrderMatch([tbl_1]![Field1]) AS Order INTO tbl_2
FROM tbl_1
WHERE OrderMatch([tbl_1]![Field1])<>False;
I'm not sure if I have my regex pattern wrong, my VBA code wrong, or my SQL code wrong.
Upvotes: 1
Views: 527
Reputation: 97131
Seems you intend to split out multiple text matches from a field in tbl_1
and store each of those matches as a separate row in tbl_2
. Doing that with an Access query is not easy. Consider a VBA procedure instead. Using your sample data in Access 2007, this procedure stores what you asked for in tbl_2
(in a text field named Order
).
Public Sub ParseAndStoreOrders()
Dim rsSrc As DAO.Recordset
Dim rsDst As DAO.Recordset
Dim db As DAO.database
Dim regx As Object ' RegExp
Dim foundMatches As Object ' MatchCollection
Dim foundMatch As Object ' Match
Set regx = CreateObject("VBScript.RegExp")
regx.IgnoreCase = True
regx.Global = True
regx.Multiline = True
regx.pattern = "\b[a-z]{2,3}\d{10,12}\b"
Set db = CurrentDb
Set rsSrc = db.OpenRecordset("tbl_1", dbOpenSnapshot)
Set rsDst = db.OpenRecordset("tbl_2", dbOpenTable, dbAppendOnly)
With rsSrc
Do While Not .EOF
If regx.Test(!field1) Then
Set foundMatches = regx.Execute(!field1)
For Each foundMatch In foundMatches
rsDst.AddNew
rsDst!Order = foundMatch.value
rsDst.Update
Next
End If
.MoveNext
Loop
.Close
End With
Set rsSrc = Nothing
rsDst.Close
Set rsDst = Nothing
Set db = Nothing
Set foundMatch = Nothing
Set foundMatches = Nothing
Set regx = Nothing
End Sub
Paste the code into a standard code module. Then position the cursor within the body of the procedure and press F5 to run it.
Upvotes: 1
Reputation: 112632
This loop
For Each foundMatch In foundMatches
OrderMatch = foundMatch.Value
Next
assigns all the results to the same variable OrderMatch
in turn, which always replaces the old value of OrderMatch
. Therefore the function will always return the last match.
If you want to return all the values, return an array for instance
Public Function OrderMatch(field As String) As String()
Dim results() As String
Dim i As Long
... get matches
ReDim results(0 To foundMatches.Count - 1) As String
If regx.test(field) Then
For i = 0 To foundMatches.Count - 1
results(i) = foundMatches(i).Value
Next
End If
OrderMatch = results
End Function
(I am currently working with Access XP, so I don't know whether the indexes go from 1 to Count or from 0 to Count-1.)
UPDATE
And always specify the return type of functions. This is more informative for people who want to use the function (including you if you have to resuse this function in 6 months) and prevents from supid coding errors. If really Variant
is meant, specify ... As Variant
so that your intention becomes clear.
Upvotes: 0
Reputation: 53653
This function is only returning one value because that's the way you have set it up with the logic. This will always return the last matching value.
For Each foundMatch In foundMatches
OrderMatch = foundMatch.Value
Next
Even though your function implicitly returns a Variant
data type, it's not returning an array because you're not assigning values to an array. Assuming there are 2+ matches, the assignment statement OrderMatch = foundMatch.Value
inside the loop will overwrite the first match with the second, the second with the third, etc.
Assuming you want to return an array of matching values:
Dim matchVals() as Variant
Dim m as Long
For Each foundMatch In foundMatches
matchValues(m) = foundMatch.Value
m = m + 1
ReDim Preserve matchValues(m)
Next
OrderMatch = matchValues
Upvotes: 0