Search and filter in MS Access forms - forms

Background: I have a table named 'SNR_log', out of which I have queried to obtain below list.
Next,I designed a split form, with a text box.In this text box SNR is enterted to find and filter from the list. To perform this operation 'Apply filter' macro is used with a where condition,
[SNR] Like "*" & [Forms]![SNR_History]![Text17] & "*"
Problem: Is it possible to filter by inserting SNR in the textbox and obtaining the below result.
In other words, with SNR as input, display UID and all the SNR under that UID.

You're not going to be able to do this in an "Apply Filter" macro. You're going to need some VBA behind the scenes.
The first thing you want to do is query your data for the UID. You would do that like this:
Dim db as Database
Dim Rec as Recordset
Dim UIDx as Integer
Dim strSQL as String
Dim strSQL2 as String
Set db = CurrentDB
strSQL = "SELECT UID FROM SNR_Log " & _
"WHERE [SNR] Like ""*" & [Forms]![SNR_History]![Text17] & "*""
Set Rec = db.OpenRecordSet(strSQL)
'Now grab the UID that's returned and put it inside a variable
UIDx = Rec(0)
Now you have your UID that you want to query on. So set up your RecordSource based on that:
strSQL2 = "SELECT * FROM SNR_Log WHERE [UID] = " & UIDx & ""
Forms!Me!frmSubFormName.RecordSource = strSQL2
Forms!Me!frmSubFormName.ReQuery
That will change the record source of the form, and refresh the form so it's looking at the current record source you just set it to.
You may have to putz with strSQL a bit, I can't remember exactly how to work with the Like operator inside a SQL string. I'm pretty sure it's correct, though.

I was able to work around it in a simple way. I created a main form and subform. The record source for subform is the text box where SNR is entered. Once a command button is clicked with following event procedure in it I obtained the desired result;
Private Sub Command4_Click()
Text0.SetFocus
Me.SNR_log_Subform.Form.RecordSource = "Select * from SNR_Log where UID in(Select UID from SNR_Log where SNR like '*" & Text0.Text & "*')"
End Sub

Related

Access VBA Filtering form and charts by a combo box list

I have a form that has charts and textboxes and a list, the form has no control source.
and I have one combobox that has date list, based on that list I want to filter the data on the form and reload the charts and text boxes to view the filtered data by the selected date.
I reached up to this point but unfortunately it's not filtering
my code on the after update event
Private Sub cmbFindCustomer_AfterUpdate()
'declare variables
Dim sFilter As String
'in this case, the ID is text so the ID value
'needs to be wrapped in single quotes.
sFilter = "[Date]= '" & Me.cmbFindCustomer & "'"
'assign the filter value,and turn filtering on
Me.Filter = sFilter
Me.FilterOn = True
Me.Recalc
Forms!Main.Requery
Forms!Main.Repaint
End Sub
A date field is not text but Date, and a combobox always returns text, so you need to convert the selected value and format it as a date value expression:
sFilter = "[Date] = #" & Format(Me!cmbFindCustomer.Value, "yyyy\/mm\/dd") & "#"

How to search and display the fields from a table in an editor widget using progress 4gl

Accept a customer number and then output the details of each order and items to an editor widget.
Here customer , order ,order-line and items are table names.
where as customer and order tables have cust-num as common field , order and order-line(table-name) and order have order-num as common field , order-line and item have item-num as common field.
now i have to use a fill-in (f1 is object name) to get the cust-num and use a search button (search1 as object name) to find the corresponding fields and display them in the editor widget ( editor-1 as object name).
define temp-table ttcustomer
field custnum like customer.cust-num
field cname like customer.name
field orders like order.order-num
field items like item.item-num
field itemname like item.item-name .
find first customer WHERE customer.cust-num = input f1 NO-LOCK .
create ttcustomer .
assign
ttcustomer.custnum = customer.cust-num
ttcustomer.cname = customer.name.
for each order WHERE Order.cust-num = input f1 NO-LOCK .
assign
ttcustomer.orders = order.order-num.
for each order-line where order-line.order-num = order.order-num no-lock.
for each item where item.item-num = order-line.item-num no-lock.
assign
ttcustomer.items = item.item-num
ttcustomer.itemname = item.item-name.
end.
end.
end.
i have written this code in search button .
how can i display the temp-table ttcustomer in editor widget please help me :)
You'd probably be better off using a browser instead of an editor. But if you are going to use an editor, this should give you what you need:
DEFINE VARIABLE edData AS CHARACTER NO-UNDO.
FOR EACH ttcustomer:
edData = edData + ttcustomer.items + ", " + ttcustomer.itemname + CHR(10).
END.
editor-1:SCREEN-VALUE = edData.
The editor is just text so you won't be able to do any record selection/manipulation like you can with a browser. And if you have many ttcustomer records, you run the risk of overflowing the 32k character string size limit.

