Access VBA - rs.findfirst using regex validation - ms-access

Hi all first time post.
I'm running a database that has to check data once imported in. I already use one findfirst that works fine which is just checking length of a string. Next I wanted to find any that didn't match a certain pattern in the same field.
Record set is simply
strSQL = "SELECT * FROM TblOrder ORDER BY ID"
Set rs = CurrentDb.OpenRecordset(strSQL, dbOpenDynaset)
The first find that works is;
rs.FindFirst ("Len(string) > 13")
If rs.NoMatch Then
MsgBox "All good"
Else
Do While Not rs.NoMatch
MsgBox "String over 13 - " & rs.AbsolutePosition + 1
rs.FindNext ("Len(string) > 13")
Loop
rs.FindNext ("Len(string) > 13")
End If
So that one is no problem but the one below is where I can't get it to work;
rs.FindFirst ("Validation(string, ""123\d{10}$"") = False")
If rs.NoMatch Then
MsgBox "All are correct pattern"
Else
Do While Not rs.NoMatch
rs.FindNext (Validation(string, "123\d{10}$") = False)
MsgBox "Must start with 978 - " & rs.AbsolutePosition
Loop
rs.FindNext (Validation(string, "123\d{10}$") = False)
End If
I've tried without quotes like on the findnext lines and with as per the findfirst. Without finds null value for the string and with gets a data type mismatch. The string is a number like 123456789 but stored in table as text. The validation works fine if I look through the recordset one row at a time like below;
If Validation(rs!string, "978\d{10}$") = False Then
but I want to scan the entire column rather than go line by line. Line by line was taking too long.
The validation function code is below;
Function Validation(ByRef StrInput As String, ByVal strPattern As String) As String
Dim regex As Object
Set regex = CreateObject("VBScript.RegExp")
With regex
.Pattern = strPattern
.IgnoreCase = True
.Global = False
.MultiLine = False
End With
If regex.Test(StrInput) = True Then
Validation = True
Else
Validation = False
End If
End Function

Don't worry about this I've fixed it. False needed to be in speech-marks.

Related

Access 2013, Extract number from field to use in search

