I am trying to create a search form that can search from one table at a time based on the table selected by the user from a combo-box. I have a text box called searchCriteria and the combo-box is called TableList. The idea is that if the user wanted to search customer_table, she would select “customer_table” in the combo box, enter her search criteria in the text box, and press the search button. If she wanted to search order_table, she would select that table from the combo-box and so on. I would like the query to return all full records pertaining to the criteria entered. So, for example, if the user searched “James” from the customer table, I would like the full records (all fields) returned where the criteria “James” is present:
Company | First Name | Last Name | Account # | Sales Rep
Jammy Juice | James | Dan | 555 | Jim Halbert
Jameson Car Shop | Tom | Cramb | 556 | Dwight Smiles
Landon Refrigeration | Bob | McVance | 557 | James Bland
From my understanding, this will require some VBA to write a query…
I am assuming the query would be something like the following:
SELECT t.* FROM customer_table t WHERE (t.*)::text LIKE '%James%';
Where the string entered (James) and the table name (customer_table) are entered by the user.
Simply, I'm having trouble getting the form data (the table name and search text) into my query.
Does anyone know how to implement this in Microsoft Access 2010? Any insight would be appreciated.
The any sequence of characters wildcard in Access is * not %. If you want to search all fields in Access then you could concatenate all the fields:
WHERE [FirstName] & [Surname] & [City] & [Address]
LIKE "'*" & Forms!frmSearch!txtFind & "*'"
You might separate the fields with a distinct character, to prevent finding words that overlap between the fields.
Or, individually, but less efficient:
WHERE [FirstName] Like "'*" & Forms!frmSearch!txtFind & "*'"
OR [Surname] Like "'*" & Forms!frmSearch!txtFind & "*'"
OR [Address] Like "'*" & Forms!frmSearch!txtFind & "*'"
(I don't know where (t.*)::text came from, but it is not Access.)
So you will firstly need to discover all the field-names for the table they have selected. You can obtain the TableDef for the table-name they have selected and loop through its Fields collection to obtain the field-names. There are other ways to do this. For example, GetSchema.
Here is the LAZY way of doing this in Access 2010:
Create a continuous form
In the form Properties, set Navigation Buttons = Yes
When you open the form, you will see a Search box:
Type what you want to search for. Hit Enter to see further matches.
Notes:
This searches all fields displayed on the form
This does not restrict records to the search term
This is probably not a good idea for very large recordsets
There might be a need to do some user training for this feature
To refer text selected in combo-box
Me.TableList.Column(0)
To refer text in text box
Me.searchCriteria.Text
So your VBA command will look like
DoCmd.RunSQL("SELECT t.* FROM " & Me.TableList.Column(0) & " t WHERE (t.*)::text LIKE '%" & Me.searchCriteria.Text & "%'")
Related
I have an Access DB, along with tables and forms.
In one table I have customers and in a second table as a subdatasheet on the first table I have tags.
I have created a form which shows the customer list along with the subdatasheet which displays all the tags that a customer has.
I would like to filter based on a field of the main datasheet, and on the subdatasheet for a tag.
E.g. where customer_name = "Peter" and tag ="Neighbor"
I saw this code but when trying to use it fails.
Filter on Subdatasheet
When trying Set mainDS = Me.Controls(dataSheetName).Form where do I find the datasheetName?
I tried entering Form Name, Table Name, I searched in form properties but didn't manage to find a solution.
Based on your question it sounds like you are new to Access and you should review table normalization.
Everything starts with a normalized table structure. Unfortunately you are starting with the relatively difficult Many to Many Relationship. Each Customer can have many tags which is a One to Many Relationship, but there wouldn't be much point to comparing customers unless they could have the same tags, which means each tag can have many customers as well. For example, create your tables with the corresponding primary and foreign keys and tell access about the relationhip by hitting the ribbon and selecting the relationships tool:
The raw data in the CustomersTags table is what you are interested in, but it is not userfriendly
----------------------------------------------------------------
| CustomerTagID | CustomerID | TagID |
----------------------------------------------------------------
| 1 | 1 | 1 |
----------------------------------------------------------------
| 2 | 3 | 1 |
----------------------------------------------------------------
| 3 | 2 | 3 |
----------------------------------------------------------------
| 4 | 2 | 4 |
----------------------------------------------------------------
| 5 | 2 | 5 |
----------------------------------------------------------------
| 6 | 3 | 1 |
----------------------------------------------------------------
| 7 | 2 | 1 |
----------------------------------------------------------------
| 8 | 3 | 5 |
----------------------------------------------------------------
| 9 | 2 | 2 |
This is why we use forms for entering and viewing the data. For speeds sake select CustomersTags and hit create table:
This is not user friendly so we don't show primary keys and replace (right-click and select change to) all the i'ds with user friendly combo-boxes. I also change the forms format to Data sheet.
I continue making the form prettier and add two unbound combo boxes to the header to filter the form with. You can do this with listboxes or checkboxes, or etc as well. For instance a multi-select list box so you can select multiple tags. In all cases you just set the forms filter
Option Compare Database
Option Explicit
'select the combobox afterupdate property to get these events
'you can use macro's if you want
'the exact code is very dependent on things like whether you have default values. I just show the most necessary case
Private Sub cmbCustomer_AfterUpdate()
If (IsNull(Me.cmbCustomer) Or IsNull(Me.cmbTag)) Then
'do nothing
Else
'filter the forms record source
Me.filter = "(CustomerID = " & Me.cmbCustomer & ") AND TagID = " & Me.cmbTag
Me.FilterOn = True
End If
End Sub
Private Sub cmbTag_AfterUpdate()
If (IsNull(Me.cmbCustomer) Or IsNull(Me.cmbTag)) Then
'do nothing
Else
'the filter is just the WHERE PART of an sql statement without the where
Me.filter = "(CustomerID = " & Me.cmbCustomer & ") AND TagID = " & Me.cmbTag
Me.FilterOn = True
End If
End Sub
Next, I switch the forms format to continuous forms and continue prettyifying because to my surprise the datasheet format was blocking setting the filter string.
Here is some guidance for making comboboxes, comboboxes are used for displaying userfriendly values instead of IDs. For instance instead of displaying the raw CustomerID we display the Customer_name but the value in the combobox is the CustomerID. The unbound combobox's value is the CustomerID corresponding to the user friendly Customer_Name the user selects. Whether the combobox is bound or unbound you set them up in the same way. Select a bunch of columns from some table or query then declare how many columns you are showing and their width. To hide a column set it's width to 0. The order of the columns determines the order of the numbers. I bound to the tables and both tables go ID then user friendly description so column-widths is 0,1
This example allows selecting impossible combinations of Customer and tag which will cause an error. One way to avoid this would be to set the rowsource of the alternate combobox when you choose a value on the first. For instance, when you pick a customer you restrict the tags which can be selected in the next combobox. This is cascading comboboxes:
Option Compare Database
Option Explicit
Private Sub cmbCustomer_AfterUpdate()
'it helps to use the query designer to get the sql right
Me.cmbTag.RowSource = "SELECT Tags.TagID, Tags.FriendlyDescription" & _
" FROM Tags INNER JOIN (Customers INNER JOIN CustomersTags ON Customers.CustomerID = CustomersTags.CustomerID)" & _
" ON Tags.TagID = CustomersTags.TagID WHERE (((CustomersTags.CustomerID)= " & Me.cmbCustomer & "))"
Me.cmbTag.Visible = True
End Sub
Private Sub cmbTag_AfterUpdate()
Me.filter = "(CustomerID = " & Me.cmbCustomer & ") AND TagID = " & Me.cmbTag
Me.FilterOn = True
Me.cmbCustomer.SetFocus
Me.cmbTag.Visible = False 'quick and dirty reset 'can't invis a focused control
Me.Refresh
End Sub
The Situation
I maintain a legacy MS-Access database which is used to manage the contacts to a set of doctors offices. Initially the table "offices" (old) included information about the office and the employed doctors like this (abbreviated):
office
street
number
postcode
city
name1
surname1
name2
surname2
name3
surname3
An office could have between 1 and 3 doctors.
Initial Steps
A new requirement came up which asked for the separate modelling of offices and doctors as a 1:n Relationship.
I created a new table doctors:
name
surname
office_id
transformed the underlying data, dropped the redundant fields from the "office"-table and am now in the process of modifying the buisness logic accordingly.
First idea: Elegant or lazy? It fails any way
My first impulse was to create an "office" view which recreated the old "office" table and change the references to the "old" office table to this "office" view. This way I could keep the logic as it is and only adjust as needed.
The problem was, that the "office" table is very fundamental and used all over the place and i didn't manage to recreate the table without writing my own functions using a cursor making the view very costly. That the database is used in a Network environment didn't help because although this worked in principle, the .accdb file blows up to 70 to 80mb resulting in massive load times, making it in effect unusable.
Lets do it properly
So I ditched the "office" view and reworked the database to employ the new data model. A MailMerge Letter is given me particular trouble and i haven't found a good solution yet. The DataSource for the MailMergeDocument is a View relying basicly on the data in the "offices" and "doctors" table.
The issue
previously the Address field in word just used this as a datasource
id
street
postcode
city
name1
surname1
name2
surname2
name3
surnname3
3
Mainstreet
01234
timbuktu
Doe
John
Mae
Jane
Bar
Foo
Now i have an 1:n relationship and the datasource looks like this:
office_id
street
postcode
city
surname
name
3
Mainstreet
01234
timbuktu
John
Doe
3
Mainstreet
01234
timbuktu
Jane
Mae
3
Mainstreet
01234
timbuktu
Foo
Bar
However i still need to get all 3 (or in other cases just 2 or 1) Doctors on the adress field and yes there can be more than 1 office listed in the datasource.
Possible Solutions
I see basicly 2 Options:
Transform the view back into the previous format, without using Custom Functions, making it costly again, using a Single SQL-Statement.
Using the field functions within the word document to skip through the records as needed.
In theory the Nextif-function should do exactly that, however i haven't been able to figure out how and if I can evaluate Data between different records in the word document. I need something like:
Nextif nextrecord.id = currentrecord.id
but i havent found a way to reference the records whithin the fieldfunctions.
Any suggestions for a clean solution are welcome.
Regards
As is so often the way, after spending all this time posing my question, i have found a way to tackle the problem. I thought about solution 1 recreating the old table format without the use of VBA and came up with a lean way to do it.
The idea
If my doctors where numbered per office like this is doc no 1 in office no 6 etc i could simply count to three in the office table and refer to this unique DocID in a Join.
I need a view based on the office table that looks like this:
office_id
docid1
docid2
docid3
street
postcode
city
6
61
62
63
..
..
..
and a view based on the doctors table looking like this:
doctor_id
office_id
doc_id
name
surname
72
6
61
..
..
73
6
62
..
..
74
6
63
..
..
office view
This one is easy enough we take the office table as the basis for the view and simply add the following colums:
[office.id]*10+1 AS DocID1, [office.id]*10+2 AS DocID2, [office.id]*10+3 AS DocID3
doctors view
this one required a bit of googling but i found this forum post (#June7 is that also you in that thread?)
so we take the doctors table as the basis and add the following column:
[Office_ID]*10+DCount("1","Doctors","office_ID= " & [Office_ID] & " AND Doctors.ID <= " & [Doctors.ID]) AS DocID
This gives us the desired Doc ID and we can now create a third view based on these 2 where we add the doctors view 3 times and join it each time with docID1, docID2, docID3 respectively and each time add name and surname.
conclusion
It is possible to recreate the old table without using VBA and solely relying on built in functions and SQL. I expect this to be a reasonable fast way to achieve this although i haven't measured it.
I welcome comments and improvements but I am now reasonably happy with the result.
Thanks for your input.
For your original layout, the possibility of more than one entry could have been handled seamlessly via standard field coding in the mailmerge main document. For example:
Dear Dr {MERGEFIELD name1 \f " "}{MERGEFIELD surname1}{IF{MERGEFIELD surname2}<> "" "¶
"}{MERGEFIELD name2 \f " "}{MERGEFIELD surname2}{IF{MERGEFIELD surname3}<> "" "¶
"}{MERGEFIELD name3 \f " "}{MERGEFIELD surname3}¶
{MERGEFIELD office} {MERGEFIELD number} {MERGEFIELD street}¶
{MERGEFIELD city} {MERGEFIELD postcode}
For your 1:n relationship layout, you could save yourself a lot of effort and use Word's DATABASE field in conjunction with your mailmerge. For example, assuming you have a table of office_ids, with the addresses in that table:
Dear Dr {DATABASE \d "Filepath/MyDatabase.accdb" \s " SELECT [Name]&' '&[Surname] AS `Addressee` FROM [Doctors$] WHERE [office_id] = {MERGEFIELD ID}"}¶
{MERGEFIELD office} {MERGEFIELD number} {MERGEFIELD street}¶
{MERGEFIELD city} {MERGEFIELD postcode}
Note: The field brace pairs (i.e. '{ }') for the above examples are all created in the mailmerge main document itself, via Ctrl-F9 (Cmd-F9 on a Mac); you can't simply type them or copy & paste them from here. Nor is it practicable (for the most part) to add them via the standard Word dialogues. The spaces shown in the field constructions are all required. Instead of the ¶ symbols shown in the examples, you should use real line/paragraph breaks.
For more details, plus links to practical examples, see:
https://www.msofficeforums.com/mail-merge/21803-mailmerge-tips-tricks.html
https://www.msofficeforums.com/mail-merge/38721-microsoft-word-catalogue-directory-mailmerge-tutorial.html
I managed to enter data to a database via a form;
actually works like a charm.
Now, what I need, is a lookup function (preferably not a form), with which I can search a table on another worksheet.
Let's say, I have an edit field or a cell, in which I enter a term which shall be looked for in a certain column on the table in another worksheet.
I would like to get a list of all entries which contain the word and the value from another cell (an ID).
Example:
Search term: Tom
Table:
Tim | 2
Tom | 3
Tommy | 5
The List should Show Tom and Tommy and their respective IDs,
but everything I tried didn't turn out as intended (mostly didn't work at all)...
I'm currently making a database for a (fake) DVD Rental business for a school assignment.
I have created a table which links genre and DVD information, as well as separating any instances of two genres in one field. This looks like the following image:
I have created a query to contain the DVD names (linked by genre_ID) and the related genres, and that is shown below:
I have now moved the query into a form, shown here:
The combobox at the top is intended to be used for searching for genres, but I cannot figure out how to achieve that (preferably using VBA), so it will filter any searched for genres and show all films that match them (regardless of what field they are in).
I don't think the teacher will care too much that the database cannot handle films with 3 genres, so I won't need to change that.
This is only first problem appeared due bad database design. This is typical many-to-many relation and it would be better to use 3 tables instead of one.
But if you still need filtering by more than one field, just change RecorSource property in AfterUpdate event of combobox, WHERE clause should contain all fields with conditions connected by OR operator:
"WHERE genre1 = '" & Me.cboSearch & "' OR genre2 = '" & Me.cboSearch & "' OR genre3 = '" & Me.cboSearch & "'"
The same way you can change Filter property instead of RecordSource
You should have such configuration which allows you to add >=0 genres per movie.
+---------------+-------------+-------------------------------------+
| DVD table | Genre Table | DVD_VS_GENRE_TABLE |
+---------------+-------------+-------------------------------------+
| dvd_id | genre_id | DVD_ID(foreignkey to dvd table) |
| Name | genre | GENRE_ID(foreignkey to genre table) |
| other columns | | |
+---------------+-------------+-------------------------------------+
and then its the matter of your SQL code to filter
mSQL=Select tbl_dvd.name, tbl_genre.genre
from (tbl_dvd left join tbl_dvd_vs_genre on tbl_dvd.dvd_id = tbl_dvd_vs_genre.dvd_id) left join tbl_genre on tbl_dvd_vs_genre.genre_id = tbl_genre.genre_id
WHERE (genre like "*" & yourcombo.value & "*")
me.yoursubform.form.recordset := msql
and you can assign this code to your on change event which will filter while typing..
ps: haven't tested the code but you get the logic.
I'm trying to fill the combobox with values from a concatenated field in a MS Access query. The embedded image is what is currently shown in the drop down box and what is shown in the box when a value is selected.
The problem is that i do NOT want the values in the drop down box to show as if in columns, but rather as a concatenated string. So, instead of ... TAYLOR | AVICHAI ... it should be TAYLOR, AVICHAI. And additionally, when the value is selected, then instead of showing just TAYLOR it would show TAYLOR, AVICHAI.
I've tried every property I can think of and tried concatenating in the original table, the query and even in vba code AFTER just grabbing the two fields from the database.
Any help? Concatenated View
You need to concatenate the values together in your query and display that field in the combo box.
SELECT peopleID, lastName & ", " & firstName AS name FROM tblPeople
And then in your the format tab of your combobox set:
column count to 2
column widths to 0";1"
This will cause only your column with a width (the combined names) to be displayed in the drop down and when selected.
The documentation says: "In a combo box, the first visible column is displayed in the text box portion of the control."
More precisely, the value shown is the value of the first column with a non-zero width.
Thus, to achieve your goal, modify your query so that it returns the following:
Taylor, Avichai | Taylor | Avichai
Raines, Patricia | Raines | Patricia
...
Then, in the combox box properties, set
the number of columns to 3 and
the column widths such that the first column is very small (but not zero).