Replacing Multi-Value Fields With A Join Table - ms-access

I'm moving on from multi-value fields due to my conversion to SQL Server for the back end. Unfortunately, I can't figure out how to replace it.
What I've done is created a many to many relationship between my "Opportunities" and "Purpose" tables utilizing a join table (one to many on each). This part was easy.
What I don't understand is how to then create a dropdown listbox (with check boxes to select the options) for the purpose.
I've found resources online pointing to the idea that I will need to use VBA, but have yet to find any actual examples. Is anyone familiar with how to do this?
Thanks in advance.

I had to do this for exactly the same reason; moving to a SQL Server back-end. My solution keeps the checkbox functionality but doesn't have them in a drop-down. You can put the check-boxes in a sub-form (that looks like a drop-down?) if you don't want a bunch of boxes on your main form.
For each checkbox you want to test for whether a record in the related table exists and add it if it doesn't when the box is checked, and the reverse when it's unchecked. The check-boxes must be unbound for this to work.
Here's the add/remove code. It goes in the "After Update" event:
Private Sub cb_1_AfterUpdate()
If Me.cb_1 = True Then
If DCount("Parent_ID", "Checkbox_records", "Parent_ID = " & Me.P_ID & "and Checked_box_num = 1") = 0 Then
strSQL = "INSERT INTO Checkbox_records (Parent_ID, Checked_box_num) VALUES (" & Me.P_ID & "," & "1)"
CurrentDb.Execute strSQL, dbFailOnError
End If
End If
If Me.cb_1 = False Then
If DCount("Parent_ID", "Checkbox_records", "Parent_ID = " & Me.P_ID & "and Checked_box_num = 1") > 0 Then
strSQL = "DELETE FROM Checkbox_records WHERE Parent_ID = " & Me.P_ID & " and Checked_box_num = 1"
CurrentDb.Execute strSQL, dbFailOnError
End If
End If
End Sub
The problem with unbound boxes is that they don't change when you switch records. So you have to set the boxes to reflect the data state when you change records, which you can do in the form's "On Current" event:
Private Sub Form_Current()
If DCount("Parent_ID", "Checkbox_records", "Parent_ID = " & Me.P_ID & "and Checked_box_num = 1") > 0 Then
Me.cb_1 = True
Else
Me.cb_1 = False
End If
End Sub
The thing I don't like about this solution is that you have to duplicate the code for each box, but it runs smoothly so it's livable.

Related

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

Passing query result to a textbox control on an access form

I need help with a textbox field on an Access 2007 form. I'm trying to insert the result of a query into the text box control on the form. This is used soley as information for the user. The form supplies the query with parameters to get the value. The query works fine and returns the correct result. What I can't seem to figure out is how to pass the query result to the textbox. I’ve tried several different ways but with no luck.
(PS> I know a combo box can do a lookup, however I don’t want the user to have to click the dropdown just to select the value as there can only ever be one value result from the query.) I'm open to suggestions as I'm not a programmer or DB Admin, but I've taking a few classes on Access (enough to be dangerous).
Private Sub cbo3_Change()
Me.tbx2 = ("SELECT tbl_Billing.Savings_b FROM tbl_Billing GROUP BY tbl_Billing.UBI_b, tbl_Billing.TaxYr_b, tbl_Billing.TaxPrg_b, tbl_Billing.Savings_b HAVING (((tbl_Billing.UBI_b)=forms!f1_UpBilled!cbo1) And ((tbl_Billing.TaxYr_b)=forms!f1_Upbilled!cbo2) And ((tbl_Billing.TaxPrg_b)=forms!f1_UpBilled!cbo3));")
End Sub
If you wish to do this in run time, you need to do the following, I take the controls you are referring to in this is on the same form.
The very simple and straight forward way to get it done is as follows,
Private Sub cbo3_Change()
Dim tmpRS As DAO.Recordset
Set tmpRS = CurrentDb.OpenRecordset("SELECT tbl_Billing.Savings_b FROM tbl_Billing GROUP BY " & _
"tbl_Billing.UBI_b, tbl_Billing.TaxYr_b, tbl_Billing.TaxPrg_b, " & _
"tbl_Billing.Savings_b HAVING ((tbl_Billing.UBI_b = '" & Me.cbo1 & "') And (tbl_Billing.TaxYr_b = '" & Me.cbo2 & "') " & _
"And (tbl_Billing.TaxPrg_b = '" & Me.cbo3 & "'))")
If tmpRS.RecordCount > 0 Then
Me.tbx2 = tmpRS.Fields(0)
Else
Me.tbx2 = 0
End If
Set tmpRS = Nothing
End Sub
Just note, I have implied all your combo boxes are returning String and the field you are comparing against are Text type. If that is not the case, you need to make changes accordingly.