MS Access: How can I make this textbox automatically fill in a value based on a query?

I have 5 textboxes on a form.
job (text)
suffix (integer)
production date (date)
shift (text)
start time (time)
The user fills out these boxes and then submits the form to write to a table. The user does this multiple times per shift using the same information.
The problem: Sometimes the user forgets to fill out the 5th textbox, so I want to simply check their previously submitted values in the table and fill in the textbox with the max start time.
What I tried: I made a query to take the first 4 textboxes as critera and then return the max start time. The query works, but I can't get it to show up by default on the form. I used the default value property to point to the correct value in the query, but nothing shows up.
The goal: The 5 textboxes should be blank at first (which they are, currently). After the user fills out the first 4 textboxes, the 5th box should populate with the max start time.
You need to add AfterUpdate code for 4 fields, which should check value of all 4 first fields and if they are not empty, fill 5th textbox using DLookup from your query.
For instance you can call the function like this from all AfterUpdate events:
Private Function CheckData()
If Nz(Me.job, "") <> "" And Nz(Me.suffix, "") <> "" _
And Nz(Me.[production date], "") <> "" And Nz(Me.Shift, "") <> "" Then
Me.[start time] = DLookup("start time", "MyQuery", "job='" & Me.job & _
"' and suffix=" & Me.suffix & " and [production date]=#" & _
Format(Me.[production date], "yyyy/mm/dd") & "# and Shift='" & Me.Shift & "'")
End If
End Function

Creating Database Table From Temp-Table (by Code)