Have a sales proposal access database for work that has a field that you can put a earlier corresponding proposal number as a reference. If you click on a button under that field it will take you directly to that earlier record. There are times we have a prefix in front of the number A-12345, E-12345 or it might just be 12345.
I need to be able to grab just the number without the letter and - for the search to work correctly. Thanks
Here is the image of my screen
Assuming you have a table with columns Proposal and Reference and a single form with controls txtReference and txtProposal, put this code to the On_Click event of your form button (I'm using DAO):
Dim strProposal As String
Dim i As Integer
Dim rs As DAO.Recordset
If Len(Nz(Me.txtReference, "")) < 1 Then
MsgBox "No reference number entered"
Else
For i = 1 To Len(Me.txtReference)
If IsNumeric(Mid(Me.txtReference, i, 1)) Then
strProposal = strProposal & Mid(Me.txtReference, i, 1)
End If
Next
End If
Set rs = Me.RecordsetClone
rs.MoveFirst
rs.FindFirst "Proposal = '" & StrProposal & "'"
If rs.NoMatch Then
MsgBox "Original proposal not found"
Else
Me.Bookmark = rs.Bookmark
Me.txtProposal.SetFocus
End If
rs.Close
Set rs = Nothing

Access VBA Filtering code Syntax Error issue

I have the code below (courtesy of allenbrowne, his info are in the code). When I am filtering using one of the columns (One_or_Two_Pearl) it is giving me a (Syntax error missing operator in query expression). I can not trace the problem as everything look fine. That column is defined as Text type and contains data (1, 2, NA). This problem is only occurring for this column, and when I debug the yellow mark is indicating the (Me.Filter = strWhere) part.
Thank you in advance for your help.
'http://allenbrowne.com/ser-62.html
'Purpose: This module illustrates how to create a search form, _
where the user can enter as many or few criteria as they wish, _
and results are shown one per line.
'Note: Only records matching ALL of the criteria are returned.
'Author: Allen Browne (allen#allenbrowne.com), June 2006.
Option Compare Database
Option Explicit
Private Sub cmdFilter_Click()
'Purpose: Build up the criteria string form the non-blank search boxes, and apply to the form's Filter.
'Notes: 1. We tack " AND " on the end of each condition so you can easily add more search boxes; _
we remove the trailing " AND " at the end.
' 2. The date range works like this: _
Both dates = only dates between (both inclusive. _
Start date only = all dates from this one onwards; _
End date only = all dates up to (and including this one).
Dim strWhere As String 'The criteria string.
Dim lngLen As Long 'Length of the criteria string to append to.
Const conJetDate = "\#mm\/dd\/yyyy\#" 'The format expected for dates in a JET query string.
'***********************************************************************
'Look at each search box, and build up the criteria string from the non-blank ones.
'***********************************************************************
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.cboxprojphase) Then
strWhere = strWhere & "([Project_Phase] = """ & Me.cboxprojphase & """) AND "
End If
'Another text field example. Use Like to find anywhere in the field.
If Not IsNull(Me.cboxcontract) Then
strWhere = strWhere & "([Contract] = """ & Me.cboxcontract & """) AND "
End If
'Number field example. Do not add the extra quotes.
If Not IsNull(Me.cboxdesigndpm) Then
strWhere = strWhere & "([Design_DPM] = """ & Me.cboxdesigndpm & """) AND "
End If
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.cboxadmupc) Then
strWhere = strWhere & "([ADM/UPC] = """ & Me.cboxadmupc & """) AND "
End If
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.cboxpearl) Then
strWhere = strWhere & "([One_or_Two_Pearl] = """ & Me.cboxpearl & """) "
End If
'***********************************************************************
'Chop off the trailing " AND ", and use the string as the form's Filter.
'***********************************************************************
'See if the string has more than 5 characters (a trailng " AND ") to remove.
lngLen = Len(strWhere) - 5
If lngLen <= 0 Then 'Nah: there was nothing in the string.
MsgBox "No criteria", vbInformation, "Nothing to do."
Else 'Yep: there is something there, so remove the " AND " at the end.
strWhere = Left$(strWhere, lngLen)
'For debugging, remove the leading quote on the next line. Prints to Immediate Window (Ctrl+G).
'Debug.Print strWhere
'Finally, apply the string as the form's Filter.
Me.Filter = strWhere
Me.FilterOn = True
End If
End Sub
Private Sub cmdReset_Click()
'Purpose: Clear all the search boxes in the Form Header, and show all records again.
Dim ctl As Control
'Clear all the controls in the Form Header section.
For Each ctl In Me.Section(acHeader).Controls
Select Case ctl.ControlType
Case acTextBox, acComboBox
ctl.Value = Null
Case acCheckBox
ctl.Value = False
End Select
Next
'Remove the form's filter.
Me.FilterOn = False
Me.OrderByOn = False
End Sub
Private Sub Form_BeforeInsert(Cancel As Integer)
'To avoid problems if the filter returns no records, we did not set its AllowAdditions to No.
'We prevent new records by cancelling the form's BeforeInsert event instead.
'The problems are explained at http://allenbrowne.com/bug-06.html
Cancel = True
MsgBox "You cannot add new Records to the search form.", vbInformation, "Permission denied."
End Sub
Private Sub Form_Open(Cancel As Integer)
'Remove the single quote from these lines if you want to initially show no records.
'Me.Filter = "(False)"
'Me.FilterOn = True
Dim strURL As String
Dim objIE As Object
Dim arrSites(2) As String
Dim i As Integer
arrSites(0) = "http://google.com"
arrSites(1) = "http://google.com"
Set objIE = CreateObject("InternetExplorer.Application")
For i = 0 To 1 Step 1
strURL = arrSites(i)
If i = 0 Then
objIE.Navigate strURL
Else
objIE.Navigate2 strURL, 2048
End If
Next i
objIE.Visible = True
Set objIE = Nothing
'objIE.Quit
End Sub
Private Sub optSortorder_AfterUpdate()
If Me.optSortorder = 1 Then
Me.OrderBy = Me.cboSortField
Else
Me.OrderBy = Me.cboSortField & " DESC"
End If
Me.OrderByOn = True
End Sub
Both your problems are here:
strWhere = Left$(strWhere, lngLen)
'For debugging, remove the leading quote on the next line. Prints to Immediate Window (Ctrl+G).
'Debug.Print strWhere
Uncomment the Debug.Print line to see what is happening:
it tries to remove a trailing " AND " from your [One_or_Two_Pearl] line, but there is none.

Access VBA function While...Wend or Do ...Loop

I am working on a function for my access database that fills in a form field in my task form automatically based on the data entered in products forms.
Function IsProductReceived(varID As Variant) As String
Dim rst As New ADODB.Recordset
Dim strSQL As String
Dim lngTOID As Long
Dim strReceiveDate As Date
Dim bAcceptable As Boolean
On Error GoTo ErrorHandler
If IsNull(varID) Then
IsProductReceived = "TBD"
Else
lngTOID = varID
strSQL = "SELECT tblProduct.TaskID, tblProduct.Received, tblProduct.Acceptable FROM tblProduct WHERE tblProduct.TaskID = " & lngTOID
rst.Open strSQL, CurrentProject.Connection, adOpenDynamic, adLockOptimistic
If rst.BOF And rst.EOF Then
IsProductReceived = "TBD"
Exit Function
Else
While rst.EOF = False
If rst![Received] <> "" Then
strReceiveDate = rst![Received]
bAcceptable = rst![Acceptable]
If IsDate(strReceiveDate) Then
If bAcceptable = False Then
IsProductReceived = "YES/NOT ACCEPTED"
Else
IsProductReceived = "YES/ACCEPTED"
End If
Else
IsProductReceived = "NO"
End If
Else
IsProductReceived = "NO"
End If
rst.MoveNext
Wend
End If
rst.Close
Set rst = Nothing
End If
Exit Function
ErrorHandler:
MsgBox Err.Description
Err.Clear
If rst.State = adStateOpen Then
rst.Close
Set rst = Nothing
End If
End Function
There is often more that one product forms related to the task form and products are received at different times. I want the "IsProductReceived = "no" to remain on the task form until ALL products related to the task are received.
This code seems to be working as long as the first product has not been received. I can seem to figure out how to make it remain "no" until all products are received.
I currently am using a while/wend, I have attempted a Do/loop but am still not having satisfactory results. Any help would be much appreciated
How about:
Function IsProductReceived(TaskID) As String
Dim product As New ADODB.Recordset
Dim sql As String
Dim countAll As Integer
Dim countReceived As Integer
Dim countAccepted As Integer
IsProductReceived = "TBD"
If Not IsNumeric(TaskID) Then Exit Function
sql = "SELECT Received, Acceptable FROM tblProduct WHERE TaskID = " & TaskID
product.Open sql, CurrentProject.Connection, adOpenDynamic, adLockOptimistic
While Not product.EOF
countAll = countAll + 1
If IsDate(product!Received) Then countReceived = countReceived + 1
If product!Acceptable Then countAccepted = countAccepted + 1
product.MoveNext
Wend
product.Close
If countAll = 0 Then
IsProductReceived = "No"
ElseIf countAll = countAccepted Then
IsProductReceived = "YES/ACCEPTED"
ElseIf countAll = countReceived Then
IsProductReceived = "YES/NOT ACCEPTED"
Else
IsProductReceived = "No"
End If
End Function
A few notes:
Indent your code better.
Drop the faux Hungarian notation, use descriptive variable names.
Avoid deep nesting, especially when it comes to determining the return value.
Check parameters and exit early if the check fails. This removes nesting depth from the function.
Avoid Variant parameter types unless the function must deal with different data types. Here an Integer or Long type would probably be a better fit. (Using a typed function parameter removes the need for a type check entirely.)
While x = False is an antipattern. Use While Not x.
No need to save recordset fields in local variables first. Just use them directly.
Avoid building SQL from string concatenation. After an IsNumeric() check the above is probably okay, but you really should use parameterized queries.
The issue I'm seeing with your code is that you're getting a record set from a table, looping through the set and testing "Recieved" and then assigning a return value for your function after each test. Effectively, you're just returning the value of the very last record in the recordset. Perhaps instead of setting the value of isProductRecieved inside the While loop, set a bool value to false whenever you encounter a product that hasn't been recieved and then set the return value of the function after the loop:
Dim receive As Boolean
Dim accept As Boolean
receive = True
accept = False
If rst![Received] <> "" Then
strReceiveDate = rst![Received]
bAcceptable = rst![Acceptable]
If IsDate(strReceiveDate) Then
If bAcceptable = False Then
accept = False
Else
accept = True
End If
Else
receive = False
End If
Else
receive = False
End If
So now, if "receive" makes it all the way to the end of your while loop, you know that each product is received but if any product was not received it would be set to false. You could also build a short circuit in there to make it a tiny bit faster.

Access VBA Object Required Error, referencing Field in a Table for IF statement

I am getting an object required error and because that is so horribly vague, I am having trouble fixing it. I am trying to say: If the AssocID field is blank for this record, go through with the SQL statement, etc. If it's not, display a MsgBox and go to the next record. Here is my code.
Private Sub StartButton_Click()
DoCmd.SetWarnings False
Dim mySQL As String
GetID = Forms!frm_MainMenu!AssocIDBox
CurRecord = Forms!frm_EC_All![Loan#].Value
mySQL = "UPDATE tbl_Data SET tbl_Data.[AssocID] = " & GetID & " , tbl_Data.[tsStartAll] = Now WHERE tbl_Data.[Loan#] = " & CurRecord
'
'
'
If IsNull(tbl_Data.AssocID.Value) Then
DoCmd.RunSQL mySQL
DoCmd.SetWarnings True
Me.TimerActivatedLabel.visible = True
Me.Refresh
ClaimedMsg = MsgBox("Loan has been assigned to you.", vbOKOnly, "Loan Assigned")
If ClaimedMsg = vbOK Then
DoCmd.SetWarnings False
DoCmd.RunSQL "UPDATE tbl_Data SET tbl_Data.[tsHaveDocs] = Now WHERE tbl_Data.[Loan#] = " & CurRecord
DoCmd.SetWarnings True
End If
Else
AlreadyAssignedMsg = MsgBox("Loan has already been assigned. Press OK to move to the next loan.", vbOKOnly, "Already Assigned!")
If AlreadyAssignedMsg = vbOKOnly Then
DoCmd.GoToRecord , , acNext
End If
End If
End Sub
The error occurs on the If IsNull(etc) line. I've looked at a lot of other stuff but it either didn't apply or I didn't really understand what they were talking about. I'm still kind of a beginner. Thanks!
If, for some strange reason, you don't have tbl_Data.AssocID field as a controlsource for a textbox in your form, you can do this:
...
' Declare a string variable and set it to the value of the field
' I concatenate an empty string to be sure to have not a null
Dim AssocID as string
AssocID = Dlookup("AssocID","tbl_Data","[Loan#] = " & CurRecord) & ""
' Test the string variable
If AssocID = "" Then
...

How would I make a form which searches for values in all tables of a database in access

I am trying to make a form which searches for the value inside all of the tables in the database (there are more than 1 table). The result will be displayed as the name of the table which this appears in. If someone can help me that will be nice.
In short, I have a form with a textbox and button. I enter the search string (for example 183939) and click on the button. It searches the value (183939) inside all the fields in the tables in the database, and if the value is found, then it displays the name of the table that it appears in. Thanks for the help.
I think this is a bad idea because it could take a very long time, and provide confusing results due to also searching system tables... but the following function will return an array of all table names containing the search term or nothing if it wasn't found. Calling example is such: theTables = containingTable("hello") where theTables is a variant. A limitation is that this will fail for multi-valued fields.
Function containingTables(term As String)
Dim db As Database
Dim tds As TableDefs
Dim td As TableDef
Set db = CurrentDb
Set tds = db.TableDefs
For Each td In tds
For Each f In td.Fields
On Error Resume Next
If DCount("[" & f.Name & "]", "[" & td.Name & "]", "[" & f.Name & "] LIKE '*" & term & "*'") Then
If Err.Number <> 0 Then
Debug.Print Err.Number, Err.Description
Err.Clear
On Error GoTo 0
Else
containingTables = containingTables & td.Name & ","
Exit For
End If
End If
Next
Next
Set tds = Nothing
Set db = Nothing
'Alternate Version
if Len(containgingTables) then containingTables = Left(containingTables, Len(containingTables) - 1)
'Original Version
'if Len(containgingTables) then containingTables = Split(Left(containingTables, Len(containingTables) - 1), ",")
End Function
To display the results with the alternate version, just use: Msgbox(containingTables(searchTerm)) where searchTerm is whatever you are searching.
Me as well i don't know why you would want to do something like that...
I think the solution posted by Daniel Cook is correct, i just took a slightly different approach. Do you need to match the exact value like I do? Anyway, here's my code:
Function searchTables(term as String)
Dim T As TableDef
Dim Rs As Recordset
Dim Result() As String
Dim Counter
Counter = 0
For Each T In CurrentDb.TableDefs
If (Left(T.Name, 4) <> "USys") And (T.Attributes = 0) Then
Set Rs = T.OpenRecordset
While Not Rs.EOF
For Each Field In Rs.Fields
If Rs(Field.Name) = term Then
Counter = Counter + 1
ReDim Preserve Result(Counter)
Result(Counter) = T.Name & "," & Field.Name
End If
Next
Rs.MoveNext
Wend
Rs.Close
End If
Next
If Counter = 0 Then
searchTables = Null
Else
searchTables = Result
End If
End Function
You should filter out duplicated values, in case the function matches multiple times the same filed in the same table.