Find Value in Last Instance of Multiple Record - ms-access

I'm new to Access and SQL so I'm having a bit of trouble with this undoubtedly simple issue.
I'm using VBA and SQL to update a table based on a few listbox selections.
One of the fields I'm updating is an "instance" field which tracks the instance number of each ID. For example, the first time ID 5 was added to the table its instance number would be 1. The second time it was added to the table its instance would be 2. So it would look like this:
ID | Date | InstanceNUM |
005 | 11/22/2013 | 1 |
005 | 11/23/2013 | 2 |
and so on. It's likely that other records would/will separate the two records in the example above.
After doing some research, it seems like DLast is the best function for this job since the last instance of the particular ID will also contain the instance number in field three. However, when I use this syntax:
Dim i As Long
Dim IDnum As Long
i = DLast("[InstanceNum]", "tbl_Data", [ID] = IDnum)
'where [InstanceNum] is the field name for the value I want to return, _
'"tbl_Data" is the table name where the value I want to return exists, _
'and [ID] is the field name where the ID exists.
I get the Run-time error '94': Invalid use of Null. My understanding of this error is that DLast couldn't find a value based on this criteria (and it can't store that result in a Long data type variable), but I know that the record exists (I've checked the table for the value and I've checked the variable for the value).
EDIT
The [ID] field is a text data type. I set the IDnum variable this way:
Dim IDnum As String
Set ctlList = Me!List7
For i = 0 To ctlList.ListCount - 1
If ctlList.Selected(i) = True Then
IDnum = ctlList.Column(0, i)
Exit For
End If
Next I
The above code loops through a listbox in a form I have and sets the IDnum variable as the ID number in the first column.
Per HansUp's suggestion, I have updated my code to this:
i = DLast("InstanceNum", "tbl_Data", "[ID] =" & IDnum)
But now I get the Run-time error '3464': Data type mismatch in criteria expression.
I'm at a loss for why this is happening. Perhaps there's an easy answer to this, but I haven't been able to find one... And maybe there's an easier way to do this? Does anyone know why this error is happening?

Since [ID] is text type, add quotes around the value of IDnum for DLast.
i = DLast("InstanceNum", "tbl_Data", "[ID] = '" & IDnum & "'")
However if the stored [ID] values include leading zeros, you must Format IDnum to include those.
i = DLast("InstanceNum", "tbl_Data", "[ID] = '" & Format(IDnum, "000") & "'")

Related

Find value located in the last record using VBA

I know this has probably been asked thousands of times before, but I seem to be having trouble finding an existing question that addresses this question in a wy I can understand.
My question in how can I access a value located in the last record of a table? For example, if this is my table:
personID personName personNumber
1 Sally PR32
2 Emily PR33
3 Joseph PR34
I want to access the value "PR34" (and then be able to manipulate it if possible). I know how to find the last record with Dmax, but I'm struggling to figure out how to find a value within it. I'm ultimately trying to take the value, parse the string down to just the numbers, and increment it by one (and then append the letters back on).
You can do this in a query as well:
UPDATE T
SET T.personNumber = "PR" & CLng(Replace([personNumber], "PR", "")) + 1
WHERE (((T.personID)=(SELECT Max(personID) FROM T)));
Where T is the name of your Table.
If you're in the DLookUp/DMax workflow, you can just use the following:
This gives you the ID, you already figured this out:
DMax("PersonID", "MyTable")
Then lookup the associated personNumber with that ID:
DLookUp("personNumber", "MyTable", "personID = " & DMax("PersonID", "MyTable"))
But if you want to manipulate it (or just be more efficient), using a recordset is the way to go:
'Create a recordset
Dim rs As Recordset
'Query the last value, get the personNumber
Set rs = CurrentDb.OpenRecordset("SELECT TOP 1 personNumber FROM MyTable ORDER BY personID DESC")
'Open it up for editing
rs.Edit
'Increment the number by 1
rs.Fields("personNumber").Value = Left(rs.Fields("personNumber").Value, 2) & CInt(Mid(rs.Fields("personNumber").Value, 3, Len(rs.Fields("personNumber").Value) - 2)) + 1
'Update the recordset
rs.Update

Regarding fill some empty fields in access table