I have a temp-table called tt. I want to create a database table with the same field names and types using my temp-table.
I can't figure out how to do in Progress-4gl. Is this possible ?
Thanks.
Short answer: yes
The safest way is to do this by code is to create an incremental df and then load this.
Here is a very partial start that should get you going:
DEFINE TEMP-TABLE tt NO-UNDO
FIELD ii AS INT
FIELD cc AS CHAR
INDEX ttix IS UNIQUE PRIMARY ii.
DEF VAR hb AS HANDLE NO-UNDO.
DEF VAR hf AS HANDLE NO-UNDO.
DEF VAR ifield AS INT NO-UNDO.
hb = TEMP-TABLE tt:DEFAULT-BUFFER-HANDLE.
OUTPUT TO "tt.df".
PUT UNFORMATTED SUBSTITUTE( "ADD TABLE &1", QUOTER( hb:NAME ) ) SKIP.
DO ifield = 1 TO hb:NUM-FIELDS:
hf = hb:BUFFER-FIELD( ifield ).
PUT UNFORMATTED
SUBSTITUTE(
"ADD FIELD &1 OF &2 AS &3",
QUOTER( hf:NAME ),
QUOTER( hb:NAME ),
hf:DATA-TYPE
) SKIP.
/* to do: add other field attributes like label, initial value, decimals, format */
END.
/* to do: add indices */
OUTPUT CLOSE.
The resulting df can be loaded with:
RUN prodict/load_df.p ( "tt.df" ).
While this might be possible it's not a very good idea. Chances are you will corrupt your database. This might lead to data loss. You are much better off sticking to the provided tools even if they might have some problems.
Backup first
Always back up before applying any changes!
Single Use Database
You can modify a single usage database (if you're locally connected) online directly in the Data Dictionary. This is quite straight forward. Just use the GUI to add the table, it's fields and indices. Make sure you commit your changes.
Multi User Database
A multi user database can be changed offline or (a little bit more limited) online. Adding a new table with corresponding fields and indices is a change that can be made online. Adding the changes offline is done exactly in the same way as the "Single Use Database".
Online changes requires more work:
First of all you need two connected databases. I'll call them "live" and "development" in this case. "Development" will consist of a perfect copy of "live" when it comes to the schema. It doesn't have to have any data in it. The databases can have the same name - then you'll use a "logical name" alias when connecting to one of them.
Make the changes in "development" just like you would in an offline database or single usage database (use Data Dictionary). Make sure you commit your changes.
Enter the Data Administration Tool. Make sure you have "development" selected as default database. Go to the Admin -> Dump Data and Definitions -> Create Incremental .df file. Select your "live" database as comparative database and select a good file name (delta.df is default).
The file created is a diff-file between your "live" and "development" databases. It's wise to check it to make sure you're not dropping any tables or anything like that by mistake.
Now select the "live" database as default and go to Admin -> Load Data and Definitions. Here you can choose to add the changes offline (default) or online (check 'Add new objects on-line').
Some schema changes will require you to compile your source code again!
Using the example of #Stefan Drissen, and done some modification.
{temp-tables.i}
def input param table-handle tt.
def var hb as handle no-undo.
def var hf as handle no-undo.
def var ifield as int no-undo.
def var vindex as char no-undo.
def var vi as int.
hb = tt:default-buffer-handle.
output to value("/home/user/teste.df") append.
put unformatted substitute( "ADD TABLE &1", quoter(hb:name)) skip.
DO ifield = 1 to hb:num-fields:
hf = hb:buffer-field( ifield ).
put unformatted
substitute(
"ADD FIELD &1 OF &2 AS &3",
quoter( hf:name ),
quoter( hb:name ),
hf:data-type
) skip.
put unformatted
substitute(
"FORMAT &1",
quoter(hf:format)
) skip.
put unformatted
substitute (
"LABEL &1",
quoter( hf:name )
) skip.
put unformatted "ORDER " + string(ifield * 10) skip.
if ifield < hb:num-fields then put unformatted skip (1).
end.
put unformatted skip(1).
/*=== INDEX CREATION ===*/
assign vindex = hb:index-information(1).
if lookup("default",vindex) <= 0
then do:
put unformatted(
substitute(
"ADD INDEX &1 ON &2",
quoter(entry(01,vindex,",")),
quoter( hb:name ))) skip.
if entry(02,vindex,",") = "1"
then put unformatted "UNIQUE" skip.
if entry(03,vindex,",") = "1"
then put unformatted "PRIMARY" skip.
do vi = 5 to 20 by 2:
if num-entries(vindex,",") > vi
then put unformatted "INDEX-FIELD " entry(vi,vindex,",") " " (if entry(vi + 1,vindex,",") = "0" then "ASCENDING" else "DESCENDING") skip.
end.
end.
/*=== INDEX CREATION ===*/
put unformatted skip(1).
output close.
To make it work, just call this code on a program.p.
run createDF.p(input table nameOfYourTempTable).
CREATE TABLE new_table
AS (SELECT * FROM tt);
Just replace the new_table with the table name you want to give

Using cascading combo boxes in a datasheet subform

In Access 2010, I have tables Task and Action that have a many-to-many relationship through table ActionTask. In the form for Task, I want to put a subform for all the Actions related to the current task through the ActionTask junction table.
This, in itself, I can do.
The trick is that Action is actually the bottom rank of a four-tier hierarchy of tables.
Each Action belongs to a Goal, each Goal belongs to a Theme, each Theme belongs to a Strategy.
Rather than just have a combo box listing all the available Actions, I'd like to use cascading combo boxes for the Strategy/Theme/Goal/Action.
This I can also do.
The problem is when I use this technique in a datasheet, if I select a given row, the selected row shows the proper Strategy/Theme/Goal/Action, but in all the other rows
the Action is blank
the Strategy/Theme/Goal is set to the current row's values, rather than that row's values
I've tried using the "Continuous Forms" view rather than the "Datasheet" view, but the result is pretty much the same.
I think I know why (the Me.StrategyCombo = ... stuff in my Form_Current callback), but I don't know another way to achieve that.
How can I make the subform display all the rows properly?
Here's the Access file, but all the relevant details should be below.
Tables:
Strategy : (ID, Desc)
Theme : (ID, StrategyID, Desc)
Goal : (ID, ThemeID, Desc)
Action : (ID, GoalID, Desc)
Task : (ID, Desc, ...)
ActionTask : (ActionID, TaskID)
Form Settings:
[Forms]![Task]![ActionTaskSub]:
Link Master Fields: ID
Link Child Fields : TaskID
[Forms]![Task]![ActionTaskSub].[Form]:
On Current:
Private Sub Form_Current()
' when the form loads a record, should reverse propegate
' action > goal > theme > strategy
Dim goalID, themeID, strategyID
' figure out the goal, theme, and strategy that go with this action
If (Me.ActionID) Then
goalID = DLookup("[GoalID]", "Action", "[ID] = " & CStr(Me.ActionID))
themeID = DLookup("[ThemeID]", "Goal", "[ID] = " & CStr(goalID))
strategyID = DLookup("[StrategyID]", "Theme", "[ID] = " & CStr(themeID))
End if
' populate the combo boxes and make the appropriate selections
Me.StrategyCombo = strategyID
Me.ThemeCombo.Requery
Me.ThemeCombo = themeID
Me.GoalCombo.Requery
Me.GoalCombo = goalID
Me.ActionCombo.Requery
Me.ActionCombo = Me.ActionID
End Sub
[Forms]![Task]![ActionTaskSub].[Form]![StrategyCombo]:
Row Source : SELECT [Strategy].[ID], [Strategy].[Desc] FROM [Strategy];
After Update:
Private Sub StrategyCombo_AfterUpdate()
Me.ThemeCombo = Null
Me.ThemeCombo.Requery
Call ThemeCombo_AfterUpdate
End Sub
[Forms]![Task]![ActionTaskSub].[Form]![ThemeCombo]:
Row Source : SELECT [Theme].[ID], [Theme].[Desc] FROM [Theme] WHERE
[Theme].[StrategyID] = [Forms]![Task]![ActionTaskSub].[Form]![StrategyCombo];
After Update:
Private Sub ThemeCombo_AfterUpdate()
Me.GoalCombo = Null
Me.GoalCombo.Requery
Call GoalCombo_AfterUpdate
End Sub
[Forms]![Task]![ActionTaskSub].[Form]![GoalCombo]:
Row Source : SELECT [Goal].[ID], [Goal].[Desc] FROM [Goal] WHERE
[Goal].[ThemeID] = [Forms]![Task]![ActionTaskSub].[Form]![ThemeCombo];
After Update:
Private Sub GoalCombo_AfterUpdate()
Me.ActionCombo = Null
Me.ActionCombo.Requery
End Sub
[Forms]![Task]![ActionTaskSub].[Form]![ActionCombo]:
Row Source : SELECT [Action].[ID], [Action].[Desc] FROM [Action] WHERE
[Action].[GoalID] = [Forms]![Task]![ActionTaskSub].[Form]![GoalCombo];
I solved the issue of dependent (Cascading) comboboxes in a datasheet.
By nature, if combobox 1 is dependent on a value from combobox 2, it will apply the "rowsource" from the current row to all rows in the datatable
Solution:
1 In the load or designer, define the rowsource to include all rows so the datavalue behind it will always match with an option in the rowsource (otherwise you display a blank value)
ie:
Private Sub Form_Load()
Me.cmbProjects.RowSource = "SELECT [Projects].[ProjectPK], [Projects].[ProjectName], [Projects].[PI_ID] FROM [Projects] ORDER BY [ProjectName] "
End Sub
2 In the dependent combobox get focus event, redefine the rowsource to be based on a value from the other combo box that the former depends on. Requery the control
Private Sub cmbProjects_GotFocus()
Me.cmbProjects.RowSource = "SELECT [Projects].[ProjectPK], [Projects].[ProjectName], [Projects].[PI_ID], [Projects].[Status] FROM [Projects] WHERE [PI_ID] = " + CStr(cmbPIs.Value) + " ORDER BY [PI_ID], [ProjectName] "
cmbProjects.Requery
End Sub
3 In the dependent combobox lost focus event, redefine the rowsource to NOT be based on a value from the other combo box that the former depends on.
Private Sub cmbProjects_LostFocus()
Me.cmbProjects.RowSource = "SELECT [Projects].[ProjectPK], [Projects].[ProjectName], [Projects].[PI_ID], [Projects].[Status] FROM [Projects] ORDER BY [PI_ID], [ProjectName] "
cmbProjects.Requery
End Sub
You can't. Any action applies to the subform current record but appears to affect all controls. There are various work-arounds.
As far as I know you can't, but you can work around it by creating a continuous form, putting your fields in tabular layout, then adding a textbox for each combobox (if field is prodID, then I name the textbox txtprodID). Move the textbox right on top of the combobox, but a little narrower, just shy of the combo list arrow, then write code to update the textbox.