Programmatically setting textbox value but returning run-time error 2115

I am trying to set the value of a text box based on the value I select in a combo box and a pre-existing value in another text box. Both the controls are in a continuous subform within a form. One key was to save the record OnDirty for Combo1, then execute the code to update TextBox1 AfterUpdate. Everything works, except that I get the following error message every time I change a value in Combo1:
Run-time error '2115':
The macro or function set to the BeforeUpdate or ValidationRule property for this field is preventing Database from saving the data in the field.
If I click 'End' on the error message, I'm fine. I have no validation rules on any elements on any of the tables connected to these forms. I am not using either the BeforeUpdate or ValidationRule properties on either the form or subform.
The code now looks like this:
Private Sub Combo1_Dirty(Cancel As Integer)
DoCmd.RunCommand acCmdSaveRecord
End Sub
Private Sub Combo1_AfterUpdate()
DoCmd.RunCommand acCmdSaveRecord
Dim con As ADODB.Connection
Set con = Application.CurrentProject.Connection
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
ssql = "(SELECT TABLE1.DESCRIPTION As d1 " & _
"FROM TABLE1 " & _
"INNER JOIN TABLE2 ON " & _
"(TABLE1.CATEGORY = TABLE2.CATEGORY) " & _
"AND (TABLE1.LEVEL = TABLE2.LEVEL) " & _
"WHERE " & _
"(((TABLE1.LEVEL)= " & [Forms]![MainForm].[Subform].Form.Combo1.Value & ") " & _
"AND ((TABLE2.CATEGORY)= '" & [Forms]![MainForm].[Subform].Form.[CATEGORY].Value & "'));)"
rs.Open ssql, con
Do Until rs.EOF = True
[Forms]![MainForm].[Subform].Form.TextBox1.SetFocus
[Forms]![MainForm].[Subform].Form.TextBox1.Text = rs.Fields!d1
rs.MoveNext
Loop
End Sub
When I click 'Debug', it highlights this line of code:
[Forms]![MainForm].[Subform].Form.TextBox1.Text = rs.Fields!d1
Again, neither the TextBox1 control or the data element underneath it have any Validation rules set, and my code is not using any BeforeUpdate (actually, not using that anywhere in the database). Any ideas why I'm getting an error, even though it's working otherwise?
Any help is greatly appreciated. Thanks!
Firstly, Dirty is called before BeforeUpdate, which is called before AfterUpdate,
so you don't need to save the record in Dirty and in AfterUpdate, I would just ditch the code for Dirty altogether since it's not really adding anything.
Try:
[Forms]![MainForm].[Subform].Form.TextBox1.Value = rs!d1
instead of
[Forms]![MainForm].[Subform].Form.TextBox1.SetFocus
[Forms]![MainForm].[Subform].Form.TextBox1.Text = rs.Fields!d1
Also, is there a particular reason you're looping through the whole recordset just to set one textbox? Does the textbox have a control source?
Wouldn't it be easier to change the subform's recordsource to rs?

MS Access combo box setting a record with two primary keys