i have a table like this and now my task is to fill the empty field with respect to vol and country of the respective column
rule : i have to see the highest volume in the vol field and take the country of that vol and put it in the empty field (ie i have to fill empty cell with country JP because the vol of japan is more )
like this i have to fill .
the remaining fileds like RB , Plant, MCR are changing for remaining empty cells.
i had use Dlookup for this but i coudnt get the solution could any one please help me it is very helpful if you find me the solution.
like this i have to fill all empty line in the table
Ok i am giving more clear view of my table view so that you can understand easily
this is the only table i have to do the task
and now my task is i have to fill the empty cells in the country field but the condition is i should look the RB , Plant ,MCM with respect to vol field (ie: if you see the table the RB , plant ,MCM feilds are same but the country and vol are changing for one group so i have to also consider the fields, that means if the RB, plant, mcm fields are same then i have to take the one of the country in the group with the highest volume) (i just give example in the first thre rows RB , plant , MCM are same so i have to take the highest volume country ie IN so the empty cell should be IN and secodn group empty cells should be TH like that i have to fill.
As I mentioned in my comment to Johnny Bones' answer, your original requirement could have been accomplished with an UPDATE statement that used the DLookup() and DMax() functions like this:
UPDATE MyTable
SET Country=DLookup("Country","MyTable","Vol=" & DMax("Vol","MyTable"))
WHERE Country IS NULL
Even with your revised requirements it is still possible, just more scary-looking:
UPDATE MyTable
SET Country=DLookup("Country","MyTable","RB='" & RB & "' AND Plant='" & Plant & "' AND MCM='" & MCM & "' AND Country IS NOT NULL AND Vol=" & Nz(DMax("Vol","MyTable","RB='" & RB & "' AND Plant='" & Plant & "' AND MCM='" & MCM & "' AND Country IS NOT NULL"),0))
WHERE Country IS NULL
The above query was tested and verified as working in Access 2010.
You can do it in VBA, and have more control over how it reacts. Something like this will do:
Dim db As Database
Dim rec As Recordset
Dim MySQL As String
Set db = CurrentDb
Set rec = db.OpenRecordset("Select Top 1 MyTable.Country FROM MyTable ORDER BY MyTable.Vol DESC")
MyCountry = rec(0)
rec.Close
SetWarnings = False
MySQL = "UPDATE MyTable SET MyTable.Country = '" & MyCountry & "' WHERE IsNull(MyTable.Country) or Len(MyTable.Country) < 2"
DoCmd.RunSQL MySQL
SetWarnings = True
db.Close
I tested the above and it works, although if your table's field's properties are different you may need to tweak some of the code.
Obviously, anywhere you see "MyTable" you should use your own table's name.

Tried to execute a query that does not include the specified expression 'StaffDetails.StaffID' as part of an aggregate function

I have a datagrid called DGHours which I would like to be populated with the SQL below. However I have been given the error message 'Additional information: You tried to execute a query that does not include the specified expression 'StaffDetails.StaffID' as part of an aggregate function.' I don't understand what is wrong with my sql, any help? I want the data grid to display StaffID, FirstName, LastName, and the total number of hours by by the inner joined staffID. I think the problem is to do with SUM(TimeSheet.Hours).
Dim SqlQuery As String = "SELECT [StaffDetails.StaffID], [StaffDetails.FirstName], [StaffDetails.LastName], [TimeSheet.StaffID], SUM(TimeSheet.Hours) FROM [StaffDetails] INNER JOIN [TimeSheet] ON [StaffDetails].StaffID = [TimeSheet].StaffID WHERE [TimeSheet].TimeSheetMonth='" & cbMonth.Text & "'"
Dim da As OleDbDataAdapter = New OleDbDataAdapter(SqlQuery, conn)
Dim ds As DataSet = New DataSet
da.Fill(ds, "Hours")
Dim dt As DataTable = ds.Tables("Hours")
With DGHours
.AutoGenerateColumns = True
.DataSource = ds
.DataMember = "Hours"
End With
If you use square brackets, they should include each single component like table name or column name, not the whole thing: Use e. g. [StaffDetails].[StaffID] instead of StaffDetails.StaffID].
But in your case, you could also just omit all square brackets in the statement. These would only be necessary if you have special characters like spaces, commas, etc. in the table or column names, or if you would use SQL keywords like SELECT or FROM as table names (which is bad practice anyway).
Furthermore, if you use aggregate functions like Sum on some columns, you need to GROUP BY the other columns, i. e. for your query you would have to append
GROUP BY StaffDetails.StaffID,
StaffDetails.FirstName,
StaffDetails.LastName,
TimeSheet.StaffID
to the end of your query.

