Updating Issues with MySQL and Access - mysql

So this is a problem which started happening a day ago.
I have an Access database file which stores a form for creating jobs, updating job sector, and deleting it from the MySQL table.
There are two tables that are used for this form: a local one stored in Access called "Job Route" and another through MYSQL ODBC Driver, ANSI 5.3 version called "To Do". The local table stores user-submitted data containing information on all job areas and state, while the MYSQL table only shows one job area at a time.
When a new entry is created, the text box details from the Access form are being stored onto both tables. Where each job contains up to 4 different sectors (e.g. [start date], [area1], [person in charge 1], [description1], ... [area4], [person in charge 4], [description4]). Whenever the data is being updated to its next state, in the local table only the job counter field is incremented, while every field in the MYSQL table called "To Do" is updated to its next state fields.
Connection to the server is good, and everything was running fine until an issue popped up in the updating function.
Basically how that function works is that on a listbox control, all current job data is being queried from the "To Do" table. The user selects an entry, and hits a button which loads the next sector information data from "Job Route", onto various textbox controls. The user can change those textbox inputs if they want - the only thing that is changed when the function runs is the "To Do". The information in "Job Route" remains the same. When the user hits the update button, the next sector field data is updated to "To Do", while only a counter in "Job Route" is being incremented to signify the current sector.
My problem is this. For the most part almost everything is running fine, but for one of the fields in "To Do" table does not update with the values it should from the textbox. So for instance if the textbox control was set to "Wyntile", the field name should be set to that, but for some reason a different value shows up instead, example="Apples". Here is the code:
Private Sub moveJob2_Click()
'get the job number
JobNum = Text31
CurrArea = DLookup("[Area]", "[To_Do]", "[Job_Number] =""" & JobNum & """")
area1 = DLookup("[Area1]", "[Job Route]", "[Job Number] =""" & JobNum & """")
area2 = DLookup("[Area2]", "[Job Route]", "[Job Number] =""" & JobNum & """")
area3 = DLookup("[Area3]", "[Job Route]", "[Job Number] =""" & JobNum & """")
area4 = DLookup("[Area4]", "[Job Route]", "[Job Number] =""" & JobNum & """")
'get what the current area is
Current = DLookup("[Current]", "[Job Route]", "[Job Number] =""" & JobNum & """")
'if the current area is the first area then check to make sure there is a second
'if so, then set the new area to it
If Current = 1 Then
If area2 = "---" Then
MsgBox area1 + " was the last area in the route. The job cannot be moved."
Exit Sub
End If
newArea = area2
ElseIf Current = 2 Then
If area3 = "---" Then
MsgBox area2 + " was the last area in the route. The job cannot be moved."
Exit Sub
End If
newArea = area3
ElseIf Current = 3 Then
If area4 = "---" Then
MsgBox area3 + " was the last area in the route. The job cannot be moved."
Exit Sub
End If
newArea = area4
Else
MsgBox area4 + " was the last area in the route. The job cannot be moved."
Exit Sub
End If
'set up link to both the To_Do and Job Route tables
Dim dbJobNumbers As DAO.Database
Dim rstJob As DAO.Recordset
Dim jobRoute As DAO.Recordset
Set dbJobNumbers = CurrentDb
Set rstJob = dbJobNumbers.OpenRecordset("To_Do")
Set jobRoute = dbJobNumbers.OpenRecordset("Job Route")
' >> Edit the job in the To_Do table
****' ERROR: Out of all these, only [Person_In_Charge] is being set to something
****' completely different from Text33, which wasn't changed by the user.
rstJob.FindFirst "[Job_Number]=""" + Text31 + """"
rstJob.Edit
rstJob("[Area]").Value = newArea
rstJob("[Person_In_Charge]").Value = Text33
rstJob("[Equipment]").Value = Text37
rstJob("[Description]").Value = Text35
rstJob.Update
'update the current area for the Job Route
jobRoute.FindFirst "[Job Number]=""" + Text31 + """"
jobRoute.Edit
jobRoute("[Current]").Value = CInt(Current) + 1
jobRoute.Update
'success message
MsgBox Text31 + " has been moved from " + CurrArea + " to " + newArea + "."
'requery the listboxes
Dim selectParas As String
selectParas = "SELECT [a].[Job_Number] as [Job Number], [a].[Description], [a].[Person_In_Charge] as [Person in Charge], [a].[Area] " & _
" FROM [To_Do] As [a];"
listRemoveJobs.RowSource = selectParas
listRemoveJobs.Requery
listChangeJobArea.RowSource = selectParas
listChangeJobArea.Requery
End Sub
The function has been running fine, and even now when I test it again it runs as programmed. Though today I recieved the "ODBC Insert on 'To Do' has failed" error, but that's for a different function. So I was thinking that something is wrong in the ODBC connection/MySQL table, but when I checked the table in phpmyadmin for the most part that table follows a similar format of other mysql tables used in Access.
Also to note, the person who had told me this issue runs on an old Windows XP version, where once before on that computer there were known issues of the defined OBDC ANSI 5.3 Driver instance completely disappearing from Access' Data Source list before. (The driver is still installed on Windows). That time, apparently the driver instance later re-appeared again magically in the D.S. list when that computer was restarted. ... I know this is rather long, but I can't seem to find the cause of why this updating error in Access is happening. Is there a known issue of ODBC having stability issues in connection? Why is the value changed to something completely different on update? Any insight would be appreciated.

While there is no reproducible example to help with your specific situation, consider running pure SQL UPDATE queries with binded parameters. Your area conditional logic can be rewritten into nested IIF expression. Possibly, this will simplify your problem and streamline your needs without DLookup or multiple recordset updates. Also, your re-assignment to RowSource is not necessary. Below utilizes parameterization, a best practice when running SQL in application layer:
SQL (save both below as Access queries)
mySavedJoinUpdateQuery
PARAMETERS Text33Param Text(255), Text35Param Text(255)
Text37Param Text(255), JobNumberParam Text(255);
UPDATE [To_Do] d
INNER JOIN [Job Route] r
ON d.Job_Number = r.Job_Number
SET [Area] = IIF([Current] = 1 AND [Area2] != '---', [Area2],
IIF([Current] = 2 AND [Area3] != '---', [Area3],
IIF([Current] = 3 AND [Area4] != '---', [Area4], [Area1)
)
),
[Person_In_Charge] = Text33Param,
[Equipment] = Text37Param,
[Description] = Text35Param
WHERE r.[Job Number] = JobNumberParam;
mySavedSimpleUpdateQuery
PARAMETERS JobNumberParam Text(255);
UPDATE [Job Route] r
SET r.[Current] = r.[Current] + 1
WHERE r.[Job Number] = JobNumberParam;
VBA
Private Sub moveJob2_Click()
Dim qdef As QueryDef
Dim selectParas As String
' UPDATE JOIN QUERY
Set qdef = CurrentDb.QueryDefs("mySavedJoinUpdateQuery")
qdef!JobNumberParam = Text31
qdef!Text33Param = Text33
qdef!Text35Param = Text35
qdef!Text37Param = Text37
qdef.Execute dbFailOnError
Set qdef = Nothing
' UPDATE SIMPLE QUERY
Set qdef = CurrentDb.QueryDefs("mySavedSimpleUpdateQuery")
qdef!JobNumberParam = Text31
qdef.Execute dbFailOnError
Set qdef = Nothing
' REQUERY LIST BOXES
listRemoveJobs.Requery
listChangeJobArea.Requery
End Sub

Related

Missing Data in Multiple instances of a report

I have an Access database that opens multiple instances of 2 reports. The quantity depends on how many student registrations is captured per training run. This may be up to 25, meaning that when the user click on the "Print Documents" button, 25 instances of the 2 reports are opened. The first report is a student registration form, and the second is a indemnity form. Both these forms has certain data on them, like the personal details.
Opening the multiple reports is no issue. The first report is opened with the below code:
Z = 1
While Not Rst3.EOF
Set rpt(Z) = New Report_Rpt_StudentReg
rpt(Z).Visible = True
rpt(Z).Caption = "Student Registation " & Rst3.StudentID
'Append it to the collection.
clnClient.Add Item:=rpt(Z), Key:=CStr(rpt(Z).hwnd)
Rst3.MoveNext
Z = Z + 1
Wend
In the report Rpt_StudentReg On Load, there are code running to with SQL statements to retrieve all the data and fill the fields on the report. The second report (Waiver) is opened from within the first report with similar code below:
Set rptWaiver(Z) = New Report_Rpt_Indemnity
rptWaiver(Z).Visible = True
rptWaiver(Z).Caption = "Indemnity " & Rst3.StudentID
'Append it to our collection.
clnClient.Add Item:=rptWaiver(Z), Key:=CStr(rptWaiver(Z).hwnd)
My issue is none of the data is dispalyed on any of the reports.
If I run a single instance, everything is fine and the relevant data is displayed.
Your assistance is appreciated.
Thanks
Deon
I am opening multiple instances with no issues. In the report's On Load Event, there is code running each time the report is opened. I use SQL to retrieve the data from the tables. Debugging the code, retrieves the correct data, but the data is not on the report when it is opened. The On Load event code as follows:
Private Sub Report_Load()
Set Dbs = CurrentDb: Set Dbs1 = CurrentDb
Select Case Mode
Case "WithData"
If MyMode = "Certification" Then
SQL1 = "SELECT StudentID FROM Tbl_Certification WHERE CertNumber = " & Rst3!CertNumber & ";"
Set Rst1 = Dbs1.OpenRecordset(SQL1)
SQL = "SELECT * FROM Tbl_StudentReg WHERE IDNumber = " & Rst1!StudentID & ";"
Set Rst = Dbs.OpenRecordset(SQL)
Rst1.Close
Else
SQL = "SELECT * FROM Tbl_StudentReg WHERE IDNumber = " & Forms!Frm_StudentReg_Edit.IDNumber & ";"
Set Rst = Dbs.OpenRecordset(SQL)
End If
Set Rst = Dbs.OpenRecordset(SQL)
Me.FirstName = Rst!FirstName
Surname = Rst!Surname
PrevSurname = Rst!PrevSurname
IDNumber = Rst!IDNumber
BirthDate = Rst!BirthDate
AltIDNumber = Rst!AltIDNumber
AltIDType1 = Rst!AltIDType
'-------------------
SQL1 = "SELECT * FROM Tbl_AltID WHERE AltID = " & Rst!AltIDType & ";"
Set Rst1 = Dbs1.OpenRecordset(SQL1)
AltIDTypeDescr1 = Rst1!AltIDDescr
'-----------------------------
Nationality1 = Rst!Nationality
SQL1 = "SELECT * FROM Tbl_Nationality WHERE NationalityNo = " & Rst!Nationality & ";"
Set Rst1 = Dbs1.OpenRecordset(SQL1)
NationalityDescr1 = Rst1!NationalityDescr
'-------------------------------
When the reports are opened for preview, there is no data in the reports. They are all blank.

Dynamic SQL generation is not supported against multiple base tables. - Trying to update Database

The list of problems with my software never seem to end! And once again, it's down to issues with SQL.
Previously, I'd asked on stackoverflow how to perform an inner join query (basically) - see my page for info - and now I have a new issue. I'm trying to update my "Students" database, however every time I do it returns with the error below:
Dynamic SQL generation is not supported against multiple base tables.
I'm not sure what I'm doing wrong, or how I go about fixing it. In my eyes, all I'm doing is trying to update the students table. The "inner join" part was for obtaining classes (see my last post for further clarification: Syntax Error (Missing Operator) In Query Expression ")
I've got the code for the relevant areas below:
Me.Validate()
Me.myDA.Update(Me.myDataSet.Tables("students"))
Me.myDataSet.AcceptChanges()
MsgBox("Registration Complete.", MsgBoxStyle.Information, "ASTRAS")
regiattendancedatagripview.Visible = False
reg_complete.Visible = False
reg_merit.Visible = False
reg_referral.Visible = False
reg_viewdetails.Visible = False
The above code is meant to update the students database that is being presented to me in a Datagridview. That datagridview has had information passed onto it from the "Classes" database, allowing it to display only the names required in the register. Code below:
If attendance_reg_periodComboBox.Text = "" Then
Else
Try
Dim con As OleDbConnection = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & Application.StartupPath & "\DATA.accdb")
Dim cmd As OleDbCommand = New OleDbCommand("SELECT *
FROM [classes] INNER JOIN [students] ON
classes.StudentForename = students.Forename AND
classes.StudentSurname = students.Surname
WHERE classes.TeacherName ='" & personloggedon.Text & "'
AND classes.Day ='" & System.DateTime.Now.DayOfWeek.ToString & "'
AND classes.Period ='" & attendance_reg_periodComboBox.Text & "'",
con)
con.Open()
myDA = New OleDbDataAdapter(cmd)
Dim builder As OleDbCommandBuilder = New OleDbCommandBuilder(myDA)
myDataSet = New DataSet
myDA.Fill(myDataSet, "students")
regiattendancedatagripview.DataSource = myDataSet.Tables("students").DefaultView
con.Close()
con = Nothing
If regiattendancedatagripview.Rows.Count = 0 Then
Label7.Visible = True
regiattendancedatagripview.Visible = False
Else
regiattendancedatagripview.Visible = True
regiattendancedatagripview.Sort(regiattendancedatagripview.Columns(2), System.ComponentModel.ListSortDirection.Ascending)
reg_complete.Visible = True
reg_merit.Visible = True
reg_referral.Visible = True
reg_viewdetails.Visible = True
End If
Catch ex As Exception
MsgBox("An unknown error occured while trying to obtain the class register. Error details below:" & vbNewLine & vbNewLine & ex.Message, MsgBoxStyle.Critical, "ASTRAS")
End Try
End If
Essentially - my understanding is that the names from "classes" are being used to search and return a list of names from "students" and so display them in a datagridview. So how come my program can't update the absences column (LastKnownAbsence)?

Access Tab Control - Set Pages Visibilty based on Subform Records

I know how to do this, but wondering if I might be able to write a more elegant solution. I have a form with a tab control. The control has 14 pages, each one, with it's own sub form. One of the pages (pgRequirements) has a subform of requirements, with a combo control "Requirement Type". It is a continuous form, so the user can add as many requirements as they want, for the main record.
There are 9 of those requirements, which have their own tab control page / sub form. I want to set visibility of those tab control pages, based on this parent's sub form requirements. So a current main record, can have multiple sub-requirement records. If any of those match e.g. requirement type A, than page A should be visible, otherwise it should not be.
I need this code to run anytime the main form is loaded, and the detail is made visible (meaning a main record has been chosen from a find form). Also anytime a requirement record is added or removed. The below is assuming that the parent-child links on the main form to subform will limit the requirement records, to just those that are for the current main record.
Here is the simple code, that will do the job, but is probably over-written:
If Me.FKRequirementType.Column(1) = "ReqType1" Then
Me.Parent!pgReqType1.Visible = True
Else
Me.Parent!pgReqType1.Visible = False
End If
If Me.FKRequirementType.Column(1) = "ReqType2" Then
Me.Parent!pgReqType2.Visible = True
Else
Me.Parent!pgReqType2.Visible = False
End If
If Me.FKRequirementType.Column(1) = "ReqType3" Then
Me.Parent!pgReqType3.Visible = True
Else
Me.Parent!pgReqType3.Visible = False
End If
If Me.FKRequirementType.Column(1) = "ReqType4" Then
Me.Parent!pgReqType4.Visible = True
Else
Me.Parent!pgReqType4.Visible = False
End If
Thanks!
EDIT
I turned this into a public function, so I can call it from anywhere. One problem. It's not working lol (small problem). I don't get any errors, but all the tab control pages are visible. When I add a new record, most of them should be hidden. I have a tblReqType table, with all the requirement types. I added a column to this, with the exact name of it's corresponding tab control page name, so I can loop through that table, for all records where that page name is not null, and set their page visible or not, based on the current main record ID having a record-requirement (cross reference table) record for each requirement type.
This is the public function I wrote. Can anyone help me understand what I'm missing in these loops for setting the visibility true (vtrue) vs setting the visibility false (vfalse)
Public Function ShowRequirements()
Dim db As DAO.Database
Dim strRstVTrue As String
Dim rstvTrue As DAO.Recordset
Dim strRstVFalse As String
Dim rstvFalse As DAO.Recordset
Dim strFieldName As String
'Setup the recordset
Set db = CurrentDb
strRstVTrue = "SELECT tblMRecordRequirements.ID, tblMRecordRequirements.FKMC, tblReqType.txtRequirementPage " & _
"FROM tblMRecordRequirements LEFT JOIN tblReqType ON tblMRecordRequirements.FKRequirementType = tblReqType.ID " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC = " & Nz(Forms!frmMRecords!ID, 0)
strRstVFalse = "SELECT tblReqType.ID, tblReqType.txtRequirementPage, tblMRecordRequirements.FKMC " & _
"FROM tblReqType LEFT JOIN tblMRecordRequirements ON tblReqType.ID = tblMRecordRequirements.FKRequirementType " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC <> " & Nz(Forms!frmMRecords!ID, 0)
Set rstvTrue = CurrentDb.OpenRecordset(strRstVTrue, dbOpenDynaset, dbSeeChanges)
Set rstvFalse = CurrentDb.OpenRecordset(strRstVFalse, dbOpenDynaset, dbSeeChanges)
strFieldName = "txtRequirementPage"
Do While Not rstvTrue.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvTrue.Fields(strFieldName)).Visible = True
Loop
Do While Not rstvFalse.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvFalse.Fields(strFieldName)).Visible = False
Loop
End Function
If anyone can help me figure out my stupidity, you deserve an up vote, a check mark, and a cookie.
EDIT again
Below is updated code for the public function. I fixed the rs for the true query, and I added in the MoveNext for the loops.
Public Function ShowRequirements()
Dim db As DAO.Database
Dim strRstVTrue As String
Dim rstvTrue As DAO.Recordset
Dim strRstVFalse As String
Dim rstvFalse As DAO.Recordset
Dim strFieldName As String
'Setup the recordset
Set db = CurrentDb
strRstVTrue = "SELECT tblMRecordRequirements.ID, tblMRecordRequirements.FKMC, tblReqType.txtRequirementPage " & _
"FROM tblMRecordRequirements LEFT JOIN tblReqType ON tblMRecordRequirements.FKRequirementType = tblReqType.ID " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC = " & Nz(Forms!frmMRecords!ID, 0)
strRstVFalse = "SELECT tblReqType.ID, tblReqType.txtRequirementPage, tblMRecordRequirements.FKMC " & _
"FROM tblReqType LEFT JOIN tblMRecordRequirements ON tblReqType.ID = tblMRecordRequirements.FKRequirementType " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC <> Is Null"
Set rstvTrue = CurrentDb.OpenRecordset(strRstVTrue, dbOpenDynaset, dbSeeChanges)
Set rstvFalse = CurrentDb.OpenRecordset(strRstVFalse, dbOpenDynaset, dbSeeChanges)
strFieldName = "txtRequirementPage"
Do While Not rstvTrue.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvTrue.Fields(strFieldName)).Visible = True
rstvTrue.MoveNext
Loop
Do While Not rstvFalse.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvFalse.Fields(strFieldName)).Visible = False
rstvFalse.MoveNext
Loop
End Function
EDIT REDUX
I think I may have it worked out, but let me know what you all think. I really appreciate all your thoughts on this, as I know you all have a lot of experience not just in figuring out these kinds of challenges, but ensuring the code is good and not prone to issues.
Here is where I am at:
Public Function ShowRequirements()
Dim db As DAO.Database
Dim db2 As DAO.Database
Dim strRstVTrue As String
Dim rstvTrue As DAO.Recordset
Dim strRstVFalse As String
Dim rstvFalse As DAO.Recordset
Dim strFieldName As String
strFieldName = "txtRequirementPage"
Set db = CurrentDb
Set db2 = CurrentDb
strRstVTrue = "SELECT tblReqType.txtRequirementPage " & _
"FROM tblReqType LEFT JOIN tblMRecordRequirements ON tblMRecordRequirements.FKRequirementType = tblReqType.ID " & _
"WHERE tblReqType.txtRequirementPage Is Not Null AND tblMRecordRequirements.FKMC = " & MCID
strRstVFalse = "SELECT tblReqType.txtRequirementPage " & _
"FROM tblReqType LEFT JOIN tblMRecordRequirements ON tblMRecordRequirements.FKRequirementType = tblReqType.ID " & _
"WHERE tblMRecordRequirements.ID Not In (Select ID From [tblMRecordRequirements] WHERE [tblMRecordRequirements]![FKMC] = " & MCID & _
") AND tblReqType.txtRequirementPage Is Not Null;"
Set rstvTrue = db.OpenRecordset(strRstVTrue, dbOpenDynaset, dbSeeChanges)
Set rstvFalse = db2.OpenRecordset(strRstVFalse, dbOpenDynaset, dbSeeChanges)
Do While Not rstvTrue.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvTrue.Fields(strFieldName)).Visible = True
rstvTrue.MoveNext
Loop
Do While Not rstvFalse.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rstvFalse.Fields(strFieldName)).Visible = False
rstvFalse.MoveNext
Loop
End Function
I need this code to run anytime the main form is loaded, and the detail is made visible (meaning a main record has been chosen from a find form). Also anytime a requirement record is added.
Put the code you shared inside a sub procedure and call the sub procedure from Form_Load(), Form_Current(), Form_AfterInsert() event handler, etc.
As for elegance, I'd focus on maintainability and efficiency rather than looks, but concise code is also nice. 1) You can use a With block to reduce redundant object method calls, but that'll only work for one reference at a time. 2) Instead create another variable to temporarily hold a value/object from a series of dot property-accessors. 3) It looks like pages and column values are already numbered with a consistent naming pattern, so leverage that in a loop. 4) Comparison operations in VBA are largely Boolean operations, so they return True or False. The result of an entire Boolean expression can be assigned to another Boolean variable/property. (Boolean operations can also return Null... which is usually, but not always, treated like False. If you're certain that your data doesn't have Null values, then you can simplify the code and ignore this issue. If the data can contain null, then you need to adjust the code appropriately.)
Me.Parent!pgReqType1 is calling the default property of the Parent form which is Controls, which default property is Item. The bang operator ! passes the code text as a string into the collection Item method. In short, it is equivalent to Me.Parent.Controls.Item("pgReqType1").
Dim i as integer
Dim ctls as Controls
Dim reqValue as string
Set ctls = Me.Parent.Controls
reqValue = Me.FKRequirementType.Column(1)
For i = 1 to 4
ctls.Item("pgReqType" & i).Visible = (reqValue = "ReqType" & i)
Next i
About all I can do is translate the specific code snippet you show. I have the feeling there is probably more to it than this, because the code snippet you shared ensures that there will only be one tab visible: It is testing the same column value multiple times which could only have one value. Bug? Incomplete example?
This really goes against my better judgement of Stack Overflow principles --to not answer multi-part, continuing debugging questions-- but I really want a cookie.
How could the posted code have ever worked, since you are not moving through either recordset? There are no MoveNext calls. That means that both recordsets are empty or an error is being thrown that is being ignored somewhere (i.e. On Error Resume Next). Otherwise, it should lock up Access with infinite loops. Sometimes you can stop the code with Ctrl+Break, but not always successful in Access.
More precise table schema is required to properly interpret your data, but I'll make some assumptions. You state that tblReqType contains all requirement types. I'll assume that tblMRecordRequirements contains rows only for requirements which are "applied" (a.k.a. "on", "selected") for the ID value in tblMRecordRequirements.FKMC. Assuming the converse, if there are no rows in tblMRecordRequirements with ID in tblMRecordRequirements.FKMC for a given tblMRecordRequirements.FKRequirementType, then the requirement is not "applied" to that ID.
Does every row in tblReqType have a value in txtRequirementPage, or do some rows have null values? Also, can multiple requirements have the same page specified? Or is it a true one-to-one requirement-to-page mapping with no null values?
First of all why would the first query not be an INNER JOIN, since I assume that only records that match in both tables should be returned for the Visible = True condition? Depending on your answers above, this would probably make the condition tblReqType.txtRequirementPage Is Not Null unnecessary in the first query.
Simply reversing a LEFT JOIN will not return what you want especially if you select all other ID values ( tblMRecordRequirements.FKMC <> Nz(Forms!frmMRecords!ID, 0) ). All that does is give you the requirements for every other ID values. Not only will that be inefficient since it could return many, many irrelevant records, it could be likely that over all other ID values that every possible requirement would be applied, so that the second query will essentially cause all requirements to be invisible.
Further picky observations:
If Forms!frmMRecords!ID is null, then you might as well not even execute the queries. You should check that value for null separately and perform appropriate actions rather than letting that specific condition fall through the other code, even if the end side effect is what you desire. It makes the code harder to debug and interpret properly. In other words write code that does "If ID is null, set all pages to visible = false, then exit the sub (i.e. skip other code)"
It's more efficient to get a read-only snapshot rather than an fully-updatable Dynaset recordset: Too much overhead just for looping through without data manipulation.
Proper break points, debug output and error handling code can help identify bad code. It is worth tracing through results of recordsets manually to inspect values and proper SQL syntax.
Try this:
Public Sub ShowRequirements()
Dim db As DAO.Database
Dim iID As Long '? Assuming long integer
Dim strSQL As String
Dim rsTabs As DAO.Recordset
On Error Resume Next
iID = -1 '* Set to bogus value
If Not IsNull(Forms!frmMRecords!ID) Then
iID = Forms!frmMRecords!ID
End If
If iID = -1 Or Err.Number <> 0 Then
'* Problem accessing ID control on form (empty recordset, new record row, etc.)
'* or it is null
'Set all tab pages to Visible = False?
Exit Sub
End If
On Error GoTo Catch
'* Setup the recordset
Set db = CurrentDb
'* Use embedded query (replacable with saved query) for filtering on ID values.
'* This is critical so that the LEFT JOIN does not return or filter records
'* based on other ID values.
strSQL = _
"SELECT tblReqType.ID, tblReqType.txtRequirementPage, (IDReq.FKRequirementType Is Not Null) As ShowTab " & _
" FROM tblReqType LEFT JOIN" & _
" (SELECT MReq.FKRequirementType FROM tblMRecordRequirements AS MReq " & _
" WHERE MReq.FKMC = " & iID & ") AS IDReq" & _
" ON tblReqType.ID = IDReq.FKRequirementType" & _
" WHERE tblReqType.txtRequirementPage Is Not Null"
Set rsTabs = db.OpenRecordset(strRstVTrue, dbOpenSnapshot, dbReadOnly)
Do While Not rsTabs.EOF
Forms!frmMRecords.tbMRecordSubs.Pages(rsTabs!txtRequirementPage).Visible = rsTabs!ShowTab
rsTabs.MoveNext '* Advance recordset. (Avoid infinite loops.)
Loop
CloseAll:
On Error Resume Next
'* Best habit to explicitly close recordsets and database connections, even when not necessary (since they'll close automatically when they go out of scope)
If Not rsTabs Is Nothing Then
rsTabs.Close
Set rsTabs = Nothing
End If
Set db = Nothing
Exit Sub
Catch:
'* At least report error for development
Debug.Print "ShowRequirements(): Error: " & Err.Number & ": " & Err.Description
'* Show MsgBox or update form status control?
'* Set all tab pages to Visible = False?
'* Form state could be misleading without proper user notification and/or error handling
Resume CloseAll
End Sub

Warning for Dynamic Combobox list

I have a combobox that builds it's list upon first usage. I know that the way I want "NotInList" to behave isn't conventional - I don't want to waste adding the item to a table separate from the needed entry, but I'd like to still warn about an item that hasn't been used yet, so that the user has to think twice before accepting the entry.
Once the user adds the item, it will automatically appear in the list next time because the data source for the combo box is as follows:
SELECT tbl_SP.PROGRAM
FROM tbl_SP
GROUP BY tbl_SP.PROGRAM
HAVING (((tbl_SP.PROGRAM) Is Not Null And (tbl_SP.PROGRAM)<>""));
I tried this:
Private Sub cmbPROGRAM_NotInList(NewData As String, Response As Integer)
If MsgBox("'" & Chr(34) & NewData & Chr(34) & " hasn't been used yet. Add to list? ", vbQuestion + vbYesNo, "Add - " & NewData & "?") = vbYes Then
Response = acDataErrAdded
End If
End Sub
but of course, Access wants the item to actually exist before it will release the error. And...if I set LimitToList to "No" then the user doesn't get a warning.
How can I achieve this behavior?
Ok, I tried this which works fine if the user selects YES, but becomes more complicated when the user selects "NO"
Public Function ReturnsRecords(strSQL As String) As Boolean
Dim d As DAO.Database
Dim arr(1 To 3) As DAO.Recordset
'Dim rs As DAO.Recordset
'assume 3 items in array above
Set d = CurrentDb
Set arr(1) = d.OpenRecordset(strSQL)
' MsgBox "Record Count is " & arr(1).RecordCount
If arr(1).RecordCount > 0 Then
ReturnsRecords = True
Else
ReturnsRecords = False
End If
Set d = Nothing
End Function
Private Sub cmbPROGRAM_BeforeUpdate(Cancel As Integer)
Dim strSQL As String
strSQL = "Select * from LU_PROGRAM where PROGRAM ='" & Me.cmbPROGRAM & "'"
If ReturnsRecords(strSQL) = False Then
If MsgBox("'" & Chr(34) & Me.cmbPROGRAM & Chr(34) & " hasn't been used yet. Add to list? ", vbQuestion + vbYesNo, "Add - " & Me.cmbPROGRAM & "?") = vbNo Then
Cancel = True
' how do I reset this? Me.cmbPROGRAM.Text = Null
End If
End If
End Sub
How do I clear the combobox if the user selects NO? If I select me.undo, that will undo all of the entries, but I just want to clear the combobox.
Incidentally, the form is totally unbound and doesn't accept an entry until the user selects "Save"
First, I'm not quite sure what you wish to achieve ...
Then, educate the users to press Escape to cancel. This is mandatory wisdom when operating an Access application.
For your code to work, you can't change the content of a control in the BeforeUpdate event. So try the AfterUpdate event with either:
Me!cmbPROGRAM.Text = ""
or:
Me!cmbPROGRAM.Value = Null

Concatenating Null values in DCount query that compare to corresponding concatenated table fields

Using Access 2007.
I have to compare several data entry fields with their corresponding tables fields. If the fields match, do not add a new record. If not, add a new record with the values.
AnimalInfo Table fields
WHno (Wildlife Health Number)
Species
LETClr1 (Left Ear Tag Color 1)
LETNo1 (Left Ear Tag Number 1)
LETClr2 (Left Ear Tag Color 2)
LETNo2 (Left Ear Tag Number 2)
RETClr1 (Right Ear Tag Color 1)
RETNo1 (Right Ear Tag Number 1)
RETClr2 (Right Ear Tag Color 2)
RETNo2 (Right Ear Tag Number 2)
Form F_HotelForm unbound fields
txtSpecies
txtLETClr1
txtLETNo1
txtLETClr2
txtLETNo2
txtRETClr1
txtRETNo1
txtRETClr2
txtRETNo2
I am trying to create a DCount to check and see if there are any matching records. The animal's uniqueness is determined by its species and ear tag information. It can have one ear tag number and color, or four. (Some of the older data has none but I can't do anything about that! In those cases, a new record, i.e. new Wildlife Health Number will be generated)
This is what I want to accomplish with this form:
If there are no matching fields (DCount = 0) add a new record and update fields from the form into table.
If there is 1 matching record, then the animal's wildlife health number is displayed (in a new form eventually)
If there are multiple records, then these are displayed in another form and the user needs to pick the correct animal.
LETClr1 and LETNo1 are paired.
LETClr2 and LETNo2 are paired.
RETClr1 and RETNo1 are paired.
RETClr2 and RETNo2 are paired.
Any and all of these fields could have values or not. Left ear tag numbers and colors could have been entered as either LETClr1 or LETClr2 so I have to compare both LETClr1 and LETClr2 with the txtLETClr1 data entry. (This holds true for all paired fields)
Below is a sample of the script so far. It is very rudimentary as I am very new to this and am just trying to see what works.
Private Sub GenerateWHno_Click()
Dim rs As DAO.Recordset
If IsNull(Forms!F_HotelEntry!txtSpecies) Or (Forms!F_HotelEntry!txtSpecies) = "" Then
MsgBox "Species is a required field. Please enter a species"
Exit Sub
End If
MsgBox txtSpecies
SpeciesCount = DCount("[Species]", "AnimalInfo", "[Species]= '" & txtSpecies & "'AND [L_ET_Color1]='" & txtL_ET_Color1 & "' AND [L_ET_No1]='" & txtL_ET_No1 & "'")
If SpeciesCount > 1 Then
MsgBox SpeciesCount & " Greater than 1"
ElseIf SpeciesCount = 0 Then
MsgBox "You need a new WHno"
WHno = Nz(DMax("WHno", "AnimalInfo")) + 1
MsgBox WHno
Set rs = CurrentDb.OpenRecordset("AnimalInfo")
rs.AddNew
rs!WHno = WHno
rs!Species = txtSpecies
rs!L_ET_Color1 = txtL_ET_Color1
rs!L_ET_No1 = txtL_ET_No1
rs!R_ET_Color2 = txtR_ET_Color2
rs.Update
rs.Close
Else
End If
Forms!F_HotelEntry!txtSpecies = ""
Forms!F_HotelEntry!txtL_ET_Color1 = ""
Forms!F_HotelEntry!txtL_ET_No1 = ""
End Sub
So the problem is that I cannot concatenate NULL fields. The DCount only works if there is Non Null data in the form/table.
Any ideas as to how I can work around this?
Many thanks.
My comments are getting garbled so I am putting below original posting.
I copied the suggested code into module and rewrote query a couple of different way but still got error message: Run-time error 424. Object required
SpeciesCount = DCount("[Species]", "AnimalInfo", "[Species] = txtSpecies AND (is_null([L_ET_Color1],"""") = is_null(txtL_ET_Color1,""""))")
SpeciesCount = DCount("[Species]", "AnimalInfo", "[Species] = '" & txtSpecies & "'AND is_null([L_ET_Color1],"") ='" & is_null(txtL_ET_Color1, "") & "' AND [L_ET_No1]='" & txtL_ET_No1 & "'")
I have been tinkering with this for 3 hours and am no closer to a solution. What am I doing wrong?
In a module create the following function:
public function is_null(ctl as variant, nullreplace as string) as variant
if ctl is null then
is_null = nullreplace
else
is_null = ctl
end if
end function
In your query change your reference to a given field, yourField to is_null(yourField, "").
In a string enclosed in double quotes, this will need to be is_null(yourField, """") (it needs a double-quote to escape a double-quote, so 4 is 2 internally)