I have a database with 2 primary keys, one for a LINE NUMBER and one for PHASE of construction. The reason for this is that we have projects that may use the same Line Number but must track several Phases of the project entirely seperatly. What I have is a combo box that will drive the record information on a form. This works fine, but now when I have more than one phase it will only bring up the line's first phase and not the other 4 phases. When something other than phas one is picked it results the first phase information.
Is there a way to tie a combo box with 2 fields to select the proper record based on both fields picked?
Or maybe I need to rething the way the form is brought up... Is there a better way to do this?
Code used to select the record:
Sub SetFilter()
Dim LSQL As String
LSQL = "select * from tblLineData_Horizon"
LSQL = LSQL & " where lineno = '" & cboSelected & "'"
Form_frmHorizon_sub.RecordSource = LSQL
End Sub
Private Sub cboSelected_AfterUpdate()
'Call subroutine to set filter based on selected Line Number
SetFilter
End Sub
Private Sub Form_Open(Cancel As Integer)
'Call subroutine to set filter based on selected Line Number
SetFilter
End Sub
A basic idea, but you'll most likely want to tweak the behaviour a bit and have some more checks. When the form loads, you only have the ability to select LineNo. When cbxLineNo has a value in it, it enables cbxPhaseNo for selection and upon selection, it changes the RecordSource of your subform.
Private Sub cbxLineNo_AfterUpdate()
If IsNull(cbxLineNo) Then
cbxPhaseNo.Enabled = False
Else
cbxPhaseNo.Enabled = True
cbxPhaseNo.RowSource = "SELECT PhaseNo FROM tblLineData_Horizon WHERE LineNo = " & cbxLineNo & ";"
End If
End Sub
Private Sub cbxPhaseNo_AfterUpdate()
If IsNull(cbxPhaseNo) = False And IsNull(cbxLineNo) = False Then
tblLineData_Horizon_sub.Form.RecordSource = "SELECT * FROM tblLineData_Horizon WHERE LineNo = " & cbxLineNo & " AND PhaseNo = " & cbxPhaseNo & ";"
End If
End Sub
Private Sub Form_Load()
cbxLineNo.Enabled = True
cbxPhaseNo.Enabled = False
cbxLineNo.RowSource = "SELECT LineNo FROM tblLineData_Horizon GROUP BY LineNo;"
End Sub
You question is a little unclear, but you can create a combobox with more than one column, then your select statement would be:
where lineno = '" & cboSelected.Column(0) & "' And otherfield='"& cboSelected.Column(1)&"'"
Go to the query behind your combobox by clicking into its RowSource property and clicking the Build button (...). Once you've added the columns you need, bring up the Properties for the query and set Unique Values to Yes, so that it doesn't repeat the combinations of the fields.
You will also need to change other properties of the combobox: 'Column Count' and 'Column Widths'.

Runtime query does not work.Why?

I have a Problem with my codes and it made me confuse.this is my scenario:
in my access Project I have a Form and subform that work together.I wanted to enhace my performance . I deceided to load my form and subforms in Runtime(from this article) .I put my query on my form code, in Form_load event like this:
Private Sub Form_load()
Me.RecordSource = "SELECT DISTINCTROW tb_bauteile.* " & _
"FROM tb_bauteile LEFT JOIN FehlerCodes_akt_Liste ON tb_bauteile.CDT = FehlerCodes_akt_Liste.CDT " & _
"WHERE (((FehlerCodes_akt_Liste.Steuergerät)='MEDC17'))" & _
"ORDER BY FehlerCodes_akt_Liste.Fehlerpfad;"
End Sub
but another problem occured with another controls in my form.when i click another control .this function should be run:
Private Sub subPfadFilter(Kombifeld As Variant, Obd As String)
Dim strKrit, strAuswahl, strSg As String
If (IsNull(Me.CDT) Or (Me.CDT = "")) Then
strAuswahl = "*"
Else
strAuswahl = Me.CDT
blnFiltOn = True
End If
.....
but it doesn't work and say me method or dataobjectt not found( without this if statement works the function works fine .the problem come from CDT ) if I put the query in Datasource in my form property that works properly
I cant understand the relation between these two things(putting the query on datasource of form property then working the if statement (CDT) good but when i put the query in Form load that does not work ) ,would you please help me if posible?have you any idee?
thank you so much for your helps
When using the "Me" reference, you need to ensure the code is behind the present form, you've mentioned that the Form_Load code is on there, but is the "subPfadFilter" somewhere else?
If so then it needs to either be moved on to this form, or change your reference to the forms name instead of "Me".
If it is on the form then the error seems to be complaining about the CDT item, you will need to confirm that this is the name of either a text field or a control on this form.
If it is then you can try inserting:
Me.Requery
After your SQL statement, this may wake up the forms reference to the object.
This bit is not essential but to avoid carrying out two tests on CDT and streamlining your code slightly I would amend to this:
If Me.CDT & "" = "" Then
strAuswahl = "*"
Else
strAuswahl = Me.CDT
blnFiltOn = True
End If
This captures both Is Null or "" in one shot.
UPDATE:
For an unbound form, you need to set your form's controls via VB as well as the forms recordsource e.g:
Private Sub Form_load()
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("SELECT DISTINCTROW b.* " & _
"FROM tb_bauteile As b LEFT JOIN FehlerCodes_akt_Liste As f " & _
"ON b.CDT = f.CDT " & _
"WHERE f.Steuergerät = 'MEDC17' " & _
"ORDER BY f.Fehlerpfad;")
rs.MoveFirst
Me.CDT = rs!CDT
'Any other fields go here
rs.Close
Set rs = Nothing
End Sub
However if you are returning more than one record at a time then you will either need to stick with a bound form or perhaps use a listbox to display your information. Unbound forms do not use the Forms "Recordset" property.
Note: I have also 'Aliased' the table names in your SQL statement, this is a good practice to get into : )