How to group records in date ranges based on a certain field

I am currently using a continuous form to a show a set of data that results in the following format (each line being one record):
1/04/13 Person1
31/03/13 Person1
30/03/13 Person1
29/03/13 Person2
28/03/13 Person1
There are 100s of these records and I would like to condense it to the following:
30/03/13 - 1/04/13 Person1
29/03/13 Person2
28/03/13 Person1
The data is from a query, so can be manipulated at the SQL level before it even gets to the form stage. I am also comfortable using macros or VBA to manipulate the form. However, I still haven't been able to find a suitable way of doing this.
I realise that this is typical behaviour for a report, but since I eventually want this to be embedded in another form as a subform, I can't use reports (can't create a subreport in a form unless I am missing something).
Any help or advice is much appreciated.
Good description of the problem. Try building your source query like this (assuming the fields are called MyDate and MyPerson and the source table is MyTable):
Select Person, min(MyDate) as StartDate, max(MyDate) as EndDate
from MyTable
group by Person
This gives you 3 fields that you can drop into your form.
Edit
Now that we have a more complete picture, the task just got more complicated, but you still have options assuming the 'events' between people don't overlap (if they do, then I can't think of how to create eventIDs afterward). In the absence of an EventID, you'll have to create one:
First, you need to move your data to a new table so that you can reorder it and add a blank field for the event ID you'll be adding in step 2.
1a. Copy MyTable into a new table. Add an extra field called EventID.
1b. To populate this table, first Delete * from TmpTbl
1c. Then something like:
Insert into TmpTbl.* from MyTable order by MyDate,MyPerson
This code will look through your temp table (TmpTbl as i call it) and add an event id.
Sub AddEventIDs()
Dim rst As Recordset
Set rst = CurrentDb.OpenRecordset("TmpTbl")
Dim LastPerson As String
Dim EventID As Integer
docmd.setwarnings false
While rst.EOF = False
If LastPerson <> rst.Fields("MyPerson") Then EventID = EventID + 1
LastPerson = rst.Fields("MyPerson")
rst.Edit
rst.Fields("EventID") = EventID
rst.Update
rst.MoveNext
Wend
docmd.setwarnings true
End Sub
On the On Load event of your form, add in the 2 queries and the sub routine from #1 and #2.
Your final query will be:
Select MyPerson, EventID, min(MyDate) as StartDate, max(MyDate) as EndDate
from MyTable
group by MyPerson,EventID

sorting a listbox in VBA

I have a listbox with values. Now I need to sort that listbox, BUT NOT BY ALPHABETICALLY.
You see the values in a listbox are from a table.
------------------------
| name | order | size |
========================
value1 4
value2 3
value3 1
value4 2
I hope I made myself clear. So the list box has the items "value1, value2, value3, value4". I want it to sort by the order declared in the table. The list box has nothing to do with the table and is not "data bound" to it. The values come from somewhere else.
Tech used: VBA, Access 2007
It sounds like the Row Source Type for your list box is "Value List". Since the values come from a table, change Row Source Type to "Table/Query", then use a query for Row Source:
SELECT [name], [order]
FROM YourTable
ORDER BY [order];
You don't have to display [order] in the list box ... set its column width to zero.
Your list box control does not have to be bound to a data source field for this approach to work. If the list box is unbound, the user selection will not be stored, but the list box will still allow users to make selections from the list box.
Notice I enclosed [name] and [order] in square brackets because both are reserved words. See Problem names and reserved words in Access
Dim First As Integer
Dim Last As Integer
Dim a As Integer
Dim b As Integer
Dim Temp As String
Dim test() As String
First = LBound(test)
Last = UBound(test)
For a = First To Last - 1
For b = a + 1 To Last
If test(a) > test(b) Then
Temp = test(b)
test(b) = test(a)
test(a) = Temp
End If
Next b
Next a
For a = 1 To UBound(test)
lst_email.AddItem test(a) 'lst_email is a listbox
Next